summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdrian Bunk <adrian.bunk@movial.com>2011-06-03 09:17:04 +0000
committerAdrian Bunk <adrian.bunk@movial.com>2011-06-03 09:17:04 +0000
commit799757ccf1d03c33c75bc597cd5ef77741dcb6a7 (patch)
treea8c3be85c730de28b012586591b76301033d3d21
Imported upstream 4.91upstream-4.91upstreampackaging
-rw-r--r--AUTHORS56
-rw-r--r--COPYING340
-rw-r--r--COPYING.LIB504
-rw-r--r--ChangeLog1614
-rw-r--r--INSTALL236
-rw-r--r--Makefile.am431
-rw-r--r--Makefile.in3477
-rw-r--r--Makefile.tools239
-rw-r--r--NEWS0
-rw-r--r--README38
-rw-r--r--TODO254
-rw-r--r--acinclude.m4405
-rw-r--r--aclocal.m49192
-rw-r--r--attrib/att.c968
-rw-r--r--attrib/att.h306
-rw-r--r--attrib/client.c1116
-rw-r--r--attrib/client.h28
-rw-r--r--attrib/example.c341
-rw-r--r--attrib/example.h26
-rw-r--r--attrib/gatt.c577
-rw-r--r--attrib/gatt.h53
-rw-r--r--attrib/gattrib.c636
-rw-r--r--attrib/gattrib.h80
-rw-r--r--attrib/gatttool.c634
-rw-r--r--attrib/gatttool.h28
-rw-r--r--attrib/interactive.c842
-rw-r--r--attrib/main.c60
-rw-r--r--attrib/manager.c105
-rw-r--r--attrib/manager.h26
-rw-r--r--attrib/utils.c126
-rw-r--r--audio/a2dp-codecs.h116
-rw-r--r--audio/a2dp.c2361
-rw-r--r--audio/a2dp.h164
-rw-r--r--audio/audio.conf45
-rw-r--r--audio/avdtp.c3914
-rw-r--r--audio/avdtp.h316
-rw-r--r--audio/bluetooth.conf36
-rw-r--r--audio/control.c1193
-rw-r--r--audio/control.h50
-rw-r--r--audio/ctl_bluetooth.c384
-rw-r--r--audio/device.c862
-rw-r--r--audio/device.h94
-rw-r--r--audio/gateway.c711
-rw-r--r--audio/gateway.h51
-rw-r--r--audio/gsta2dpsink.c730
-rw-r--r--audio/gsta2dpsink.h85
-rw-r--r--audio/gstavdtpsink.c2029
-rw-r--r--audio/gstavdtpsink.h107
-rw-r--r--audio/gstbluetooth.c109
-rw-r--r--audio/gstpragma.h24
-rw-r--r--audio/gstrtpsbcpay.c352
-rw-r--r--audio/gstrtpsbcpay.h66
-rw-r--r--audio/gstsbcdec.c223
-rw-r--r--audio/gstsbcdec.h66
-rw-r--r--audio/gstsbcenc.c603
-rw-r--r--audio/gstsbcenc.h75
-rw-r--r--audio/gstsbcparse.c220
-rw-r--r--audio/gstsbcparse.h69
-rw-r--r--audio/gstsbcutil.c521
-rw-r--r--audio/gstsbcutil.h75
-rw-r--r--audio/headset.c2951
-rw-r--r--audio/headset.h109
-rw-r--r--audio/ipc.c133
-rw-r--r--audio/ipc.h360
-rw-r--r--audio/main.c194
-rw-r--r--audio/manager.c1415
-rw-r--r--audio/manager.h56
-rw-r--r--audio/media.c714
-rw-r--r--audio/media.h54
-rw-r--r--audio/pcm_bluetooth.c1784
-rw-r--r--audio/rtp.h76
-rw-r--r--audio/sink.c743
-rw-r--r--audio/sink.h49
-rw-r--r--audio/source.c633
-rw-r--r--audio/source.h50
-rw-r--r--audio/telephony-dummy.c433
-rw-r--r--audio/telephony-maemo5.c2104
-rw-r--r--audio/telephony-maemo6.c1993
-rw-r--r--audio/telephony-ofono.c1627
-rw-r--r--audio/telephony.h244
-rw-r--r--audio/transport.c927
-rw-r--r--audio/transport.h38
-rw-r--r--audio/unix.c1917
-rw-r--r--audio/unix.h30
-rw-r--r--bluez.pc.in10
-rw-r--r--btio/btio.c1319
-rw-r--r--btio/btio.h98
-rw-r--r--compat/bnep.c339
-rw-r--r--compat/dun.c334
-rw-r--r--compat/dund.172
-rw-r--r--compat/dund.c645
-rw-r--r--compat/dund.h40
-rw-r--r--compat/fakehid.c669
-rw-r--r--compat/hidd.141
-rw-r--r--compat/hidd.c862
-rw-r--r--compat/hidd.h30
-rw-r--r--compat/lib.h86
-rw-r--r--compat/msdun.c153
-rw-r--r--compat/pand.177
-rw-r--r--compat/pand.c811
-rw-r--r--compat/pand.h36
-rw-r--r--compat/sdp.c720
-rw-r--r--compat/sdp.h39
-rwxr-xr-xcompile143
-rwxr-xr-xconfig.guess1501
-rw-r--r--config.h.in96
-rwxr-xr-xconfig.sub1705
-rwxr-xr-xconfigure16522
-rw-r--r--configure.ac60
-rw-r--r--cups/cups.h38
-rw-r--r--cups/hcrp.c353
-rw-r--r--cups/main.c876
-rw-r--r--cups/sdp.c119
-rw-r--r--cups/spp.c118
-rwxr-xr-xdepcomp630
-rw-r--r--doc/adapter-api.txt286
-rw-r--r--doc/agent-api.txt91
-rw-r--r--doc/assigned-numbers.txt23
-rw-r--r--doc/attribute-api.txt164
-rw-r--r--doc/audio-api.txt458
-rw-r--r--doc/control-api.txt142
-rw-r--r--doc/device-api.txt199
-rw-r--r--doc/health-api.txt166
-rw-r--r--doc/hfp-api.txt86
-rw-r--r--doc/input-api.txt44
-rw-r--r--doc/manager-api.txt74
-rw-r--r--doc/media-api.txt165
-rw-r--r--doc/network-api.txt88
-rw-r--r--doc/sap-api.txt34
-rw-r--r--doc/serial-api.txt41
-rw-r--r--doc/service-api.txt62
-rw-r--r--doc/version.xml.in1
-rw-r--r--gdbus/gdbus.h177
-rw-r--r--gdbus/mainloop.c383
-rw-r--r--gdbus/object.c869
-rw-r--r--gdbus/polkit.c202
-rw-r--r--gdbus/watch.c748
-rw-r--r--health/hdp.c2223
-rw-r--r--health/hdp.h35
-rw-r--r--health/hdp_main.c62
-rw-r--r--health/hdp_manager.c100
-rw-r--r--health/hdp_manager.h27
-rw-r--r--health/hdp_types.h129
-rw-r--r--health/hdp_util.c1211
-rw-r--r--health/hdp_util.h61
-rw-r--r--health/mcap.c2195
-rw-r--r--health/mcap.h169
-rw-r--r--health/mcap_internal.h141
-rw-r--r--health/mcap_lib.h228
-rw-r--r--health/mcap_sync.c1015
-rw-r--r--input/device.c1265
-rw-r--r--input/device.h56
-rw-r--r--input/fakehid.c411
-rw-r--r--input/fakehid.h40
-rw-r--r--input/input.conf9
-rw-r--r--input/main.c86
-rw-r--r--input/manager.c207
-rw-r--r--input/manager.h25
-rw-r--r--input/server.c237
-rw-r--r--input/server.h25
-rwxr-xr-xinstall-sh520
-rw-r--r--lib/bluetooth.c470
-rw-r--r--lib/bluetooth.h219
-rw-r--r--lib/bnep.h153
-rw-r--r--lib/cmtp.h69
-rw-r--r--lib/hci.c2913
-rw-r--r--lib/hci.h2367
-rw-r--r--lib/hci_lib.h233
-rw-r--r--lib/hidp.h85
-rw-r--r--lib/l2cap.h208
-rw-r--r--lib/mgmt.h271
-rw-r--r--lib/rfcomm.h99
-rw-r--r--lib/sco.h62
-rw-r--r--lib/sdp.c4791
-rw-r--r--lib/sdp.h518
-rw-r--r--lib/sdp_lib.h631
-rw-r--r--lib/uuid.c273
-rw-r--r--lib/uuid.h65
-rwxr-xr-xltmain.sh8406
-rwxr-xr-xmissing376
-rw-r--r--network/common.c262
-rw-r--r--network/common.h42
-rw-r--r--network/connection.c622
-rw-r--r--network/connection.h28
-rw-r--r--network/main.c59
-rw-r--r--network/manager.c222
-rw-r--r--network/manager.h25
-rw-r--r--network/network.conf6
-rw-r--r--network/server.c809
-rw-r--r--network/server.h29
-rw-r--r--plugins/dbusoob.c234
-rw-r--r--plugins/echo.c167
-rw-r--r--plugins/formfactor.c148
-rw-r--r--plugins/hal.c144
-rw-r--r--plugins/hciops.c3744
-rw-r--r--plugins/maemo6.c252
-rw-r--r--plugins/mgmtops.c1937
-rw-r--r--plugins/pnat.c526
-rw-r--r--plugins/service.c824
-rw-r--r--plugins/storage.c43
-rw-r--r--sap/main.c55
-rw-r--r--sap/manager.c93
-rw-r--r--sap/manager.h22
-rw-r--r--sap/sap-dummy.c330
-rw-r--r--sap/sap.h186
-rw-r--r--sap/server.c1438
-rw-r--r--sap/server.h26
-rw-r--r--sbc/formats.h55
-rw-r--r--sbc/sbc.c1234
-rw-r--r--sbc/sbc.h113
-rw-r--r--sbc/sbc_math.h61
-rw-r--r--sbc/sbc_primitives.c554
-rw-r--r--sbc/sbc_primitives.h80
-rw-r--r--sbc/sbc_primitives_armv6.c299
-rw-r--r--sbc/sbc_primitives_armv6.h52
-rw-r--r--sbc/sbc_primitives_iwmmxt.c304
-rw-r--r--sbc/sbc_primitives_iwmmxt.h42
-rw-r--r--sbc/sbc_primitives_mmx.c375
-rw-r--r--sbc/sbc_primitives_mmx.h41
-rw-r--r--sbc/sbc_primitives_neon.c893
-rw-r--r--sbc/sbc_primitives_neon.h41
-rw-r--r--sbc/sbc_tables.h660
-rw-r--r--sbc/sbcdec.c293
-rw-r--r--sbc/sbcenc.c308
-rw-r--r--sbc/sbcinfo.c322
-rw-r--r--sbc/sbctester.c358
-rw-r--r--scripts/bluetooth-hid2hci.rules36
-rw-r--r--scripts/bluetooth-serial.rules35
-rw-r--r--scripts/bluetooth.rules4
-rw-r--r--scripts/bluetooth.rules.in4
-rw-r--r--scripts/bluetooth_serial39
-rw-r--r--serial/main.c59
-rw-r--r--serial/manager.c176
-rw-r--r--serial/manager.h25
-rw-r--r--serial/port.c622
-rw-r--r--serial/port.h29
-rw-r--r--serial/proxy.c1278
-rw-r--r--serial/proxy.h25
-rw-r--r--serial/serial.conf10
-rw-r--r--src/adapter.c3752
-rw-r--r--src/adapter.h302
-rw-r--r--src/agent.c794
-rw-r--r--src/agent.h77
-rw-r--r--src/attrib-server.c1310
-rw-r--r--src/attrib-server.h35
-rw-r--r--src/bluetooth.conf33
-rw-r--r--src/bluetooth.ver10
-rw-r--r--src/bluetoothd.8.in91
-rw-r--r--src/dbus-common.c254
-rw-r--r--src/dbus-common.h47
-rw-r--r--src/device.c2388
-rw-r--r--src/device.h119
-rw-r--r--src/error.c116
-rw-r--r--src/error.h43
-rw-r--r--src/event.c731
-rw-r--r--src/event.h44
-rwxr-xr-xsrc/genbuiltin17
-rw-r--r--src/glib-helper.c587
-rw-r--r--src/glib-helper.h36
-rw-r--r--src/hcid.h66
-rw-r--r--src/log.c136
-rw-r--r--src/log.h57
-rw-r--r--src/main.c509
-rw-r--r--src/main.conf66
-rw-r--r--src/manager.c431
-rw-r--r--src/manager.h43
-rw-r--r--src/oob.c41
-rw-r--r--src/oob.h32
-rw-r--r--src/oui.c101
-rw-r--r--src/oui.h25
-rw-r--r--src/plugin.c249
-rw-r--r--src/plugin.h47
-rw-r--r--src/ppoll.h16
-rw-r--r--src/rfkill.c173
-rw-r--r--src/sdp-xml.c789
-rw-r--r--src/sdp-xml.h59
-rw-r--r--src/sdpd-database.c346
-rw-r--r--src/sdpd-request.c1094
-rw-r--r--src/sdpd-server.c292
-rw-r--r--src/sdpd-service.c537
-rw-r--r--src/sdpd.h97
-rw-r--r--src/storage.c1345
-rw-r--r--src/storage.h92
-rw-r--r--src/textfile.c492
-rw-r--r--src/textfile.h43
-rw-r--r--src/uinput.h724
-rw-r--r--test/agent.c700
-rwxr-xr-xtest/apitest448
-rw-r--r--test/attest.c183
-rw-r--r--test/avtest.c869
-rw-r--r--test/bdaddr.868
-rw-r--r--test/bdaddr.c460
-rw-r--r--test/btiotest.c555
-rw-r--r--test/dbusdef.py16
-rw-r--r--test/gaptest.c335
-rw-r--r--test/hciemu.131
-rw-r--r--test/hciemu.c1343
-rwxr-xr-xtest/hsmicro20
-rwxr-xr-xtest/hsplay22
-rw-r--r--test/hstest.c308
-rw-r--r--test/ipctest.c1129
-rw-r--r--test/l2test.c1379
-rwxr-xr-xtest/list-devices87
-rw-r--r--test/lmptest.c175
-rwxr-xr-xtest/monitor-bluetooth56
-rw-r--r--test/rctest.190
-rw-r--r--test/rctest.c781
-rw-r--r--test/sap-client943
-rw-r--r--test/scotest.c434
-rw-r--r--test/sdptest.c146
-rw-r--r--test/service-did.xml33
-rw-r--r--test/service-ftp.xml37
-rw-r--r--test/service-opp.xml50
-rw-r--r--test/service-record.dtd66
-rw-r--r--test/service-spp.xml25
-rwxr-xr-xtest/simple-agent120
-rwxr-xr-xtest/simple-endpoint126
-rwxr-xr-xtest/simple-service127
-rwxr-xr-xtest/test-adapter120
-rwxr-xr-xtest/test-attrib108
-rwxr-xr-xtest/test-audio45
-rwxr-xr-xtest/test-device207
-rwxr-xr-xtest/test-discovery57
-rwxr-xr-xtest/test-input45
-rwxr-xr-xtest/test-manager38
-rwxr-xr-xtest/test-network57
-rwxr-xr-xtest/test-sap-server138
-rwxr-xr-xtest/test-serial56
-rwxr-xr-xtest/test-service47
-rwxr-xr-xtest/test-telephony176
-rw-r--r--test/test-textfile.c188
-rw-r--r--test/uuidtest.c319
-rw-r--r--tools/avctrl.839
-rw-r--r--tools/avctrl.c248
-rw-r--r--tools/avinfo.c672
-rw-r--r--tools/bccmd.8130
-rw-r--r--tools/bccmd.c1254
-rw-r--r--tools/ciptool.168
-rw-r--r--tools/ciptool.c498
-rw-r--r--tools/csr.c2853
-rw-r--r--tools/csr.h553
-rw-r--r--tools/csr_3wire.c62
-rw-r--r--tools/csr_bcsp.c255
-rw-r--r--tools/csr_h4.c165
-rw-r--r--tools/csr_hci.c160
-rw-r--r--tools/csr_usb.c180
-rw-r--r--tools/dfu.c168
-rw-r--r--tools/dfu.h107
-rw-r--r--tools/dfubabel.138
-rw-r--r--tools/dfubabel.c211
-rw-r--r--tools/dfutool.153
-rw-r--r--tools/dfutool.c791
-rw-r--r--tools/hciattach.8155
-rw-r--r--tools/hciattach.c1446
-rw-r--r--tools/hciattach.h56
-rw-r--r--tools/hciattach_ath3k.c1049
-rw-r--r--tools/hciattach_qualcomm.c275
-rw-r--r--tools/hciattach_st.c278
-rw-r--r--tools/hciattach_ti.c529
-rw-r--r--tools/hciattach_tialt.c244
-rw-r--r--tools/hciconfig.8277
-rw-r--r--tools/hciconfig.c2036
-rw-r--r--tools/hcieventmask.c130
-rw-r--r--tools/hcisecfilter.c155
-rw-r--r--tools/hcitool.1209
-rw-r--r--tools/hcitool.c3007
-rw-r--r--tools/hid2hci.851
-rw-r--r--tools/hid2hci.c375
-rw-r--r--tools/kword.c65
-rw-r--r--tools/kword.h46
-rw-r--r--tools/l2ping.876
-rw-r--r--tools/l2ping.c324
-rw-r--r--tools/lexer.c1834
-rw-r--r--tools/lexer.l120
-rw-r--r--tools/parser.c1768
-rw-r--r--tools/parser.h94
-rw-r--r--tools/parser.y171
-rw-r--r--tools/ppporc.c271
-rw-r--r--tools/rfcomm.1137
-rw-r--r--tools/rfcomm.c849
-rw-r--r--tools/rfcomm.conf17
-rw-r--r--tools/sdptool.1130
-rw-r--r--tools/sdptool.c4274
-rw-r--r--tools/ubcsp.c1180
-rw-r--r--tools/ubcsp.h208
-rw-r--r--tracer/main.c152
-rwxr-xr-xylwrap222
387 files changed, 205620 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..939a0a8
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,56 @@
+Maxim Krasnyansky <maxk@qualcomm.com>
+Marcel Holtmann <marcel@holtmann.org>
+Stephen Crane <steve.crane@rococosoft.com>
+Jean Tourrilhes <jt@hpl.hp.com>
+Jan Beutel <j.beutel@ieee.org>
+Ilguiz Latypov <ilatypov@superbt.com>
+Thomas Moser <thomas.moser@tmoser.ch>
+Nils Faerber <nils@kernelconcepts.de>
+Martin Leopold <martin@leopold.dk>
+Wolfgang Heidrich <wolfgang.heidrich@esk.fhg.de>
+Fabrizio Gennari <fabrizio.gennari@philips.com>
+Brad Midgley <bmidgley@xmission.com>
+Henryk Ploetz <henryk@ploetzli.ch>
+Philip Blundell <pb@nexus.co.uk>
+Johan Hedberg <johan.hedberg@nokia.com>
+Claudio Takahasi <claudio.takahasi@indt.org.br>
+Eduardo Rocha <eduardo.rocha@indt.org.br>
+Denis Kenzior <denis.kenzior@trolltech.com>
+Frederic Dalleau <frederic.dalleau@access-company.com>
+Frederic Danis <frederic.danis@access-company.com>
+Luiz Augusto von Dentz <luiz.dentz@gmail.com>
+Fabien Chevalier <fabchevalier@free.fr>
+Ohad Ben-Cohen <ohad@bencohen.org>
+Daniel Gollub <dgollub@suse.de>
+Tom Patzig <tpatzig@suse.de>
+Kai Vehmanen <kai.vehmanen@nokia.com>
+Vinicius Gomes <vinicius.gomes@openbossa.org>
+Alok Barsode <alok.barsode@azingo.com>
+Bastien Nocera <hadess@hadess.net>
+Albert Huang <albert@csail.mit.edu>
+Glenn Durfee <gdurfee@google.com>
+David Woodhouse <david.woodhouse@intel.com>
+Christian Hoene <hoene@uni-tuebingen.de>
+Pekka Pessi <pekka.pessi@nokia.com>
+Siarhei Siamashka <siarhei.siamashka@nokia.com>
+Nick Pelly <npelly@google.com>
+Lennart Poettering <lennart@poettering.net>
+Gustavo F. Padovan <padovan@profusion.mobi>
+Marc-Andre Lureau <marc-andre.lureau@nokia.com>
+Bea Lam <bea.lam@nokia.com>
+Zygo Blaxell <zygo.blaxell@xandros.com>
+Forrest Zhao <forrest.zhao@intel.com>
+Scott Talbot <psyc@stalbot.com>
+Ilya Rubtsov <lusyaru@gmail.com>
+Mario Limonciello <mario_limonciello@dell.com>
+Filippo Giunchedi <filippo@esaurito.net>
+Jaikumar Ganesh <jaikumar@google.com>
+Elvis Pfutzenreuter <epx@signove.com>
+Santiago Carot-Nemesio <scarot@libresoft.es>
+José Antonio Santos Cadenas <jcaden@libresoft.es>
+Francisco Alecrim <francisco.alecrim@openbossa.org>
+Daniel Orstadius <daniel.orstadius@gmail.com>
+Anderson Briglia <anderson.briglia@openbossa.org>
+Anderson Lizardo <anderson.lizardo@openbossa.org>
+Bruna Moreira <bruna.moreira@openbossa.org>
+Brian Gix <bgix@codeaurora.org>
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..6d45519
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/COPYING.LIB b/COPYING.LIB
new file mode 100644
index 0000000..1f7c8cc
--- /dev/null
+++ b/COPYING.LIB
@@ -0,0 +1,504 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..5796bf3
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,1614 @@
+ver 4.91:
+ Fix issue with LMP version string and hciconfig.
+ Fix issue with missing discovery signal when scanning.
+ Fix issue with wrong state and canceling name resolving.
+ Fix issue with missing check during adapter initialization.
+ Fix issue with missing protocol not supported error and A2DP.
+ Fix issue with crash during driver unregistering and A2DP.
+ Fix issue with crash when receiving AVDTP close command.
+ Fix issue with remote SEP handling when A2DP codec changes.
+ Fix issue with SCO hangup handling and state changes.
+ Fix issue with security level and MCAP instances.
+ Fix issue with memory leak and HDP data channels.
+ Add support for discover characteristics by UUID to gatttool.
+ Add initial support for Out-of-Band association model.
+ Add initial support for SIM Access Profile.
+
+ver 4.90:
+ Fix issue with setting of global mode property.
+ Fix issue with handling of RequestSession responses.
+ Fix issue with TP_BNEP_CTRL_BV_01_C qualification test.
+ Fix issue with too short AVDTP request timeout.
+ Add support for SIM Access Profile manager.
+ Add support for new UUID utility functions.
+ Add support for attribute server notifications.
+ Add support for client characteristic configuration.
+ Update support for interactive GATT utility.
+
+ver 4.89:
+ Fix issue with name resolving when discovery is suspended.
+ Fix issue with parsing flags of advertising report.
+ Fix issue with SEP handling if interface is disabled.
+ Fix issue with device object creation on disconnect event.
+ Fix issue with indicators whenever the driver is initialized.
+ Fix issue with call indicator when parsing call info reply.
+ Fix issue with crash and allowed GATT MTU was too large.
+ Add support for SDP record of Primary GATT services.
+ Add support for interactive mode for GATT utility.
+
+ver 4.88:
+ Fix issue with HID channel reference count handling.
+ Fix issue with daemon exit on badly formatted AT+VTS.
+ Fix issue with crash while parsing of endpoint properties.
+ Fix issue with possible crash on AVDTP Suspend request timeout.
+ Fix issue with stopping inquiry before adapter is initialized.
+ Fix issue with creating device object when connection fails.
+ Fix issue with sending HCIDEVUP when adapter is already up.
+ Fix issue with handling bonding IO channel closing.
+ Fix agent cancellation in security mode 3 situations.
+ Update pairing code to support management interface.
+
+ver 4.87:
+ Fix issue with initialization when adapter is already up.
+ Fix issue with attribute server MTU and incoming connections.
+ Fix issue with duplicate characteristics after discovery.
+
+ver 4.86:
+ Revert wrong fix for SDP PDU size error response.
+ Fix various memory leaks in A2DP and AVDTP support.
+ Add Routing property to MediaTransport interface
+ Add proper tracking mechanism to NREC status.
+ Add READ_BLOB_REQUEST support to attribute server.
+
+ver 4.85:
+ Fix issue with event mask setting for older adapters.
+ Fix issue with device creation and pairing failures.
+ Add support for telephony support via oFono.
+ Add support for characteristic security level.
+ Update support for service registration.
+
+ver 4.84:
+ Fix issue with wrong parameters and device found signals.
+ Fix issue with leaking EIR data if RSSI does not change.
+ Fix issue with adapter initialization state.
+ Fix issue with closing of SDP server sockets.
+
+ver 4.83:
+ Fix issue with already connected HFP/HSP endpoints.
+ Fix missing reply when create device is canceled.
+ Fix memory leak within the attribute server.
+ Fix memory leak with unused extended inquiry name.
+ Fix setting paired state when device->authr is false.
+ Fix clearing authentication request for renewed keys.
+ Add support for storing link keys in runtime memory.
+ Update support for primary service discovery.
+
+ver 4.82:
+ Fix crash with mmap of files with multiples of page size.
+ Fix HFP response and hold (AT+BTRH) command response.
+ Fix device creation error response when powered off.
+ Fix device removal when connecting/browsing fails.
+ Add initial attribute permission implementation.
+ Add AVDTP SRC stream send buffer size verification.
+ Add support for setting link policy based on features.
+
+ver 4.81:
+ Fix issue with telephony driver initialization.
+ Fix issue with adapter services list initialization.
+ Fix crash after simultaneous authentication requests.
+ Add support for primary service search on device creation.
+
+ver 4.80:
+ Fix legacy link key storing for some buggy adapters.
+ Fix invalid memory access when EIR field length is zero.
+ Fix adapter initialization to wait for kernel HCI commands.
+ Fix initialization of adapters which are already up.
+ Fix possible race condition when initializing adapters.
+ Fix possible crashes when attempting to connect AVDTP.
+ Fix not aborting sink stream configuration on disconnect.
+ Fix not indicating disconnected state when connecting to AVDTP.
+ Fix not dropping AVDTP session when canceling stream setup.
+ Fix AVDTP abort not being send when the state is idle.
+ Fix regression with Low Energy and interleave discovery.
+ Add a new configuration option to disable Low Energy support.
+ Add iwmmxt optimization for SBC for ARM PXA series CPUs.
+ Update support for GATT Primary Service Discovery.
+ Update MCAP and HDP support.
+
+ver 4.79:
+ Fix issue with adapter initialization race condition.
+ Update new Bluetooth Management interface support.
+
+ver 4.78:
+ Fix various issues with AVDTP timer handling.
+ Fix various issues with handling of mode changes.
+ Fix issue with audio disconnect watch in connecting state.
+ Fix issue with handling call waiting indicators in telephony.
+ Fix issue with handling UUID parameter and RegisterEndpoint.
+ Add initial support for Bluetooth Management interface.
+ Add support for Application property to HealthChannel.
+
+ver 4.77:
+ Fix issue with device name and accessing already freed memory.
+ Fix issue with handling CHLD=0 command for handsfree.
+ Fix issue with manager properties and no adapters.
+ Fix issue with properties and broken service records.
+ Fix issue with A2DP playback and sample rate changes.
+ Update MCAP and HDP support.
+
+ver 4.76:
+ Fix issue in telephony driver with hanging up held call.
+ Fix issue in telephony driver with notifications when on hold.
+ Fix issue with blocking on setconf confirmation callback.
+ Fix issue with not always signaling new streams as sinks.
+ Fix issue with errors in case of endpoint request timeout.
+ Fix issue with HFP/HSP microphone and speaker gain values.
+ Add source if the device attempt to configure local sink stream.
+ Add PSM option for GATT/ATT over BR/EDR on gatttool.
+ Add support for GATT/ATT Attribute Write Request.
+ Update MCAP and HDP support.
+
+ver 4.75:
+ Fix use of uninitialized variable on legacy pairing.
+ Fix mismatch of attribute protocol opcode.
+
+ver 4.74:
+ Fix regression for Legacy Pairing.
+ Fix wrong PSM value for attribute protocol.
+ Fix issue with RSSI field in advertising reports.
+ Add support for Add BR/EDR and LE interleaved discovery.
+ Add support for GATT write characteristic value option.
+ Add support for specifying download address for AR300x.
+
+ver 4.73:
+ Fix problem with EIR data when setting the name.
+ Fix reading local name from command complete event.
+ Fix registering local endpoints with disabled socket interface.
+ Add support for more HCI operations using ops infrastructure.
+ Add support for GATT characteristic hierarchy.
+ Add support for GATT indications.
+
+ver 4.72:
+ Fix memory leak while connecting BTIO channels.
+ Fix crash with GStreamer plugin if SBC is not supported.
+ Fix issue with GATT server stop sending notifications.
+ Fix issue with GATT and dealing with the minimum MTU size.
+ Fix issue with file descriptor leak in GATT client.
+ Add support for UUID 128-bit handling in attribute client.
+ Add support for encoders/decoders for MTU Exchange.
+ Add support for the MTU Exchange procedure to the server.
+ Add support for a per channel MTU to the ATT server.
+ Add support for Characteristic interface.
+ Add support for new Media API and framework.
+ Add initial support for HDP plugin.
+
+ver 4.71:
+ Fix compilation when SBC support in not enabled.
+ Fix crash with RequestSession and application disconnects.
+ Fix memory leak and possible crash when removing audio device.
+ Fix issue with closing stream of locked sep when reconfiguring.
+ Fix issue where discovery could interfere with bonding.
+ Fix issue with Connected status when PS3 BD remote connects.
+ Fix issue with lifetime of fake input devices.
+ Add support for compile time option of oui.txt path.
+ Add support for printing IEEE1284 device ID for CUPS.
+ Add plugin for setting adapter class via DMI.
+ Add more features for attribute protocol and profile.
+ Add initial support for MCAP.
+
+ver 4.70:
+ Fix incoming call indication handling when in WAITING state.
+ Fix various SDP related qualification test case issues.
+ Fix logic to write EIR when SDP records are changed.
+ Fix UTF-8 validity check for remote names in EIR.
+ Add support for UUID-128 extended inquiry response.
+ Add service UUIDs from EIR to the DeviceFound signal.
+ Add fast connectable feature for Handsfree profile.
+ Add HCI command and event definitions for AMP support.
+ Add firmware download support for Qualcommh devices.
+ Add host level support for Atheros AR300x device.
+ Add initial support of ATT and GATT for basic rate.
+
+ver 4.69:
+ Fix issue with calling g_option_context_free() twice.
+ Fix inconsistencies with initial LE commands and events.
+ Add support for telephony ClearLastNumber method.
+ Add support for network server interface.
+
+ver 4.68:
+ Fix initialization of adapters in RAW mode.
+ Fix signal strength for HFP in Maemo's telephony support.
+ Add support for following the radio state via Maemo's MCE.
+ Add initial set of LE commands and events definitions.
+ Add mode option for L2CAP sockets to the BtIO API.
+
+ver 4.67:
+ Fix issue with authentication reply when bonding already completed.
+ Fix issue with not canceling authentication when bonding fails.
+ Fix issue with changed combination keys and temporary storage.
+ Fix issue with sdp_get_supp_feat library function.
+ Fix issue with missing unblock on device removal.
+ Fix issue with not waiting for mode change completion.
+ Add ARMv6 optimized version of analysis filter for SBC encoder.
+
+ver 4.66:
+ Fix regression with full debug enabling via SIGUSR2.
+ Fix redundant speaker/microphone gains being sent.
+ Fix not emitting PropertyChanged for SpeakerGain/MicrophoneGain.
+ Fix issue with storage usage when a record is not found in memory.
+ Fix issue with DiscoverServices not retrieving any records.
+ Fix audio profile disconnection order to match whitepaper.
+ Fix auto-accept confirmation when local agent has NoInputNoOutput.
+ Fix remote just-works SSP when MITM protection is required.
+ Fix performing dedicated bonding without MITM requirement.
+ Add support for storing debug link keys in runtime memory.
+
+ver 4.65:
+ Fix issues with general bonding being default setting now.
+ Fix driver removal upon device removal.
+ Add new "Blocked" property to device objects.
+ Add hciconfig support for blacklisting.
+ Add support for dynamic debug feature.
+
+ver 4.64:
+ Fix invalid memory access in headset_get_nrec function.
+ Fix issue with disconnect event on higher protocol layers.
+ Fix issue with list parsing in sdp_set_supp_features function.
+ Fix device object reference counting for SDP browse requests.
+ Add missing memory checks whenever memory is allocated for SDP.
+ Add support for exporting local services via D-Bus.
+ Add more L2CAP Enhanced Retransmission test options.
+
+ver 4.63:
+ Fix avdtp_abort not canceling pending requests.
+ Fix stale connection when abort gets rejected.
+
+ver 4.62:
+ Fix accidental symbol breakage with inquiry transmit power.
+ Fix using invalid data from previous headset connection.
+ Fix double free on AVDTP Abort response.
+ Fix possible crash while verifying AVDTP version.
+ Fix missing inuse flag when AVDTP stream is configured.
+ Add support for Bluetooth controller types.
+
+ver 4.61:
+ Fix issues with Read Inquiry Response Transmit Power Level.
+ Fix possible invalid read when removing a temporary device.
+ Fix mode restoration when remember_powered is false.
+ Fix conference call releasing in telephony-maemo.
+ Fix segmentation fault with authorization during headset disconnects.
+ Add support for handling unanswered AVDTP request on disconnect.
+ Add support for handling Inquiry Response Transmit Power Level.
+ Add support for caching of remote host features.
+ Add preliminary voice dialing support for HSP.
+
+ver 4.60:
+ Fix voice mailbox number reading from SIM.
+ Fix some races with D-Bus mainloop integration.
+ Add helpers for D-Bus signal watches.
+
+ver 4.59:
+ Add values for Bluetooth 4.0 specification.
+ Add SDP functions for HDP support.
+ Add test scripts for input and audio.
+ Fix missing close on BtIO create_io function.
+ Fix sending incorrect AVDTP commands after timeout occurs.
+ Fix timer removal when device disconnects unexpectedly.
+ Fix Extended Inquiry Response record for Device ID.
+
+ver 4.58:
+ Fix crash when adapter agent exists during authentication.
+ Fix CK-20W quirks for play and pause events.
+
+ver 4.57:
+ Fix unloading of drivers for uninitialized adapters.
+ Fix debug message to use requested and not opened SEID.
+ Fix codec selection for GStreamer plugin.
+ Fix deleting of SDP records during service updates.
+ Fix deleting of SDP records when a device is removed.
+ Fix handling when the SDP record is modified on remote device.
+ Fix potential buffer overflow by using snprintf instead of sprintf.
+ Fix const declarations for some storage function parameters.
+
+ver 4.56:
+ Add missing values from Bluetooth 3.0 specification.
+ Add proper tracking of device paired status.
+ Fix tracking of devices without permanently stored link key.
+ Fix issue with link key removal after connection failures.
+ Fix legacy pairing information based on remote host features.
+ Fix off-by-one issue with AVDTP capability parsing.
+ Fix AVRCP, AVCTP, AVDTP, A2DP and HFP version numbers.
+ Fix agent canceling before calling agent_destroy.
+ Fix service record parsing with an empty UUID list.
+ Fix various SDP related memory leaks.
+
+ver 4.55:
+ Add support for POSIX capabilities dropping.
+ Add special quirk for the Nokia CK-20W car kit.
+ Fix error code handling for AVDTP SetConfiguration response.
+ Fix updating out of range list when RSSI hasn't changed.
+ Fix various memory leaks and unnecessary error checks.
+
+ver 4.54:
+ Add introspection interface to output of introspection calls.
+ Fix stream handling when media transport disconnects prematurely.
+ Fix command timeout handling when there's no stream.
+ Fix headset_suspend_stream behavior for invalid states
+ Fix issue with AVDTP ABORTING state transition.
+ Fix issue with AVDTP suspend while closing.
+
+ver 4.53:
+ Fix issue with telephony connection state notifications.
+ Fix AVDTP stream leak for invalid media transport config.
+ Fix audio connection authorization handling with timeouts.
+ Fix race condition in authorizing audio connections.
+ Fix device authorized setting for AVRCP-only connections.
+ Fix duplicate attempts from device to connect signal channel.
+
+ver 4.52:
+ Add AVCTP support to test utility.
+ Fix AVDTP Abort when transport closes before response.
+ Fix authorization when the audio profiles are slow to connect.
+ Fix potential AVDTP reference leaks.
+
+ver 4.51:
+ Add utility for basic AVDTP testing.
+ Add support for configuring L2CAP FCS option.
+ Fix discovery mode for CUPS 1.4.x and later.
+ Fix global state tracking of audio service.
+ Fix last issues with the new build system.
+
+ver 4.50:
+ Fix issue with missing manual pages in distribution.
+ Fix issue with the configuration and state directories.
+ Fix issue with creating include directory.
+ Fix dependencies of include file generation.
+
+ver 4.49:
+ Add simple test program for basic GAP testing.
+ Add support for confirmation requests to agent example.
+ Add support for full non-recursive build.
+ Add five millisecond delay for Simple Pairing auto-accept.
+ Fix Class of Device setting when InitiallyPowered=false.
+
+ver 4.48:
+ Add library function for comparing UUID values.
+ Add support for creating all plugins as builtins.
+ Add support for async handling of service class changes.
+ Add support for source interface to audio IPC.
+ Fix device name settings when device is off or down.
+ Fix issue with enabled SCO server when not necessary.
+ Fix missing D-Bus access policy for CUPS backend.
+ Fix discovery results of CUPS backend.
+ Fix initialization handling of Maemo telephony.
+
+ver 4.47:
+ Add support for RFKILL unblock handling.
+ Add support for serial proxy configurations.
+ Add support for caching service class updates.
+ Fix issues with updating SDP service records.
+ Fix usage of limited discoverable mode.
+ Remove deprecated methods and signals for AudioSource.
+
+ver 4.46:
+ Add support for A2DP sink role.
+ Fix clearing svc_cache before the adapter is up.
+ Fix various pointer after free usages.
+ Fix various memory leaks.
+
+ver 4.45:
+ Fix UDEV_DATADIR fallback if pkg-config fails.
+ Fix adapter cleanup and setup prototypes.
+ Fix double-free with out-of-range devices.
+ Fix inband ring setting to be per-headset.
+ Fix handling of Maemo CSD startup.
+
+ver 4.44:
+ Add some missing manual pages.
+ Fix missing number prefix when installing udev rules.
+ Fix program prefix used in Bluetooth udev rules.
+ Fix three-way calling indicator order.
+ Fix downgrade/upgrade of callheld indicator.
+ Fix +CIEV sending when indicator value changes.
+ Fix signal handling for Maemo telephony driver.
+ Fix parsing issues with messages from Maemo CSD.
+ Fix issue with duplicate active calls.
+
+ver 4.43:
+ Add support for udev based on-demand startup.
+ Fix verbose error reporting of CUPS backend.
+ Fix various string length issues.
+ Fix issues with Maemo telephony driver.
+ Fix another device setup and temporary flag issue.
+ Fix and update example agent implementation.
+
+ver 4.42:
+ Add TI WL1271 to Texas Instruments chip list.
+ Add special udev mode to bluetoothd.
+ Fix regression when there is no agent registered.
+ Fix error return when bonding socket hang up.
+ Fix SCO server socket for HFP handsfree role.
+ Fix shutdown on SCO socket before closing.
+ Fix shutdown on A2DP audio stream channel before closing.
+ Fix issue with asserting on AVDTP reference count bugs.
+ Fix authorization denied issue with certain headsets.
+ Fix AVRCP UNITINFO and SUBUNIT INFO responses.
+ Fix discovery cancel issues in case SDP discovery fails.
+
+ver 4.41:
+ Fix pairing even if the ACL gets dropped before successful SDP.
+ Fix regression which caused device to be removed after pairing.
+ Fix HSP record fetching when remote device doesn't support it.
+ Fix SDP discovery canceling when clearing hs->pending.
+ Fix headset never connecting on the first attempt.
+ Fix headset state tracking if bt_search_service() fails.
+ Fix maximum headset connection count check.
+ Fix AVDTP Discover timeout handling.
+ Fix also UI_SET_KEYBIT for the new pause and play key codes.
+
+ver 4.40:
+ Add telephony driver for oFono telephony stack.
+ Add support for Dell specific HID proxy switching.
+ Add support for running hid2hci from udev.
+ Add mapping for AVRCP Play and Pause to dedicated key codes.
+ Fix AVRCP keycodes to better match existing X keymap support.
+ Fix various quoting issues within telephony support.
+ Fix memory allocation issue when generating PDUs for SDP.
+ Fix race condition on device removal.
+ Fix non-cancelable issue with CreateDevice method.
+ Fix non-working CancelDiscovery method call.
+
+ver 4.39:
+ Add workaround for dealing with unknown inquiry complete.
+ Fix discovering when using software scheduler.
+ Fix wrong NoInputNoOutput IO capability string.
+ Fix race condition with agent during pairing.
+ Fix agent cancellation for security mode 3 acceptor failure.
+ Fix temporary flag removal when device creation fails.
+ Fix hciattach to use ppoll instead of poll.
+ Fix service class update when adapter is down.
+ Fix service classes race condition during startup.
+ Fix release of audio client before freeing the device.
+
+ver 4.38:
+ Add support for builtin plugins.
+ Add framework for adapter operations.
+ Add constants for Enhanced Retransmission modes.
+ Fix HCI socket leak in device_remove_bonding.
+ Fix various format string issues.
+ Fix crashes with various free functions.
+ Fix issues with Headset and A2DP drivers to load again.
+ Fix sending AVRCP button released passthrough messages
+ Fix bug which prevent input devices to work after restart.
+ Fix issue with interpretation of UUID-128 as channel.
+
+ver 4.37:
+ Add version value for Bluetooth 3.0 devices.
+ Add additional L2CAP extended feature mask bits.
+ Add support for loading plugins in priority order.
+ Add support for more detailed usage of disconnect watches.
+ Add support for AVRCP volume control.
+ Add saturated clipping of SBC decoder output to 16-bit.
+ Fix potentially infinite recursion of adapter_up.
+ Fix SCO handling in the case of an incoming call.
+ Fix input service to use confirm callback.
+ Fix cleanup of temporary device entries from storage.
+
+ver 4.36:
+ Add proper tracking of AVCTP connect attempts.
+ Add support to channel pattern in Serial interface.
+ Fix A2DP sink crash if removing device while connecting.
+ Fix error handling if HFP indicators aren't initialized.
+ Fix segfault while handling an incoming SCO connection.
+ Fix Serial.Disconnect to abort connection attempt.
+
+ver 4.35:
+ Add support for Handsfree profile headset role.
+ Add additional checks for open SEIDs from clients.
+ Fix device removal while audio IPC client is connected.
+ Fix device removal when an authorization request is pending.
+ Fix incoming AVDTP connect while authorization in progress.
+ Fix disconnection timers for audio support.
+ Fix various potential NULL pointer deferences.
+ Fix callheld indicator value for multiple calls.
+ Fix voice number type usage.
+ Fix GDBus watch handling.
+
+ver 4.34:
+ Add support for version checks of plugins.
+ Add support for class property on adapter interface.
+ Add support for second SDP attempt after connection reset.
+ Add support for more detailed audio states.
+ Add support for HFP+A2DP auto connection feature.
+ Add support for new and improved audio IPC.
+ Add program for testing audio IPC interface.
+ Fix various AVDTP qualification related issues.
+ Fix broken SDP AttributeIdList parsing.
+ Fix invalid memory access of SDP URL handling.
+ Fix local class of device race conditions.
+ Fix issue with periodic inquiry on startup.
+ Fix missing temporary devices in some situations.
+ Fix SBC alignment issue for encoding with four subbands.
+
+ver 4.33:
+ Add Paired property to the DeviceFound signals.
+ Add support for Headset profile 1.2 version.
+ Fix broken network configuration when IPv6 is disabled.
+ Fix network regression that caused disconnection.
+ Fix SDP truncation of strings with NULL values.
+ Fix service discovery handling of CUPS helper.
+
+ver 4.32:
+ Fix broken SDP record handling.
+ Fix SDP data buffer parsing.
+ Fix more SDP memory leaks.
+ Fix read scan enable calls.
+ Fix A2DP stream handling.
+
+ver 4.31:
+ Add support for new BtIO helper library.
+ Fix AVDTP session close issue.
+ Fix SDP memory leaks.
+ Fix various uninitialized memory issues.
+ Fix duplicate signal emissions.
+ Fix property changes request handling.
+ Fix class of device storage handling.
+
+ver 4.30:
+ Add CID field to L2CAP socket address structure.
+ Fix reset of authentication requirements after bonding.
+ Fix storing of link keys when using dedicated bonding.
+ Fix storing of pre-Bluetooth 2.1 link keys.
+ Fix resetting trust settings on every reboot.
+ Fix handling of local name changes.
+ Fix memory leaks in hciconfig and hcitool
+
+ver 4.29:
+ Use AVRCP version 1.0 for now.
+ Decrease AVDTP idle timeout to one second.
+ Delay AVRCP connection when remote device connects A2DP.
+ Add workaround for AVDTP stream setup with broken headsets.
+ Add missing three-way calling feature bit for Handsfree.
+ Fix handsfree callheld indicator updating.
+ Fix parsing of all AT commands within the buffer.
+ Fix authentication replies when disconnected.
+ Fix handling of debug combination keys.
+ Fix handling of changed combination keys.
+ Fix handling of link keys when using no bonding.
+ Fix handling of invalid/unknown authentication requirements.
+ Fix closing of L2CAP raw socket used for dedicated bonding.
+
+ver 4.28:
+ Add AVDTP signal fragmentation support.
+ Add more SBC performance optimizations.
+ Add more SBC audio quality improvements.
+ Use native byte order for audio plugins.
+ Set the adapter alias only after checking the EIR data.
+ Fix auto-disconnect issue with explicit A2DP connections.
+ Fix invalid memory access of ALSA plugin.
+ Fix compilation with -Wsign-compare.
+
+ver 4.27:
+ Add more SBC optimization (MMX and ARM NEON).
+ Add BT_SECURITY and BT_DEFER_SETUP definitions.
+ Add support for deferred connection setup.
+ Add support for fragmentation of data packets.
+ Add option to trigger dedicated bonding.
+ Follow MITM requirements from remote device.
+ Require MITM for dedicated bonding if capabilities allow it.
+ Fix IO capabilities for non-pairing and pairing cases.
+ Fix no-bonding connections in non-bondable mode.
+ Fix new pairing detection with SSP.
+ Fix bonding with pre-2.1 devices and newer kernels.
+ Fix LIAC setting while toggling Pairable property.
+ Fix device creation for incoming security mode 3 connects.
+ Fix crash within A2DP with bogus pointer.
+ Fix issue with sdp_copy_record() function.
+ Fix crash with extract_des() if sdp_uuid_extract() fails.
+
+ver 4.26:
+ Use of constant shift in SBC quantization code.
+ Add possibility to analyze 4 blocks at once in encoder.
+ Fix correct handling of frame sizes in the encoder.
+ Fix for big endian problems in SBC codec.
+ Fix audio client socket to always be non-blocking.
+ Update telephony support for Maemo.
+
+ver 4.25:
+ Fix receiving data over the audio control socket.
+ Fix subbands selection for joint-stereo in SBC encoder.
+ Add new SBC analysis filter function.
+
+ver 4.24:
+ Fix signal emissions when removing adapters.
+ Fix missing adapter signals on exit.
+ Add support for bringing adapters down on exit.
+ Add support for RememberPowered option.
+ Add support for verbose compiler warnings.
+ Add more options to SBC encoder.
+
+ver 4.23:
+ Update audio IPC for better codec handling.
+ Fix bitstream optimization for SBC encoder.
+ Fix length header values of IPC messages.
+ Fix multiple coding style violations.
+ Fix FindDevice to handle temporary devices.
+ Add configuration option for DeviceID.
+ Add support for InitiallyPowered option.
+ Add missing signals for manager properties.
+ Add telephony support for Maemo.
+
+ver 4.22:
+ Add deny statements to D-Bus access policy.
+ Add support for LegacyPairing property.
+ Add support for global properties.
+ Add more commands to telephony testing script.
+ Add sender checks for serial and network interfaces.
+ Remove deprecated methods and signals from input interface.
+ Remove deprecated methods and signals from network interface.
+ Remove OffMode option and always use device down.
+
+ver 4.21:
+ Fix adapter initialization logic.
+ Fix adapter setup and start security manager early.
+ Fix usage issue with first_init variable.
+
+ver 4.20:
+ Cleanup session handling.
+ Cleanup mode setting handling.
+ Fix issue with concurrent audio clients.
+ Fix issue with HFP/HSP suspending.
+ Fix AT result code syntax handling.
+ Add Handsfree support for AT+NREC.
+ Add PairableTimeout adapter property.
+
+ver 4.19:
+ Fix installation of manual pages for old daemons.
+ Fix D-Bus signal emmissions for CreateDevice.
+ Fix issues with UUID probing.
+ Fix +BSRF syntax issue.
+ Add Pairable adapter property.
+ Add sdp_copy_record() library function.
+
+ver 4.18:
+ Fix release before close issue with RFCOMM TTYs.
+ Fix Connected property on input interface.
+ Fix DeviceFound signals during initial name resolving.
+ Fix service discovery handling.
+ Fix duplicate UUID detection.
+ Fix SBC gain mismatch and decoding handling.
+ Add more options to SBC encoder and decoder.
+ Add special any adapter object for service interface.
+ Add variable prefix to adapter and device object paths.
+
+ver 4.17:
+ Fix SBC encoder not writing last frame.
+ Fix missing timer for A2DP suspend.
+ Add more supported devices to hid2hci utility.
+ Add additional functionality to Handsfree support.
+
+ver 4.16:
+ Fix wrong parameter usage of watch callbacks.
+ Fix parameters for callback upon path removal.
+ Fix unloading of adapter drivers.
+
+ver 4.15:
+ Fix various A2DP state machine issues.
+ Fix some issues with the Handsfree error reporting.
+ Fix format string warnings with recent GCC versions.
+ Remove dependency on GModule.
+
+ver 4.14:
+ Fix types of property arrays.
+ Fix potential crash with input devices.
+ Fix PS3 BD remote input event generation.
+ Allow dynamic adapter driver registration.
+ Update udev rules.
+
+ver 4.13:
+ Fix service discovery and UUID handling.
+ Fix bonding issues with Simple Pairing.
+ Fix file descriptor misuse of SCO connections.
+ Fix various memory leaks in the device handling.
+ Fix AVCTP disconnect handling.
+ Fix GStreamer modes for MP3 encoding.
+ Add operator selection to Handsfree support.
+
+ver 4.12:
+ Fix crash with missing icon value.
+ Fix error checks of HAL plugin.
+ Fix SCO server socket cleanup on exit.
+ Fix memory leaks from DBusPendingCall.
+ Fix handling of pending authorization requests.
+ Fix missing protocol UUIDs in record pattern.
+
+ver 4.11:
+ Change SCO server socket into a generic one.
+ Add test script for dummy telephony plugin.
+ Fix uninitialized reply of multiple GetProperties methods.
+
+ver 4.10:
+ Fix memory leaks with HAL messages.
+ Add more advanced handsfree features.
+ Add properties to audio, input and network interfaces.
+ Stop device discovery timer on device removal.
+
+ver 4.9:
+ Fix signals for Powered and Discoverable properties.
+ Fix handling of Alias and Icon properties.
+ Fix duplicate entries for service UUIDs.
+
+ver 4.8:
+ Fix retrieving of formfactor value.
+ Fix retrieving of local and remote extended features.
+ Fix potential NULL pointer dereference during pairing.
+ Fix crash with browsing due to a remotely initated pairing.
+
+ver 4.7:
+ Fix pairing and service discovery logic.
+ Fix crashes during suspend and resume.
+ Fix race condition within devdown mode.
+ Add RequestSession and ReleaseSession methods.
+ Add Powered and Discoverable properties.
+ Add Devices property and deprecate ListDevices.
+ Add workaround for a broken carkit from Nokia.
+
+ver 4.6:
+ Fix Device ID record handling.
+ Fix service browsing and storage.
+ Fix authentication and encryption for input devices.
+ Fix adapter name initialization.
+
+ver 4.5:
+ Fix initialization issue with new adapters.
+ Send HID authentication request without blocking.
+ Hide the verbose SDP debug behind SDP_DEBUG.
+ Add extra UUIDs for service discovery.
+ Add SCO server socket listener.
+ Add authorization support to service plugin.
+
+ver 4.4:
+ Add temporary fix for the CUPS compile issue.
+ Add service-api.txt to distribution.
+ Mention the variable prefix of an object path
+
+ver 4.3:
+ Add dummy driver for telephony support.
+ Add support for discovery sessions.
+ Add service plugin for external services.
+ Various cleanups.
+
+ver 4.2:
+ Avoid memory copies in A2DP write routine.
+ Fix broken logic with Simple Pairing check and old kernels.
+ Allow non-bondable and outgoing SDP without agent.
+ Only remove the bonding for non-temporary devices.
+ Cleanup various unnecessary includes.
+ Make more unexported functions static.
+ Add basic infrastructure for gtk-doc support.
+
+ver 4.1:
+ Add 30 seconds timeout to BNEP connection setup phase.
+ Avoid memory copies in A2DP write routine for ALSA.
+ Make sure to include compat/sdp.h in the distribution.
+
+ver 4.0:
+ Initial public release.
+
+ver 3.36:
+ Add init routines for TI BRF chips.
+ Add extra attributes to the serial port record.
+ Add example record for headset audio gateway record.
+ Use Handsfree version 0x0105 for the gateway role.
+ Fix SDP record registration with specific record handles.
+ Fix BCSP sent/receive handling.
+ Fix various includes for cross-compilation.
+ Allow link mode settings for outgoing connections.
+ Allow bonding during periodic inquiry.
+
+ver 3.35:
+ Add two additional company identifiers.
+ Add UUID-128 support for service discovery.
+ Fix usage of friendly names for service discovery.
+ Fix authorization when experiemental is disabled.
+ Fix uninitialized variable in passkey request handling.
+ Enable output of timestamps for l2test and rctest.
+
+ver 3.34:
+ Replace various SDP functions with safe versions.
+ Add additional length validation for incoming SDP packets.
+ Use safe function versions for SDP client handling.
+ Fix issue with RemoveDevice during discovery procedure.
+ Fix collect for non-persistent service records.
+
+ver 3.33:
+ Add functions for reading and writing the link policy settings.
+ Add definition for authentication requirements.
+ Add support for handling Simple Pairing.
+ Add Simple Pairing support to Agent interface.
+ Add ReleaseMode method to Adapter interface.
+ Add DiscoverServices method to Device interface.
+ Remove obsolete code and cleanup the repository.
+ Move over to use the libgdbus API.
+ Enable PIE by default if supported.
+
+ver 3.32:
+ Add OCF constants for synchronous flow control enabling.
+ Add support for switching HID proxy devices from Dell.
+ Add more Bluetooth client/server helper functions.
+ Add support for input service idle timeout option.
+ Fix BNEP reconnection handling.
+ Fix return value for snd_pcm_hw_params() calls.
+ Use upper-case addresses for object paths.
+ Remove HAL support helpers.
+ Remove inotify support.
+ Remove service daemon activation handling.
+ Remove uneeded D-Bus API extension.
+
+ver 3.31:
+ Create device object for all pairing cases.
+ Convert authorization to internal function calls.
+ Add initial support for Headset Audio Gateway role.
+ Add generic Bluetooth helper functions for GLib.
+ Fix endiannes handling of connection handles.
+ Don't optimize when debug is enabled.
+
+ver 3.30:
+ Convert audio service into a plugin.
+ Convert input service into a plugin.
+ Convert serial service into a plugin.
+ Convert network service into a plugin.
+ Emit old device signals when a property is changed.
+ Fix missing DiscoverDevices and CancelDiscovery methods.
+ Add another company identifier.
+ Add basic support for Bluetooth sessions.
+ Add avinfo utility for AVDTP/A2DP classification.
+ Remove build option for deprecated sdpd binary.
+
+ver 3.29:
+ Introduce new D-Bus based API.
+ Add more SBC optimizations.
+ Add support for PS3 remote devices.
+ Fix alignment trap in SDP server.
+ Fix memory leak in sdp_get_uuidseq_attr function.
+
+ver 3.28:
+ Add support for MCAP UUIDs.
+ Add support for role switch for audio service.
+ Add disconnect timer for audio service.
+ Add disconnect detection to ALSA plugin.
+ Add more SBC optimizations.
+ Fix alignment issue of SDP server.
+ Remove support for SDP parsing via expat.
+
+ver 3.27:
+ Update uinput.h with extra key definitions.
+ Add support for input connect/disconnect callbacks.
+ Add ifdefs around some baud rate definitions.
+ Add another company identifier.
+ Add proper HFP service level connection handling.
+ Add basic headset automatic disconnect support.
+ Add support for new SBC API.
+ Fix SBC decoder noise at high bitpools.
+ Use 32-bit multipliers for further SBC optimization.
+ Check for RFCOMM connection state in SCO connect callback.
+ Make use of parameters selected in ALSA plugin.
+
+ver 3.26:
+ Fix compilation issues with UCHAR_MAX, USHRT_MAX and UINT_MAX.
+ Improve handling of different audio transports.
+ Enable services by default and keep old daemons disabled.
+
+ver 3.25:
+ Add limited support for Handsfree profile.
+ Add limited support for MPEG12/MP3 codec.
+ Add basic support for UNITINFO and SUBUNITINFO.
+ Add more SBC optimizations.
+ Fix external service (un)registration.
+ Allow GetInfo and GetAddress to fail.
+
+ver 3.24:
+ Add definitions for MDP.
+ Add TCP connection support for serial proxy.
+ Add fix for Logitech HID proxy switching.
+ Add missing macros, MIN, MAX, ABS and CLAMP.
+ Add more SBC encoder optimizations.
+ Add initial mechanism to handle headset commands.
+ Fix connecting to handsfree profile headsets.
+ Use proper function for checking signal name.
+
+ver 3.23:
+ Fix remote name request handling bug.
+ Fix key search function to honor the mmap area size.
+ Fix Avahi integration of network service.
+ Add new plugin communication for audio service.
+ Enable basic AVRCP support by default.
+ More optimizations to the SBC library.
+ Create common error definitions.
+
+ver 3.22:
+ Add missing include file from audio service.
+ Add SBC conformance test utility.
+ Add basic uinput support for AVRCP.
+ Fix L2CAP socket leak in audio service.
+ Fix buffer usage in GStreamer plugin.
+ Fix remote name request event handling.
+
+ver 3.21:
+ Add constant for Bluetooth socket options level.
+ Add initial AVRCP support.
+ Add A2DP sink support to GStreamer plugin.
+ Fix interoperability with A2DP suspend.
+ Fix sign error in 8-subband encoder.
+ Fix handling of service classes length size.
+ Store Extended Inquiry Response data information.
+ Publish device id information through EIR.
+ Support higher baud rates for Ericcson based chips.
+
+ver 3.20:
+ Fix GStreamer plugin file type detection.
+ Fix potential infinite loop in inotify support.
+ Fix D-Bus signatures for dict handling.
+ Fix issues with service activation.
+ Fix SDP failure handling of audio service.
+ Fix various memory leaks in input service.
+ Add secure device creation method to input service.
+ Add service information methods to serial service.
+ Add config file support to network service.
+ Add scripting capability to network service.
+ Add special on-mode handling.
+ Add optimization for SBC encoder.
+ Add tweaks for D-Bus 1.1.x libraries.
+ Add support for inquiry transmit power level.
+
+ver 3.19:
+ Limit range of bitpool announced while in ACP side.
+ Use poll instead of usleep to wait for worker thread.
+ Use default event mask from the specification.
+ Add L2CAP mode constants.
+ Add HID proxy support for Logitech diNovo Edge dongle.
+ Add refresh option to re-request device names.
+ Show correct connection link type.
+
+ver 3.18:
+ Don't allocate memory for the Bluetooth base UUID.
+ Implement proper locking for headsets.
+ Fix various A2DP SEP locking issues.
+ Fix and cleanup audio stream handling.
+ Fix stream starting if suspend request is pending.
+ Fix A2DP and AVDTP endianess problems.
+ Add network timeout and retransmission support.
+ Add more detailed decoding of EIR elements.
+
+ver 3.17:
+ Fix supported commands bit calculation.
+ Fix crashes in audio and network services.
+ Check PAN source and destination roles.
+ Only export the needed symbols for the plugins.
+
+ver 3.16:
+ Update company identifier list.
+ Add support for headsets with SCO audio over HCI.
+ Add support for auto-create through ALSA plugin.
+ Add support for ALSA plugin parameters.
+ Add GStreamer plugin with SBC decoder and encoder.
+ Fix network service NAP, GN and PANU servers.
+ Set EIR information from SDP database.
+
+ver 3.15:
+ Add A2DP support to the audio service.
+ Add proxy support to the serial service.
+ Extract main service class for later use.
+ Set service classes value from SDP database.
+
+ver 3.14:
+ Add missing signals for the adapter interface.
+ Add definitions and functions for Simple Pairing.
+ Add basic commands for Simple Pairing.
+ Add correct Simple Pairing and EIR interaction.
+ Add missing properties for remote information.
+ Add EPoX endian quirk to the input service.
+ Fix HID descriptor import and storage functions.
+ Fix handling of adapters in raw mode.
+ Fix remote device listing methods.
+
+ver 3.13:
+ Fix some issues with the headset support.
+ Fix concurrent pending connection attempts.
+ Fix usage of devname instead of netdev.
+ Add identifier for Nokia SyncML records.
+ Add command for reading the CSR chip revision.
+ Add generic CSR radio test support.
+ Update HCI command table.
+
+ver 3.12:
+ Add missing HCI command text descriptions
+ Add missing HCI commands structures.
+ Add missing HCI event structures.
+ Add common bachk() function.
+ Add support for limited discovery mode.
+ Add support for setting of event mask.
+ Add GetRemoteServiceIdentifiers method.
+ Add skeleton for local D-Bus server.
+ Add headset gain control methods.
+ Fix various headset implementation issues.
+ Fix various serial port service issues.
+ Fix various input service issues.
+ Let CUPS plugin discover printers in range.
+ Improve the BCM2035 UART init routine.
+ Ignore connection events for non-ACL links.
+
+ver 3.11:
+ Update API documentation.
+ Minimize SDP root records and browse groups.
+ Use same decoder for text and URL strings.
+ Fix URL data size handling.
+ Fix SDP pattern extraction for XML.
+ Fix network connection persistent state.
+ Add network connection helper methods.
+ Add initial version of serial port support.
+ Add class of device tracking.
+
+ver 3.10.1:
+ Add option to disable installation of manual pages.
+ Fix input service encryption setup.
+ Fix serial service methods.
+ Fix network service connection handling.
+ Provide a simple init script.
+
+ver 3.10:
+ Add initial version of network service.
+ Add initial version of serial service.
+ Add initial version of input service.
+ Add initial version of audio service.
+ Add authorization framework.
+ Add integer based SBC library.
+ Add version code for Bluetooth 2.1 specification.
+ Add ESCO_LINK connection type constant.
+ Export sdp_uuid32_to_uuid128() function.
+
+ver 3.9:
+ Add RemoteDeviceDisconnectRequested signal.
+ Add updated service framework.
+ Add embedded GLib library.
+ Add support for using system GLib library.
+ Create internal SDP server library.
+
+ver 3.8:
+ Sort discovered devices list based on their RSSI.
+ Send DiscoverableTimeoutChanged signal.
+ Fix local and remote name validity checking.
+ Add ListRemoteDevices and ListRecentRemoteDevices methods.
+ Add basic integration of confirmation concept.
+ Add support for service record description via XML.
+ Add support for external commands to the RFCOMM utility.
+ Add experimental service and authorization API.
+ Add functions for registering binary records.
+
+ver 3.7:
+ Fix class of device handling.
+ Fix error replies with pairing and security mode 3.
+ Fix disconnect method for RFCOMM connections.
+ Add match pattern for service searches.
+ Add support for prioritized watches.
+ Add additional PDU length checks.
+ Fix CSRC value for partial responses.
+
+ver 3.6.1:
+ Fix IO channel race conditions.
+ Fix pairing issues on big endian systems.
+ Fix pairing issues with page timeout errors.
+ Fix pairing state for security mode 3 requests.
+ Switch to user as default security manager mode.
+
+ver 3.6:
+ Update D-Bus based RFCOMM interface support.
+ Use L2CAP raw sockets for HCI connection creation.
+ Add periodic discovery support to the D-Bus interface.
+ Add initial support for device names via EIR.
+ Add proper UTF-8 validation of device names.
+ Add support for the J-Three keyboard.
+ Fix issues with the asynchronous API for SDP.
+
+ver 3.5:
+ Fix and cleanup watch functionality.
+ Add support for periodic inquiry mode.
+ Add support for asynchronous SDP requests.
+ Add more request owner tracking.
+ Add asynchronous API for SDP.
+ Document pageto and discovto options.
+
+ver 3.4:
+ Improve error reporting for failed HCI commands.
+ Improve handling of CancelBonding.
+ Fixed bonding reply message when disconnected.
+ Fix UUID128 string lookup handling.
+ Fix malloc() versus bt_malloc() usage.
+
+ver 3.3:
+ Don't change inquiry mode for Bluetooth 1.1 adapters.
+ Add udev rules for Bluetooth serial PCMCIA cards.
+ Add Cancel and Release methods for passkey agents.
+ Add GetRemoteClass method.
+ Convert to using ppoll() and pselect().
+ Initialize allocated memory to zero.
+ Remove bcm203x firmware loader.
+ Remove kernel specific timeouts.
+ Add additional private data field for SDP sessions.
+ Add host controller to host flow control defines.
+ Add host number of completed packets defines.
+ Initialize various memory to zero before usage.
+
+ver 3.2:
+ Only check for the low-level D-Bus library.
+ Update possible device minor classes.
+ Fix timeout for pending reply.
+ Add more Inquiry with RSSI quirks.
+ Sleep only 100 msecs for device detection.
+ Don't send BondingCreated on link key renewal.
+ Allow storing of all UTF-8 remote device names.
+ Create storage filenames with a generic function.
+ Fix handling of SDP strings.
+ Add adapter type for SDIO cards.
+ Add features bit for link supervision timeout.
+
+ver 3.1:
+ Add missing placeholders for feature bits.
+ Fix handling of raw mode devices.
+ Fix busy loop in UUID extraction routine.
+ Remove inquiry mode setting.
+ Remove auth and encrypt settings.
+
+ver 3.0:
+ Implement the new BlueZ D-Bus API.
+ Fix broken behavior with EVT_CMD_STATUS.
+ Add features bit for pause encryption.
+ Add additional EIR error code.
+ Add more company identifiers.
+ Add another Phonebook Access identifier.
+ Update sniff subrating data structures.
+
+ver 2.25:
+ Use %jx instead of %llx for uint64_t and int64_t.
+ Allow null-terminated text strings.
+ Add UUID for N-Gage games.
+ Add UUID for Apple Macintosh Attributes.
+ Add Apple attributes and iSync records.
+ Add definitions for Apple Agent.
+ Add support for the Handsfree Audio Gateway service.
+ Add support for choosing a specific record handle.
+ Add support for dialup/telephone connections.
+ Add definitions for Apple Agent.
+ Add support for record handle on service registration.
+
+ver 2.24:
+ Fix display of SDP text and data strings.
+ Add support for device scan property.
+ Add support for additional access protocols.
+ Update the D-Bus policy configuration file.
+
+ver 2.23:
+ Update the new D-Bus interface.
+ Make dfutool ready for big endian architectures.
+ Add support for AVRCP specific service records.
+ Add support for writing complex BCCMD commands.
+ Add the new BCCMD interface utility.
+ Add MicroBCSP implementation from CSR.
+ Add constants and definitions for sniff subrating.
+ Add support for allocation of binary text elements.
+ Add HCI emulation tool.
+ Add fake HID support for old EPoX presenters.
+ Reject connections from unknown HID devices.
+ Fix service discovery deadlocks with Samsung D600 phones.
+
+ver 2.22:
+ Remove D-Bus 0.23 support.
+ Add initial version of the new D-Bus interface.
+ Add support for extended inquiry response commands.
+ Add support for the Logitech diNovo Media Desktop Laser.
+ Add compile time buffer checks (FORTIFY SOURCE).
+ Decode reserved LMP feature bits.
+ Fix errno overwrite problems.
+ Fix profile descriptor problem with Samsung phones.
+
+ver 2.21:
+ Move create_dirs() and create_file() into the textfile library.
+ Let textfile_put() also replace the last key value pair.
+ Fix memory leaks with textfile_get() usage.
+ Fix infinite loops and false positive matches.
+ Don't retrieve stored link keys for RAW devices.
+ Document the putkey and delkey commands.
+ Show supported commands also in clear text.
+ Support volatile changes of the BD_ADDR for CSR chips.
+ Add support for identification of supported commands.
+ Add missing OCF declarations for the security filter.
+ Add two new company identifiers.
+
+ver 2.20:
+ Add UUIDs for video distribution profile.
+ Add UUIDs for phonebook access profile.
+ Add attribute identifier for supported repositories.
+ Add definitions for extended inquiry response.
+ Add functions for extended inquiry response.
+ Add support for extended inquiry response.
+ Add support for HotSync service record.
+ Add support for ActiveSync service record.
+ Add ActiveSync networking support.
+ Fix D-Bus crashes with new API versions.
+
+ver 2.19:
+ Fix the GCC 4.0 warnings.
+ Fix the routing for dealing with raw devices.
+ Fix off by one memory allocation error.
+ Fix security problem with escape characters in device name.
+ Add per device service record functions.
+ Send D-Bus signals for inquiry results and remote name resolves.
+ Add support for device specific SDP records.
+
+ver 2.18:
+ Support D-Bus 0.23 and 0.33 API versions.
+ Support reading of complex BCCMD values.
+ Support minimum and maximum encryption key length.
+ Add support for reading and writing the inquiry scan type.
+ Add definitions for connection accept timeout and scan enable.
+ Add support for inquiry scan type.
+ Add tool for the CSR BCCMD interface.
+ Add first draft of the Audio/Video control utility.
+ Add disconnect timer support for the A2DP ALSA plugin.
+ Make SBC parameters configurable.
+ Replace non-printable characters in device names.
+ Remove hci_vhci.h header file.
+ Remove hci_uart.h header file.
+
+ver 2.17:
+ Set the storage directory through ${localstatedir}.
+ Add the textfile library for ASCII based file access.
+ Add support for return link keys event.
+ Add support for voice setting configuration.
+ Add support for page scan timeout configuration.
+ Add support for storing and deleting of stored link keys.
+ Add support for searching for services with UUID-128.
+ Add support for retrieving all possible service records.
+ Add support for a raw mode view of service records.
+ Add support for HID information caching in hidd.
+ Add support for authentication in pand and dund.
+ Add support for changing BD_ADDR of CSR chips.
+ Add pskey utility for changing CSR persistent storage values.
+ Add the firmware upgrade utility.
+ Add connection caching for the A2DP ALSA plugin.
+ Add functions for stored link keys.
+ Add definitions for PIN type and unit key.
+ Add SDP_WAIT_ON_CLOSE flag for sdp_connect().
+ Include stdio.h in bluetooth.h header file.
+ Include sys/socket.h in the header files.
+
+ver 2.16:
+ Store link keys in ASCII based file format.
+ Support device name caching.
+ Support zero length data sizes in l2test.
+ Change default l2ping data size to 44 bytes.
+ Hide the server record and the public browse group root.
+ Read BD_ADDR if not set and if it is a raw device.
+ Add SDP language attributes.
+ Add support for browsing the L2CAP group.
+ Add support for stored pin codes for outgoing connections.
+ Add support for local commands and extended features.
+ Add support for reading CSR panic and fault codes.
+ Add config option for setting the inquiry mode.
+ Add OUI decoding support.
+ Use unlimited inquiry responses as default.
+ Use cached device names for PIN request.
+ Use the clock offset when getting the remote names.
+ Add function for reading local supported commands.
+ Add function for reading local extended features.
+ Add function for reading remote extended features.
+ Add function for getting the remote name with a clock offset.
+ Add function for extracting the OUI from a BD_ADDR.
+ Add inquiry info structure with RSSI and page scan mode.
+ Fix buffer allocation for features to string conversion.
+ Support inquiry with unlimited number of responses.
+
+ver 2.15:
+ Enable the RFCOMM service level security.
+ Add deprecated functions for reading the name.
+ Add command for reading the clock offset.
+ Add command for reading the clock.
+ Add function for reading the clock.
+ Add function for reading the local Bluetooth address.
+ Add function for reading the local supported features.
+ Don't configure raw devices.
+ Don't set inquiry scan or page scan on raw devices.
+ Don't show extended information for raw devices.
+ Support L2CAP signal sizes bigger than 2048 bytes.
+ Cleanup of the socket handling code of the test programs.
+ Use better way for unaligned access.
+ Remove sdp_internal.h and its usage.
+
+ver 2.14:
+ Make use of additional connection information.
+ Use library function for reading the RSSI.
+ Use library function for reading the link quality.
+ Use library function for reading the transmit power level.
+ Use library functions for the link supervision timeout.
+ Add tool for changing the device address.
+ Add function for reading the RSSI.
+ Add function for reading the link quality.
+ Add function for reading the transmit power level.
+ Add functions for the link supervision timeout.
+ Remove deprecated functions.
+ Update AM_PATH_BLUEZ macro.
+
+ver 2.13:
+ Use file permission 0600 for the link key file.
+ Add support for HID attribute descriptions.
+ Add support for Device ID attributes.
+ Add Device ID and HID attribute definitions.
+ Update the UUID constants and its translations.
+ Update L2CAP socket option definitions.
+ Update connection information definitions.
+ Various whitespace cleanups.
+
+ver 2.12:
+ Inherit the device specific options from the default.
+ Use --device for selecting the source device.
+ Add --nosdp option for devices with resource limitation.
+ Add support and parameter option for secure mode.
+ Add a lot of build ids and hardware revisions.
+ Add service classes and profile ids for WAP.
+ Add simple AM_PATH_BLUEZ macro.
+ Update UUID translation tables.
+ Correct kernel interface for CMTP and HIDP support.
+
+ver 2.11:
+ Initial support for the kernel security manager.
+ Various cleanups to avoid inclusion of kernel headers.
+ Fix output when the CUPS backend is called without arguments.
+ Fix problems with a 64 bit userland.
+ Use Bluetooth library functions if available.
+ Use standard numbering scheme of SDP record handles.
+ Use bit zero for vendor packets in the filter type bitmask.
+ Add SIM Access types for service discovery.
+ Add more audio/video profile translations.
+ Add another company identifier.
+ Add the missing HCI error codes.
+ Add RFCOMM socket options.
+ Add definition for the SECURE link mode.
+ Add functions for reading and writing the inquiry mode.
+ Add functions for AFH related settings and information.
+ Add version identifier for the Bluetooth 2.0 specification.
+ Add a master option to the hidd.
+ Add support for changing the link key of a connection.
+ Add support for requesting encryption on keyboards.
+ Add support for revision information of Digianswer devices.
+ Add support for the Zoom, IBM and TDK PCMCIA cards.
+ Add checks for the OpenOBEX and the ALSA libraries.
+ Add experimental mRouter support.
+
+ver 2.10:
+ Use a define for the configuration directory.
+ Fix string initialization for flags translation.
+ Fix and extend the unaligned access macros.
+ Make compiling with debug information optional.
+ Don't override CFLAGS from configure.
+ Check for usb_get_busses() and usb_interrupt_read().
+ Add optional support for compiling with PIE.
+ Make installation of the init scripts optional.
+ Make compiling with debug information optional.
+ Don't override CFLAGS from configure.
+
+ver 2.9:
+ Retry SDP connect if busy in the CUPS backend.
+ Use packet type and allow role switch in hcitool.
+ Use the functions from the USB library for hid2hci.
+ Add Broadcom firmware loader.
+ Add EPoX endian quirk for buggy keyboards.
+ Add L2CAP info type and info result definitions.
+ Add value for L2CAP_CONF_RFC_MODE.
+ Change RSSI value to signed instead of unsigned.
+ Allow UUID32 values as protocol identifiers.
+ Update the autoconf/automake scripts.
+
+ver 2.8:
+ Use LIBS and LDADD instead of LDFLAGS.
+ Use HIDP subclass field for HID boot protocol.
+ Set olen before calling getsockopt() in pand.
+ Restore signals for dev-up script.
+ Add PID file support for pand.
+ Add size parameter to expand_name() in hcid.
+ Add support for audio source and audio sink SDP records.
+ Add support for HID virtual cable unplug.
+ Add support for AmbiCom BT2000C card.
+ Add defines and UUID's for audio/video profiles.
+ Add AVDTP protocol identifier.
+ Add HIDP subclass field.
+ Add PKGConfig support.
+ Fix the event code of inquiry with RSSI.
+ Remove dummy SDP library.
+
+ver 2.7:
+ Fix display of decoded LMP features.
+ Update company identifiers.
+ Add AFH related types.
+ Add first bits from EDR prototyping specification.
+ Add support for inquiry with RSSI.
+ Add HCRP related SDP functions.
+ Add HIDP header file.
+ Add support for getting the AFH channel map.
+ Add support for AFH mode.
+ Add support for inquiry mode.
+ Add Bluetooth backend for CUPS.
+ Add the hid2hci utility.
+ Add the hidd utility.
+ Add the pand utility.
+ Add the dund utility.
+ More endian bug fixes.
+ Give udev some time to create the RFCOMM device nodes.
+ Release the TTY if no device node is found.
+ New startup script for the Bluetooth subsystem.
+ Update to the autoconf stuff.
+
+ver 2.6:
+ Change default prefix to /usr.
+ Add manpages for hcid and hcid.conf.
+ Add the sdpd server daemon.
+ Add the sdptool utility.
+ Add the ciptool utility.
+ Add new company identifiers.
+ Add BNEP and CMTP header files.
+ Add the SDP library.
+ Use R2 for default value of pscan_rep_mode.
+
+ver 2.5:
+ Add decoding of Bluetooth 1.2 features.
+ Add link manager version parameter for Bluetooth 1.2.
+ Add new company identifiers.
+ Add D-Bus support for PIN request.
+ Support for transmit power level.
+ Support for park, sniff and hold mode.
+ Support for role switch.
+ Support for reading the clock offset.
+ Support for requesting authentication.
+ Support for setting connection encryption.
+ Show revision information for Broadcom devices.
+ Replace unprintable characters in device name.
+ Use R1 for default value of pscan_rep_mode.
+ Fix some 64-bit problems.
+ Fix some endian problems.
+ Report an error on PIN helper failure.
+ Update bluepin script for GTK2.
+
+ver 2.4:
+ Increase number of inquiry responses.
+ Support for transmit power level.
+ Display all 8 bytes of the features.
+ Add support for reading and writing of IAC.
+ Correct decoding class of device.
+ Use Ericsson revision command for ST Microelectronics devices.
+ Display AVM firmware version with 'revision' command.
+ New code for CSR specific revision information.
+ Support for ST Microelectronics specific initialization.
+ Support for 3Com card version 3.0.
+ Support for TDK, IBM and Socket cards.
+ Support for initial baud rate.
+ Update man pages.
+ Fixes for some memory leaks.
+
+ver 2.3:
+ Added const qualifiers to appropriate function arguments.
+ Minor fixes.
+ CSR firmware version is now displayed by 'revision' command.
+ Voice command is working properly on big endian machines.
+ Added support for Texas Bluetooth modules.
+ Added support for high UART baud rates on Ericsson modules.
+ BCSP initialization fixes.
+ Support for role switch command (hcitool).
+ RFCOMM config file parser fixes.
+ Update man pages.
+ Removed GLib dependency.
+
+ver 2.2:
+ Updated RFCOMM header file.
+ Additional HCI command and event defines.
+ Support for voice settings (hciconfig).
+ Minor hcitool fixes.
+ Improved configure script.
+ Added Headset testing tool.
+ Updated man pages.
+ RPM package.
+
+ver 2.1.1:
+ Resurrect hci_remote_name.
+
+ver 2.1:
+ Added hci_{read, write}_class_of_dev().
+ Added hci_{read, write}_current_iac_lap().
+ Added hci_write_local_name().
+ Added RFCOMM header file.
+ Minor fixes.
+ Improved BCSP initialization (hciattach).
+ Support for displaying link quality (hcitool).
+ Support for changing link supervision timeout (hcitool).
+ New RFCOMM TTY configuration tool (rfcomm).
+ Minor fixes and updates.
+
+ver 2.0:
+ Additional company IDs.
+ BCSP initialization (hciattach).
+ Minor hciconfig fixes.
+
+ver 2.0-pr13:
+ Support for multiple pairing modes.
+ Link key database handling fixes.
+
+ver 2.0-pre12:
+ Removed max link key limit. Keys never expire.
+ Link key database is always updated. Reread PIN on SIGHUP (hcid).
+ Bluetooth script starts SDPd, if installed.
+ Other minor fixes.
+
+ver 2.0-pre11:
+ Improved link key management and more verbose logging (hcid).
+ Fixed scan command (hcitool).
+
+ver 2.0-pre10:
+ Fix hci_inquiry function to return errors and accept user buffers.
+ New functions hci_devba, hci_devid, hci_for_each_dev and hci_get_route.
+ Additional company IDs.
+ Makefile and other minor fixes.
+ Support for reading RSSI, remote name and changing
+ connection type (hcitool).
+ Device initialization fixes (hcid).
+ Other minor fixes and improvements.
+ Build environment cleanup and fixes.
+
+ver 2.0-pre9:
+ Improved bluepin. Working X authentication.
+ Improved hcitool. New flexible cmd syntax, additional commands.
+ Human readable display of the device features.
+ LMP features to string translation support.
+ Additional HCI command and event defines.
+ Extended hci_filter API.
+
+ver 2.0-pre8:
+ Additional HCI ioctls and defines.
+ All strings and buffers are allocated dynamically.
+ ba2str, str2ba automatically swap bdaddress.
+ Additional hciconfig commands. Support for ACL and SCO MTU ioctls.
+ Support for Inventel and COM1 UART based devices.
+ Minor hcitool fixes.
+ Improved l2test. New L2CAP test modes.
+ Minor fixes and cleanup.
+
+ver 2.0-pre7:
+ Bluetooth libraries and header files is now a separate package.
+ New build environment uses automake and libtool.
+ Massive header files cleanup.
+ Bluetooth utilities is now a separate package.
+ New build environment uses automake.
+ Moved all config files and security data to /etc/bluetooth.
+ Various cleanups.
+
+ver 2.0-pre6:
+ API cleanup and additions.
+ Improved hcitool.
+ l2test minor output fixes.
+ hciattach opt to display list of supported devices.
+
+ver 2.0-pre4:
+ HCI filter enhancements.
+
+ver 2.0-pre3:
+ Cleanup.
+
+ver 2.0-pre2:
+ Additional HCI library functions.
+ Improved CSR baud rate initialization.
+ PCMCIA scripts fixes and enhancements.
+ Documentation update.
+
+ver 2.0-pre1:
+ New UART initialization utility.
+ Hot plugging support for UART based PCMCIA devices.
+ SCO testing utility.
+ New authentication utility (bluepin).
+ Minor fixes and improvements.
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..56b077d
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,236 @@
+Installation Instructions
+*************************
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free
+Software Foundation, Inc.
+
+This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
+Basic Installation
+==================
+
+These are generic installation instructions.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+ It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring. (Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.)
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+ The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'. You only need
+`configure.ac' if you want to change it or regenerate `configure' using
+a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system. If you're
+ using `csh' on an old version of System V, you might need to type
+ `sh ./configure' instead to prevent `csh' from trying to execute
+ `configure' itself.
+
+ Running `configure' takes awhile. While running, it prints some
+ messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 5. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+Compilers and Options
+=====================
+
+Some systems require unusual options for compilation or linking that the
+`configure' script does not know about. Run `./configure --help' for
+details on some of the pertinent environment variables.
+
+ You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment. Here
+is an example:
+
+ ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
+
+ *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+ If you have to use a `make' that does not support the `VPATH'
+variable, you have to compile the package for one architecture at a
+time in the source code directory. After you have installed the
+package for one architecture, use `make distclean' before reconfiguring
+for another architecture.
+
+Installation Names
+==================
+
+By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc. You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PREFIX'.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+give `configure' the option `--exec-prefix=PREFIX', the package will
+use PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+ In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files. Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+There may be some features `configure' cannot figure out automatically,
+but needs to determine by the type of machine the package will run on.
+Usually, assuming the package is built to be run on the _same_
+architectures, `configure' can figure that out, but if it prints a
+message saying it cannot guess the machine type, give it the
+`--build=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+ CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+ OS KERNEL-OS
+
+ See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+ If you are _building_ compiler tools for cross-compiling, you should
+use the `--target=TYPE' option to select the type of system they will
+produce code for.
+
+ If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+If you want to set default values for `configure' scripts to share, you
+can create a site shell script called `config.site' that gives default
+values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+Variables not defined in a site shell script can be set in the
+environment passed to `configure'. However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost. In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'. For example:
+
+ ./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script). Here is a another example:
+
+ /bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent
+configuration-related scripts to be executed by `/bin/bash'.
+
+`configure' Invocation
+======================
+
+`configure' recognizes the following options to control how it operates.
+
+`--help'
+`-h'
+ Print a summary of the options to `configure', and exit.
+
+`--version'
+`-V'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`--cache-file=FILE'
+ Enable the cache: use and save the results of the tests in FILE,
+ traditionally `config.cache'. FILE defaults to `/dev/null' to
+ disable caching.
+
+`--config-cache'
+`-C'
+ Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made. To
+ suppress all normal output, redirect it to `/dev/null' (any error
+ messages will still be shown).
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`configure' also accepts some other, not widely useful, options. Run
+`configure --help' for more details.
+
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..a23cc60
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,431 @@
+
+AM_MAKEFLAGS = --no-print-directory
+
+lib_LTLIBRARIES =
+
+noinst_LTLIBRARIES =
+
+bin_PROGRAMS =
+
+sbin_PROGRAMS =
+
+noinst_PROGRAMS =
+
+dist_man_MANS =
+
+dist_noinst_MANS =
+
+CLEANFILES =
+
+EXTRA_DIST =
+
+includedir = @includedir@/bluetooth
+
+include_HEADERS =
+
+if CONFIGFILES
+dbusdir = $(sysconfdir)/dbus-1/system.d
+
+dbus_DATA = src/bluetooth.conf
+
+confdir = $(sysconfdir)/bluetooth
+
+conf_DATA =
+
+statedir = $(localstatedir)/lib/bluetooth
+
+state_DATA =
+endif
+
+plugindir = $(libdir)/bluetooth/plugins
+
+plugin_LTLIBRARIES =
+
+
+lib_headers = lib/bluetooth.h lib/hci.h lib/hci_lib.h lib/mgmt.h \
+ lib/sco.h lib/l2cap.h lib/sdp.h lib/sdp_lib.h lib/uuid.h \
+ lib/rfcomm.h lib/bnep.h lib/cmtp.h lib/hidp.h
+local_headers = $(foreach file,$(lib_headers), lib/bluetooth/$(notdir $(file)))
+
+include_HEADERS += $(lib_headers)
+
+lib_LTLIBRARIES += lib/libbluetooth.la
+
+lib_libbluetooth_la_SOURCES = $(lib_headers) \
+ lib/bluetooth.c lib/hci.c lib/sdp.c lib/uuid.c
+lib_libbluetooth_la_LDFLAGS = -version-info 14:0:11
+lib_libbluetooth_la_DEPENDENCIES = $(local_headers)
+
+CLEANFILES += $(local_headers)
+
+
+if SBC
+noinst_LTLIBRARIES += sbc/libsbc.la
+
+sbc_libsbc_la_SOURCES = sbc/sbc.h sbc/sbc.c sbc/sbc_math.h sbc/sbc_tables.h \
+ sbc/sbc_primitives.h sbc/sbc_primitives.c \
+ sbc/sbc_primitives_mmx.h sbc/sbc_primitives_mmx.c \
+ sbc/sbc_primitives_iwmmxt.h sbc/sbc_primitives_iwmmxt.c \
+ sbc/sbc_primitives_neon.h sbc/sbc_primitives_neon.c \
+ sbc/sbc_primitives_armv6.h sbc/sbc_primitives_armv6.c
+
+sbc_libsbc_la_CFLAGS = -finline-functions -fgcse-after-reload \
+ -funswitch-loops -funroll-loops
+
+noinst_PROGRAMS += sbc/sbcinfo sbc/sbcdec sbc/sbcenc
+
+sbc_sbcdec_SOURCES = sbc/sbcdec.c sbc/formats.h
+sbc_sbcdec_LDADD = sbc/libsbc.la
+
+sbc_sbcenc_SOURCES = sbc/sbcenc.c sbc/formats.h
+sbc_sbcenc_LDADD = sbc/libsbc.la
+
+if SNDFILE
+noinst_PROGRAMS += sbc/sbctester
+
+sbc_sbctester_LDADD = @SNDFILE_LIBS@ -lm
+sbc_sbctest_CFLAGS = @SNDFILE_CFLAGS@
+endif
+endif
+
+attrib_sources = attrib/att.h attrib/att.c attrib/gatt.h attrib/gatt.c \
+ attrib/gattrib.h attrib/gattrib.c
+
+gdbus_sources = gdbus/gdbus.h gdbus/mainloop.c gdbus/watch.c \
+ gdbus/object.c gdbus/polkit.c
+
+btio_sources = btio/btio.h btio/btio.c
+
+builtin_modules =
+builtin_sources =
+builtin_nodist =
+mcap_sources =
+
+if MCAP
+mcap_sources += health/mcap_lib.h health/mcap_internal.h \
+ health/mcap.h health/mcap.c \
+ health/mcap_sync.c
+endif
+
+if PNATPLUGIN
+builtin_modules += pnat
+builtin_sources += plugins/pnat.c
+endif
+
+if ECHOPLUGIN
+builtin_modules += echo
+builtin_sources += plugins/echo.c
+endif
+
+if AUDIOPLUGIN
+builtin_modules += audio
+builtin_sources += audio/main.c \
+ audio/manager.h audio/manager.c \
+ audio/gateway.h audio/gateway.c \
+ audio/headset.h audio/headset.c \
+ audio/control.h audio/control.c \
+ audio/device.h audio/device.c \
+ audio/source.h audio/source.c \
+ audio/sink.h audio/sink.c \
+ audio/a2dp.h audio/a2dp.c \
+ audio/avdtp.h audio/avdtp.c \
+ audio/ipc.h audio/ipc.c \
+ audio/unix.h audio/unix.c \
+ audio/media.h audio/media.c \
+ audio/transport.h audio/transport.c \
+ audio/telephony.h audio/a2dp-codecs.h
+builtin_nodist += audio/telephony.c
+
+noinst_LIBRARIES = audio/libtelephony.a
+
+audio_libtelephony_a_SOURCES = audio/telephony.h audio/telephony-dummy.c \
+ audio/telephony-maemo5.c audio/telephony-ofono.c \
+ audio/telephony-maemo6.c
+endif
+
+if SAPPLUGIN
+builtin_modules += sap
+builtin_sources += sap/main.c \
+ sap/manager.h sap/manager.c \
+ sap/server.h sap/server.c \
+ sap/sap.h
+
+builtin_nodist += sap/sap.c
+
+noinst_LIBRARIES = sap/libsap.a
+
+sap_libsap_a_SOURCES = sap/sap.h sap/sap-dummy.c
+endif
+
+if INPUTPLUGIN
+builtin_modules += input
+builtin_sources += input/main.c \
+ input/manager.h input/manager.c \
+ input/server.h input/server.c \
+ input/device.h input/device.c \
+ input/fakehid.c input/fakehid.h
+endif
+
+if SERIALPLUGIN
+builtin_modules += serial
+builtin_sources += serial/main.c \
+ serial/manager.h serial/manager.c \
+ serial/proxy.h serial/proxy.c \
+ serial/port.h serial/port.c
+endif
+
+if NETWORKPLUGIN
+builtin_modules += network
+builtin_sources += network/main.c \
+ network/manager.h network/manager.c \
+ network/common.h network/common.c \
+ network/server.h network/server.c \
+ network/connection.h network/connection.c
+endif
+
+if SERVICEPLUGIN
+builtin_modules += service
+builtin_sources += plugins/service.c
+endif
+
+if ATTRIBPLUGIN
+
+if READLINE
+bin_PROGRAMS += attrib/gatttool
+
+attrib_gatttool_SOURCES = attrib/gatttool.c attrib/att.c attrib/gatt.c \
+ attrib/gattrib.c btio/btio.c \
+ src/glib-helper.h src/glib-helper.c \
+ attrib/gatttool.h attrib/interactive.c \
+ attrib/utils.c
+attrib_gatttool_LDADD = lib/libbluetooth.la @GLIB_LIBS@ @READLINE_LIBS@
+endif
+
+builtin_modules += attrib
+builtin_sources += attrib/main.c \
+ attrib/manager.h attrib/manager.c \
+ attrib/client.h attrib/client.c \
+ attrib/example.h attrib/example.c
+endif
+
+if HEALTHPLUGIN
+builtin_modules += health
+builtin_sources += health/hdp_main.c health/hdp_types.h \
+ health/hdp_manager.h health/hdp_manager.c \
+ health/hdp.h health/hdp.c \
+ health/hdp_util.h health/hdp_util.c
+endif
+
+builtin_modules += hciops mgmtops
+builtin_sources += plugins/hciops.c plugins/mgmtops.c
+
+if HAL
+builtin_modules += hal
+builtin_sources += plugins/hal.c
+else
+builtin_modules += formfactor
+builtin_sources += plugins/formfactor.c
+endif
+
+EXTRA_DIST += plugins/hal.c plugins/formfactor.c
+
+builtin_modules += storage
+builtin_sources += plugins/storage.c
+
+if MAEMO6PLUGIN
+builtin_modules += maemo6
+builtin_sources += plugins/maemo6.c
+endif
+
+if DBUSOOBPLUGIN
+builtin_modules += dbusoob
+builtin_sources += plugins/dbusoob.c
+endif
+
+sbin_PROGRAMS += src/bluetoothd
+
+src_bluetoothd_SOURCES = $(gdbus_sources) $(builtin_sources) \
+ $(attrib_sources) $(btio_sources) \
+ $(mcap_sources) src/bluetooth.ver \
+ src/main.c src/log.h src/log.c \
+ src/rfkill.c src/hcid.h src/sdpd.h \
+ src/sdpd-server.c src/sdpd-request.c \
+ src/sdpd-service.c src/sdpd-database.c \
+ src/attrib-server.h src/attrib-server.c \
+ src/sdp-xml.h src/sdp-xml.c \
+ src/textfile.h src/textfile.c \
+ src/glib-helper.h src/glib-helper.c \
+ src/oui.h src/oui.c src/uinput.h src/ppoll.h \
+ src/plugin.h src/plugin.c \
+ src/storage.h src/storage.c \
+ src/agent.h src/agent.c \
+ src/error.h src/error.c \
+ src/manager.h src/manager.c \
+ src/adapter.h src/adapter.c \
+ src/device.h src/device.c \
+ src/dbus-common.c src/dbus-common.h \
+ src/event.h src/event.c \
+ src/oob.h src/oob.c
+src_bluetoothd_LDADD = lib/libbluetooth.la @GLIB_LIBS@ @DBUS_LIBS@ \
+ @CAPNG_LIBS@ -ldl -lrt
+src_bluetoothd_LDFLAGS = -Wl,--export-dynamic \
+ -Wl,--version-script=$(srcdir)/src/bluetooth.ver
+
+src_bluetoothd_DEPENDENCIES = lib/libbluetooth.la
+
+builtin_files = src/builtin.h $(builtin_nodist)
+
+nodist_src_bluetoothd_SOURCES = $(builtin_files)
+
+CLEANFILES += $(builtin_files)
+
+man_MANS = src/bluetoothd.8
+
+if CONFIGFILES
+conf_DATA += src/main.conf
+endif
+
+EXTRA_DIST += src/genbuiltin src/bluetooth.conf \
+ src/main.conf network/network.conf \
+ input/input.conf serial/serial.conf \
+ audio/audio.conf audio/telephony-dummy.c \
+ audio/telephony-maemo5.c audio/telephony-ofono.c \
+ audio/telephony-maemo6.c sap/sap-dummy.c
+
+
+if ALSA
+alsadir = $(libdir)/alsa-lib
+
+alsa_LTLIBRARIES = audio/libasound_module_pcm_bluetooth.la \
+ audio/libasound_module_ctl_bluetooth.la
+
+audio_libasound_module_pcm_bluetooth_la_SOURCES = audio/pcm_bluetooth.c \
+ audio/rtp.h audio/ipc.h audio/ipc.c
+audio_libasound_module_pcm_bluetooth_la_LDFLAGS = -module -avoid-version #-export-symbols-regex [_]*snd_pcm_.*
+audio_libasound_module_pcm_bluetooth_la_LIBADD = sbc/libsbc.la \
+ lib/libbluetooth.la @ALSA_LIBS@
+audio_libasound_module_pcm_bluetooth_la_CFLAGS = @ALSA_CFLAGS@
+
+audio_libasound_module_ctl_bluetooth_la_SOURCES = audio/ctl_bluetooth.c \
+ audio/rtp.h audio/ipc.h audio/ipc.c
+audio_libasound_module_ctl_bluetooth_la_LDFLAGS = -module -avoid-version #-export-symbols-regex [_]*snd_ctl_.*
+audio_libasound_module_ctl_bluetooth_la_LIBADD = lib/libbluetooth.la @ALSA_LIBS@
+audio_libasound_module_ctl_bluetooth_la_CFLAGS = @ALSA_CFLAGS@
+
+if CONFIGFILES
+alsaconfdir = $(datadir)/alsa
+
+alsaconf_DATA = audio/bluetooth.conf
+endif
+endif
+
+if AUDIOPLUGIN
+if GSTREAMER
+gstreamerdir = $(libdir)/gstreamer-0.10
+
+gstreamer_LTLIBRARIES = audio/libgstbluetooth.la
+
+audio_libgstbluetooth_la_SOURCES = audio/gstbluetooth.c audio/gstpragma.h \
+ audio/gstsbcenc.h audio/gstsbcenc.c \
+ audio/gstsbcdec.h audio/gstsbcdec.c \
+ audio/gstsbcparse.h audio/gstsbcparse.c \
+ audio/gstavdtpsink.h audio/gstavdtpsink.c \
+ audio/gsta2dpsink.h audio/gsta2dpsink.c \
+ audio/gstsbcutil.h audio/gstsbcutil.c \
+ audio/gstrtpsbcpay.h audio/gstrtpsbcpay.c \
+ audio/rtp.h audio/ipc.h audio/ipc.c
+audio_libgstbluetooth_la_LDFLAGS = -module -avoid-version
+audio_libgstbluetooth_la_LIBADD = sbc/libsbc.la lib/libbluetooth.la \
+ @DBUS_LIBS@ @GSTREAMER_LIBS@ -lgstaudio-0.10 -lgstrtp-0.10
+audio_libgstbluetooth_la_CFLAGS = -fvisibility=hidden -fno-strict-aliasing \
+ $(AM_CFLAGS) @DBUS_CFLAGS@ @GSTREAMER_CFLAGS@
+endif
+endif
+
+EXTRA_DIST += audio/bluetooth.conf
+
+
+include Makefile.tools
+
+if UDEVRULES
+rulesdir = @UDEV_DATADIR@
+
+udev_files = scripts/bluetooth.rules
+
+if HID2HCI
+udev_files += scripts/bluetooth-hid2hci.rules
+endif
+
+if PCMCIA
+udev_files += scripts/bluetooth-serial.rules
+endif
+
+rules_DATA = $(foreach file,$(udev_files), scripts/97-$(notdir $(file)))
+endif
+
+CLEANFILES += $(rules_DATA)
+
+EXTRA_DIST += scripts/bluetooth.rules \
+ scripts/bluetooth-hid2hci.rules scripts/bluetooth-serial.rules
+
+if PCMCIA
+udevdir = $(libexecdir)/udev
+
+dist_udev_SCRIPTS = scripts/bluetooth_serial
+endif
+
+EXTRA_DIST += doc/manager-api.txt \
+ doc/adapter-api.txt doc/device-api.txt \
+ doc/service-api.txt doc/agent-api.txt doc/attribute-api.txt \
+ doc/serial-api.txt doc/network-api.txt \
+ doc/input-api.txt doc/audio-api.txt doc/control-api.txt \
+ doc/hfp-api.txt doc/health-api.txt doc/sap-api.txt \
+ doc/media-api.txt doc/assigned-numbers.txt
+
+AM_YFLAGS = -d
+
+AM_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ @CAPNG_CFLAGS@ \
+ -DBLUETOOTH_PLUGIN_BUILTIN -DPLUGINDIR=\""$(plugindir)"\"
+
+INCLUDES = -I$(builddir)/lib -I$(builddir)/src -I$(srcdir)/src \
+ -I$(srcdir)/audio -I$(srcdir)/sbc -I$(srcdir)/gdbus \
+ -I$(srcdir)/attrib -I$(srcdir)/btio
+
+if MCAP
+INCLUDES += -I$(builddir)/health
+endif
+
+pkgconfigdir = $(libdir)/pkgconfig
+
+pkgconfig_DATA = bluez.pc
+
+DISTCHECK_CONFIGURE_FLAGS = --disable-udevrules --enable-attrib
+
+DISTCLEANFILES = $(pkgconfig_DATA)
+
+MAINTAINERCLEANFILES = Makefile.in \
+ aclocal.m4 configure config.h.in config.sub config.guess \
+ ltmain.sh depcomp compile missing install-sh mkinstalldirs ylwrap
+
+src/plugin.$(OBJEXT): src/builtin.h
+
+src/builtin.h: src/genbuiltin $(builtin_sources)
+ $(AM_V_GEN)$(srcdir)/src/genbuiltin $(builtin_modules) > $@
+
+audio/telephony.c: audio/@TELEPHONY_DRIVER@
+ $(AM_V_GEN)$(LN_S) $(abs_top_srcdir)/$< $@
+
+sap/sap.c: sap/@SAP_DRIVER@
+ $(AM_V_GEN)$(LN_S) $(abs_top_srcdir)/$< $@
+
+scripts/%.rules:
+ $(AM_V_GEN)cp $(subst 97-,,$@) $@
+
+$(lib_libbluetooth_la_OBJECTS): $(local_headers)
+
+lib/bluetooth/%.h: lib/%.h
+ $(AM_V_at)$(MKDIR_P) lib/bluetooth
+ $(AM_V_GEN)$(LN_S) $(abs_top_srcdir)/$< $@
+
+clean-local:
+ $(RM) -r lib/bluetooth
diff --git a/Makefile.in b/Makefile.in
new file mode 100644
index 0000000..0e3933e
--- /dev/null
+++ b/Makefile.in
@@ -0,0 +1,3477 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+
+
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+bin_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3) \
+ $(am__EXEEXT_4) $(am__EXEEXT_5) $(am__EXEEXT_6) \
+ $(am__EXEEXT_7)
+sbin_PROGRAMS = src/bluetoothd$(EXEEXT) $(am__EXEEXT_13) \
+ $(am__EXEEXT_14) $(am__EXEEXT_15) $(am__EXEEXT_16) \
+ $(am__EXEEXT_17)
+noinst_PROGRAMS = $(am__EXEEXT_8) $(am__EXEEXT_9) $(am__EXEEXT_10) \
+ $(am__EXEEXT_11) $(am__EXEEXT_12)
+@SBC_TRUE@am__append_1 = sbc/libsbc.la
+@SBC_TRUE@am__append_2 = sbc/sbcinfo sbc/sbcdec sbc/sbcenc
+@SBC_TRUE@@SNDFILE_TRUE@am__append_3 = sbc/sbctester
+@MCAP_TRUE@am__append_4 = health/mcap_lib.h health/mcap_internal.h \
+@MCAP_TRUE@ health/mcap.h health/mcap.c \
+@MCAP_TRUE@ health/mcap_sync.c
+
+@PNATPLUGIN_TRUE@am__append_5 = pnat
+@PNATPLUGIN_TRUE@am__append_6 = plugins/pnat.c
+@ECHOPLUGIN_TRUE@am__append_7 = echo
+@ECHOPLUGIN_TRUE@am__append_8 = plugins/echo.c
+@AUDIOPLUGIN_TRUE@am__append_9 = audio
+@AUDIOPLUGIN_TRUE@am__append_10 = audio/main.c \
+@AUDIOPLUGIN_TRUE@ audio/manager.h audio/manager.c \
+@AUDIOPLUGIN_TRUE@ audio/gateway.h audio/gateway.c \
+@AUDIOPLUGIN_TRUE@ audio/headset.h audio/headset.c \
+@AUDIOPLUGIN_TRUE@ audio/control.h audio/control.c \
+@AUDIOPLUGIN_TRUE@ audio/device.h audio/device.c \
+@AUDIOPLUGIN_TRUE@ audio/source.h audio/source.c \
+@AUDIOPLUGIN_TRUE@ audio/sink.h audio/sink.c \
+@AUDIOPLUGIN_TRUE@ audio/a2dp.h audio/a2dp.c \
+@AUDIOPLUGIN_TRUE@ audio/avdtp.h audio/avdtp.c \
+@AUDIOPLUGIN_TRUE@ audio/ipc.h audio/ipc.c \
+@AUDIOPLUGIN_TRUE@ audio/unix.h audio/unix.c \
+@AUDIOPLUGIN_TRUE@ audio/media.h audio/media.c \
+@AUDIOPLUGIN_TRUE@ audio/transport.h audio/transport.c \
+@AUDIOPLUGIN_TRUE@ audio/telephony.h audio/a2dp-codecs.h
+
+@AUDIOPLUGIN_TRUE@am__append_11 = audio/telephony.c
+@SAPPLUGIN_TRUE@am__append_12 = sap
+@SAPPLUGIN_TRUE@am__append_13 = sap/main.c \
+@SAPPLUGIN_TRUE@ sap/manager.h sap/manager.c \
+@SAPPLUGIN_TRUE@ sap/server.h sap/server.c \
+@SAPPLUGIN_TRUE@ sap/sap.h
+
+@SAPPLUGIN_TRUE@am__append_14 = sap/sap.c
+@INPUTPLUGIN_TRUE@am__append_15 = input
+@INPUTPLUGIN_TRUE@am__append_16 = input/main.c \
+@INPUTPLUGIN_TRUE@ input/manager.h input/manager.c \
+@INPUTPLUGIN_TRUE@ input/server.h input/server.c \
+@INPUTPLUGIN_TRUE@ input/device.h input/device.c \
+@INPUTPLUGIN_TRUE@ input/fakehid.c input/fakehid.h
+
+@SERIALPLUGIN_TRUE@am__append_17 = serial
+@SERIALPLUGIN_TRUE@am__append_18 = serial/main.c \
+@SERIALPLUGIN_TRUE@ serial/manager.h serial/manager.c \
+@SERIALPLUGIN_TRUE@ serial/proxy.h serial/proxy.c \
+@SERIALPLUGIN_TRUE@ serial/port.h serial/port.c
+
+@NETWORKPLUGIN_TRUE@am__append_19 = network
+@NETWORKPLUGIN_TRUE@am__append_20 = network/main.c \
+@NETWORKPLUGIN_TRUE@ network/manager.h network/manager.c \
+@NETWORKPLUGIN_TRUE@ network/common.h network/common.c \
+@NETWORKPLUGIN_TRUE@ network/server.h network/server.c \
+@NETWORKPLUGIN_TRUE@ network/connection.h network/connection.c
+
+@SERVICEPLUGIN_TRUE@am__append_21 = service
+@SERVICEPLUGIN_TRUE@am__append_22 = plugins/service.c
+@ATTRIBPLUGIN_TRUE@@READLINE_TRUE@am__append_23 = attrib/gatttool
+@ATTRIBPLUGIN_TRUE@am__append_24 = attrib
+@ATTRIBPLUGIN_TRUE@am__append_25 = attrib/main.c \
+@ATTRIBPLUGIN_TRUE@ attrib/manager.h attrib/manager.c \
+@ATTRIBPLUGIN_TRUE@ attrib/client.h attrib/client.c \
+@ATTRIBPLUGIN_TRUE@ attrib/example.h attrib/example.c
+
+@HEALTHPLUGIN_TRUE@am__append_26 = health
+@HEALTHPLUGIN_TRUE@am__append_27 = health/hdp_main.c health/hdp_types.h \
+@HEALTHPLUGIN_TRUE@ health/hdp_manager.h health/hdp_manager.c \
+@HEALTHPLUGIN_TRUE@ health/hdp.h health/hdp.c \
+@HEALTHPLUGIN_TRUE@ health/hdp_util.h health/hdp_util.c
+
+@HAL_TRUE@am__append_28 = hal
+@HAL_TRUE@am__append_29 = plugins/hal.c
+@HAL_FALSE@am__append_30 = formfactor
+@HAL_FALSE@am__append_31 = plugins/formfactor.c
+@MAEMO6PLUGIN_TRUE@am__append_32 = maemo6
+@MAEMO6PLUGIN_TRUE@am__append_33 = plugins/maemo6.c
+@DBUSOOBPLUGIN_TRUE@am__append_34 = dbusoob
+@DBUSOOBPLUGIN_TRUE@am__append_35 = plugins/dbusoob.c
+DIST_COMMON = README $(am__configure_deps) \
+ $(am__dist_udev_SCRIPTS_DIST) $(dist_man_MANS) \
+ $(include_HEADERS) $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
+ $(srcdir)/Makefile.tools $(srcdir)/bluez.pc.in \
+ $(srcdir)/config.h.in $(top_srcdir)/configure \
+ $(top_srcdir)/doc/version.xml.in \
+ $(top_srcdir)/scripts/bluetooth.rules.in \
+ $(top_srcdir)/src/bluetoothd.8.in AUTHORS COPYING COPYING.LIB \
+ ChangeLog INSTALL NEWS TODO compile config.guess config.sub \
+ depcomp install-sh ltmain.sh missing tools/lexer.c \
+ tools/parser.c tools/parser.h ylwrap
+@CONFIGFILES_TRUE@@TOOLS_TRUE@am__append_36 = tools/rfcomm.conf
+@TOOLS_TRUE@am__append_37 = tools/rfcomm tools/l2ping \
+@TOOLS_TRUE@ tools/hcitool tools/sdptool tools/ciptool
+
+@TOOLS_TRUE@am__append_38 = tools/hciattach tools/hciconfig
+@TOOLS_TRUE@am__append_39 = tools/avinfo tools/ppporc \
+@TOOLS_TRUE@ tools/hcieventmask tools/hcisecfilter
+
+@TOOLS_TRUE@am__append_40 = tools/rfcomm.1 tools/l2ping.8 \
+@TOOLS_TRUE@ tools/hciattach.8 tools/hciconfig.8 \
+@TOOLS_TRUE@ tools/hcitool.1 tools/sdptool.1 tools/ciptool.1
+
+@TOOLS_FALSE@am__append_41 = tools/rfcomm.1 tools/l2ping.8 \
+@TOOLS_FALSE@ tools/hciattach.8 tools/hciconfig.8 \
+@TOOLS_FALSE@ tools/hcitool.1 tools/sdptool.1 tools/ciptool.1
+
+@TRACER_TRUE@am__append_42 = tracer/hcitrace
+@BCCMD_TRUE@am__append_43 = tools/bccmd
+@BCCMD_TRUE@@USB_TRUE@am__append_44 = tools/csr_usb.c
+@BCCMD_TRUE@@USB_TRUE@am__append_45 = @USB_LIBS@
+@BCCMD_TRUE@am__append_46 = tools/bccmd.8
+@BCCMD_FALSE@am__append_47 = tools/bccmd.8
+@HID2HCI_TRUE@am__append_48 = tools/hid2hci
+@HID2HCI_TRUE@am__append_49 = tools/hid2hci.8
+@HID2HCI_FALSE@am__append_50 = tools/hid2hci.8
+@DFUTOOL_TRUE@am__append_51 = tools/dfutool
+@DFUTOOL_TRUE@am__append_52 = tools/dfutool.1
+@DFUTOOL_FALSE@am__append_53 = tools/dfutool.1
+@USB_TRUE@am__append_54 = tools/dfubabel tools/avctrl
+@CUPS_TRUE@cups_PROGRAMS = cups/bluetooth$(EXEEXT)
+@TEST_TRUE@am__append_55 = test/hciemu
+@TEST_TRUE@am__append_56 = test/l2test test/rctest
+@TEST_TRUE@am__append_57 = test/gaptest test/sdptest test/scotest \
+@TEST_TRUE@ test/attest test/hstest test/avtest test/ipctest \
+@TEST_TRUE@ test/lmptest test/bdaddr test/agent \
+@TEST_TRUE@ test/btiotest test/test-textfile \
+@TEST_TRUE@ test/uuidtest
+
+@TEST_TRUE@am__append_58 = test/rctest.1 test/hciemu.1
+@TEST_TRUE@am__append_59 = test/bdaddr.8
+@TEST_FALSE@am__append_60 = test/rctest.1 test/hciemu.1 test/bdaddr.8
+@HIDD_TRUE@am__append_61 = compat/hidd
+@HIDD_TRUE@am__append_62 = compat/hidd.1
+@HIDD_FALSE@am__append_63 = compat/hidd.1
+@PAND_TRUE@am__append_64 = compat/pand
+@PAND_TRUE@am__append_65 = compat/pand.1
+@PAND_FALSE@am__append_66 = compat/pand.1
+@DUND_TRUE@am__append_67 = compat/dund
+@DUND_TRUE@am__append_68 = compat/dund.1
+@DUND_FALSE@am__append_69 = compat/dund.1
+@HID2HCI_TRUE@@UDEVRULES_TRUE@am__append_70 = scripts/bluetooth-hid2hci.rules
+@PCMCIA_TRUE@@UDEVRULES_TRUE@am__append_71 = scripts/bluetooth-serial.rules
+@MCAP_TRUE@am__append_72 = -I$(builddir)/health
+subdir = .
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
+ configure.lineno config.status.lineno
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = config.h
+CONFIG_CLEAN_FILES = scripts/bluetooth.rules doc/version.xml \
+ src/bluetoothd.8 bluez.pc
+CONFIG_CLEAN_VPATH_FILES =
+LIBRARIES = $(noinst_LIBRARIES)
+ARFLAGS = cru
+AM_V_AR = $(am__v_AR_$(V))
+am__v_AR_ = $(am__v_AR_$(AM_DEFAULT_VERBOSITY))
+am__v_AR_0 = @echo " AR " $@;
+AM_V_at = $(am__v_at_$(V))
+am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY))
+am__v_at_0 = @
+audio_libtelephony_a_AR = $(AR) $(ARFLAGS)
+audio_libtelephony_a_LIBADD =
+am__audio_libtelephony_a_SOURCES_DIST = audio/telephony.h \
+ audio/telephony-dummy.c audio/telephony-maemo5.c \
+ audio/telephony-ofono.c audio/telephony-maemo6.c
+am__dirstamp = $(am__leading_dot)dirstamp
+@AUDIOPLUGIN_TRUE@am_audio_libtelephony_a_OBJECTS = \
+@AUDIOPLUGIN_TRUE@ audio/telephony-dummy.$(OBJEXT) \
+@AUDIOPLUGIN_TRUE@ audio/telephony-maemo5.$(OBJEXT) \
+@AUDIOPLUGIN_TRUE@ audio/telephony-ofono.$(OBJEXT) \
+@AUDIOPLUGIN_TRUE@ audio/telephony-maemo6.$(OBJEXT)
+audio_libtelephony_a_OBJECTS = $(am_audio_libtelephony_a_OBJECTS)
+sap_libsap_a_AR = $(AR) $(ARFLAGS)
+sap_libsap_a_LIBADD =
+am__sap_libsap_a_SOURCES_DIST = sap/sap.h sap/sap-dummy.c
+@SAPPLUGIN_TRUE@am_sap_libsap_a_OBJECTS = sap/sap-dummy.$(OBJEXT)
+sap_libsap_a_OBJECTS = $(am_sap_libsap_a_OBJECTS)
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__installdirs = "$(DESTDIR)$(alsadir)" "$(DESTDIR)$(gstreamerdir)" \
+ "$(DESTDIR)$(libdir)" "$(DESTDIR)$(plugindir)" \
+ "$(DESTDIR)$(bindir)" "$(DESTDIR)$(cupsdir)" \
+ "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(udevdir)" \
+ "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man8dir)" \
+ "$(DESTDIR)$(alsaconfdir)" "$(DESTDIR)$(confdir)" \
+ "$(DESTDIR)$(dbusdir)" "$(DESTDIR)$(pkgconfigdir)" \
+ "$(DESTDIR)$(rulesdir)" "$(DESTDIR)$(statedir)" \
+ "$(DESTDIR)$(includedir)"
+LTLIBRARIES = $(alsa_LTLIBRARIES) $(gstreamer_LTLIBRARIES) \
+ $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES) $(plugin_LTLIBRARIES)
+@ALSA_TRUE@audio_libasound_module_ctl_bluetooth_la_DEPENDENCIES = \
+@ALSA_TRUE@ lib/libbluetooth.la
+am__audio_libasound_module_ctl_bluetooth_la_SOURCES_DIST = \
+ audio/ctl_bluetooth.c audio/rtp.h audio/ipc.h audio/ipc.c
+@ALSA_TRUE@am_audio_libasound_module_ctl_bluetooth_la_OBJECTS = audio/audio_libasound_module_ctl_bluetooth_la-ctl_bluetooth.lo \
+@ALSA_TRUE@ audio/audio_libasound_module_ctl_bluetooth_la-ipc.lo
+audio_libasound_module_ctl_bluetooth_la_OBJECTS = \
+ $(am_audio_libasound_module_ctl_bluetooth_la_OBJECTS)
+AM_V_lt = $(am__v_lt_$(V))
+am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY))
+am__v_lt_0 = --silent
+audio_libasound_module_ctl_bluetooth_la_LINK = $(LIBTOOL) $(AM_V_lt) \
+ --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+ $(CCLD) $(audio_libasound_module_ctl_bluetooth_la_CFLAGS) \
+ $(CFLAGS) $(audio_libasound_module_ctl_bluetooth_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+@ALSA_TRUE@am_audio_libasound_module_ctl_bluetooth_la_rpath = -rpath \
+@ALSA_TRUE@ $(alsadir)
+@ALSA_TRUE@audio_libasound_module_pcm_bluetooth_la_DEPENDENCIES = \
+@ALSA_TRUE@ sbc/libsbc.la lib/libbluetooth.la
+am__audio_libasound_module_pcm_bluetooth_la_SOURCES_DIST = \
+ audio/pcm_bluetooth.c audio/rtp.h audio/ipc.h audio/ipc.c
+@ALSA_TRUE@am_audio_libasound_module_pcm_bluetooth_la_OBJECTS = audio/audio_libasound_module_pcm_bluetooth_la-pcm_bluetooth.lo \
+@ALSA_TRUE@ audio/audio_libasound_module_pcm_bluetooth_la-ipc.lo
+audio_libasound_module_pcm_bluetooth_la_OBJECTS = \
+ $(am_audio_libasound_module_pcm_bluetooth_la_OBJECTS)
+audio_libasound_module_pcm_bluetooth_la_LINK = $(LIBTOOL) $(AM_V_lt) \
+ --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+ $(CCLD) $(audio_libasound_module_pcm_bluetooth_la_CFLAGS) \
+ $(CFLAGS) $(audio_libasound_module_pcm_bluetooth_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+@ALSA_TRUE@am_audio_libasound_module_pcm_bluetooth_la_rpath = -rpath \
+@ALSA_TRUE@ $(alsadir)
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@audio_libgstbluetooth_la_DEPENDENCIES = \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@ sbc/libsbc.la \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@ lib/libbluetooth.la
+am__audio_libgstbluetooth_la_SOURCES_DIST = audio/gstbluetooth.c \
+ audio/gstpragma.h audio/gstsbcenc.h audio/gstsbcenc.c \
+ audio/gstsbcdec.h audio/gstsbcdec.c audio/gstsbcparse.h \
+ audio/gstsbcparse.c audio/gstavdtpsink.h audio/gstavdtpsink.c \
+ audio/gsta2dpsink.h audio/gsta2dpsink.c audio/gstsbcutil.h \
+ audio/gstsbcutil.c audio/gstrtpsbcpay.h audio/gstrtpsbcpay.c \
+ audio/rtp.h audio/ipc.h audio/ipc.c
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@am_audio_libgstbluetooth_la_OBJECTS = audio/audio_libgstbluetooth_la-gstbluetooth.lo \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@ audio/audio_libgstbluetooth_la-gstsbcenc.lo \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@ audio/audio_libgstbluetooth_la-gstsbcdec.lo \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@ audio/audio_libgstbluetooth_la-gstsbcparse.lo \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@ audio/audio_libgstbluetooth_la-gstavdtpsink.lo \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@ audio/audio_libgstbluetooth_la-gsta2dpsink.lo \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@ audio/audio_libgstbluetooth_la-gstsbcutil.lo \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@ audio/audio_libgstbluetooth_la-gstrtpsbcpay.lo \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@ audio/audio_libgstbluetooth_la-ipc.lo
+audio_libgstbluetooth_la_OBJECTS = \
+ $(am_audio_libgstbluetooth_la_OBJECTS)
+audio_libgstbluetooth_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) \
+ $(audio_libgstbluetooth_la_LDFLAGS) $(LDFLAGS) -o $@
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@am_audio_libgstbluetooth_la_rpath = \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@ -rpath $(gstreamerdir)
+lib_libbluetooth_la_LIBADD =
+am__objects_1 =
+am_lib_libbluetooth_la_OBJECTS = $(am__objects_1) lib/bluetooth.lo \
+ lib/hci.lo lib/sdp.lo lib/uuid.lo
+lib_libbluetooth_la_OBJECTS = $(am_lib_libbluetooth_la_OBJECTS)
+lib_libbluetooth_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(lib_libbluetooth_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+sbc_libsbc_la_LIBADD =
+am__sbc_libsbc_la_SOURCES_DIST = sbc/sbc.h sbc/sbc.c sbc/sbc_math.h \
+ sbc/sbc_tables.h sbc/sbc_primitives.h sbc/sbc_primitives.c \
+ sbc/sbc_primitives_mmx.h sbc/sbc_primitives_mmx.c \
+ sbc/sbc_primitives_iwmmxt.h sbc/sbc_primitives_iwmmxt.c \
+ sbc/sbc_primitives_neon.h sbc/sbc_primitives_neon.c \
+ sbc/sbc_primitives_armv6.h sbc/sbc_primitives_armv6.c
+@SBC_TRUE@am_sbc_libsbc_la_OBJECTS = sbc/sbc_libsbc_la-sbc.lo \
+@SBC_TRUE@ sbc/sbc_libsbc_la-sbc_primitives.lo \
+@SBC_TRUE@ sbc/sbc_libsbc_la-sbc_primitives_mmx.lo \
+@SBC_TRUE@ sbc/sbc_libsbc_la-sbc_primitives_iwmmxt.lo \
+@SBC_TRUE@ sbc/sbc_libsbc_la-sbc_primitives_neon.lo \
+@SBC_TRUE@ sbc/sbc_libsbc_la-sbc_primitives_armv6.lo
+sbc_libsbc_la_OBJECTS = $(am_sbc_libsbc_la_OBJECTS)
+sbc_libsbc_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(sbc_libsbc_la_CFLAGS) \
+ $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+@SBC_TRUE@am_sbc_libsbc_la_rpath =
+@ATTRIBPLUGIN_TRUE@@READLINE_TRUE@am__EXEEXT_1 = \
+@ATTRIBPLUGIN_TRUE@@READLINE_TRUE@ attrib/gatttool$(EXEEXT)
+@TOOLS_TRUE@am__EXEEXT_2 = tools/rfcomm$(EXEEXT) tools/l2ping$(EXEEXT) \
+@TOOLS_TRUE@ tools/hcitool$(EXEEXT) tools/sdptool$(EXEEXT) \
+@TOOLS_TRUE@ tools/ciptool$(EXEEXT)
+@DFUTOOL_TRUE@am__EXEEXT_3 = tools/dfutool$(EXEEXT)
+@TEST_TRUE@am__EXEEXT_4 = test/l2test$(EXEEXT) test/rctest$(EXEEXT)
+@HIDD_TRUE@am__EXEEXT_5 = compat/hidd$(EXEEXT)
+@PAND_TRUE@am__EXEEXT_6 = compat/pand$(EXEEXT)
+@DUND_TRUE@am__EXEEXT_7 = compat/dund$(EXEEXT)
+@SBC_TRUE@am__EXEEXT_8 = sbc/sbcinfo$(EXEEXT) sbc/sbcdec$(EXEEXT) \
+@SBC_TRUE@ sbc/sbcenc$(EXEEXT)
+@SBC_TRUE@@SNDFILE_TRUE@am__EXEEXT_9 = sbc/sbctester$(EXEEXT)
+@TOOLS_TRUE@am__EXEEXT_10 = tools/avinfo$(EXEEXT) \
+@TOOLS_TRUE@ tools/ppporc$(EXEEXT) tools/hcieventmask$(EXEEXT) \
+@TOOLS_TRUE@ tools/hcisecfilter$(EXEEXT)
+@USB_TRUE@am__EXEEXT_11 = tools/dfubabel$(EXEEXT) \
+@USB_TRUE@ tools/avctrl$(EXEEXT)
+@TEST_TRUE@am__EXEEXT_12 = test/gaptest$(EXEEXT) test/sdptest$(EXEEXT) \
+@TEST_TRUE@ test/scotest$(EXEEXT) test/attest$(EXEEXT) \
+@TEST_TRUE@ test/hstest$(EXEEXT) test/avtest$(EXEEXT) \
+@TEST_TRUE@ test/ipctest$(EXEEXT) test/lmptest$(EXEEXT) \
+@TEST_TRUE@ test/bdaddr$(EXEEXT) test/agent$(EXEEXT) \
+@TEST_TRUE@ test/btiotest$(EXEEXT) test/test-textfile$(EXEEXT) \
+@TEST_TRUE@ test/uuidtest$(EXEEXT)
+@TOOLS_TRUE@am__EXEEXT_13 = tools/hciattach$(EXEEXT) \
+@TOOLS_TRUE@ tools/hciconfig$(EXEEXT)
+@TRACER_TRUE@am__EXEEXT_14 = tracer/hcitrace$(EXEEXT)
+@BCCMD_TRUE@am__EXEEXT_15 = tools/bccmd$(EXEEXT)
+@HID2HCI_TRUE@am__EXEEXT_16 = tools/hid2hci$(EXEEXT)
+@TEST_TRUE@am__EXEEXT_17 = test/hciemu$(EXEEXT)
+PROGRAMS = $(bin_PROGRAMS) $(cups_PROGRAMS) $(noinst_PROGRAMS) \
+ $(sbin_PROGRAMS)
+am__attrib_gatttool_SOURCES_DIST = attrib/gatttool.c attrib/att.c \
+ attrib/gatt.c attrib/gattrib.c btio/btio.c src/glib-helper.h \
+ src/glib-helper.c attrib/gatttool.h attrib/interactive.c \
+ attrib/utils.c
+@ATTRIBPLUGIN_TRUE@@READLINE_TRUE@am_attrib_gatttool_OBJECTS = \
+@ATTRIBPLUGIN_TRUE@@READLINE_TRUE@ attrib/gatttool.$(OBJEXT) \
+@ATTRIBPLUGIN_TRUE@@READLINE_TRUE@ attrib/att.$(OBJEXT) \
+@ATTRIBPLUGIN_TRUE@@READLINE_TRUE@ attrib/gatt.$(OBJEXT) \
+@ATTRIBPLUGIN_TRUE@@READLINE_TRUE@ attrib/gattrib.$(OBJEXT) \
+@ATTRIBPLUGIN_TRUE@@READLINE_TRUE@ btio/btio.$(OBJEXT) \
+@ATTRIBPLUGIN_TRUE@@READLINE_TRUE@ src/glib-helper.$(OBJEXT) \
+@ATTRIBPLUGIN_TRUE@@READLINE_TRUE@ attrib/interactive.$(OBJEXT) \
+@ATTRIBPLUGIN_TRUE@@READLINE_TRUE@ attrib/utils.$(OBJEXT)
+attrib_gatttool_OBJECTS = $(am_attrib_gatttool_OBJECTS)
+@ATTRIBPLUGIN_TRUE@@READLINE_TRUE@attrib_gatttool_DEPENDENCIES = \
+@ATTRIBPLUGIN_TRUE@@READLINE_TRUE@ lib/libbluetooth.la
+am__compat_dund_SOURCES_DIST = compat/dund.c compat/dund.h \
+ compat/lib.h compat/sdp.h compat/sdp.c compat/dun.c \
+ compat/msdun.c src/textfile.h src/textfile.c
+@DUND_TRUE@am_compat_dund_OBJECTS = compat/dund.$(OBJEXT) \
+@DUND_TRUE@ compat/sdp.$(OBJEXT) compat/dun.$(OBJEXT) \
+@DUND_TRUE@ compat/msdun.$(OBJEXT) src/textfile.$(OBJEXT)
+compat_dund_OBJECTS = $(am_compat_dund_OBJECTS)
+@DUND_TRUE@compat_dund_DEPENDENCIES = lib/libbluetooth.la
+am__compat_hidd_SOURCES_DIST = compat/hidd.c compat/hidd.h \
+ src/uinput.h compat/sdp.h compat/sdp.c compat/fakehid.c \
+ src/textfile.h src/textfile.c
+@HIDD_TRUE@am_compat_hidd_OBJECTS = compat/hidd.$(OBJEXT) \
+@HIDD_TRUE@ compat/sdp.$(OBJEXT) compat/fakehid.$(OBJEXT) \
+@HIDD_TRUE@ src/textfile.$(OBJEXT)
+compat_hidd_OBJECTS = $(am_compat_hidd_OBJECTS)
+@HIDD_TRUE@compat_hidd_DEPENDENCIES = lib/libbluetooth.la
+am__compat_pand_SOURCES_DIST = compat/pand.c compat/pand.h \
+ compat/bnep.c compat/sdp.h compat/sdp.c src/textfile.h \
+ src/textfile.c
+@PAND_TRUE@am_compat_pand_OBJECTS = compat/pand.$(OBJEXT) \
+@PAND_TRUE@ compat/bnep.$(OBJEXT) compat/sdp.$(OBJEXT) \
+@PAND_TRUE@ src/textfile.$(OBJEXT)
+compat_pand_OBJECTS = $(am_compat_pand_OBJECTS)
+@PAND_TRUE@compat_pand_DEPENDENCIES = lib/libbluetooth.la
+am__cups_bluetooth_SOURCES_DIST = gdbus/gdbus.h gdbus/mainloop.c \
+ gdbus/watch.c gdbus/object.c gdbus/polkit.c cups/main.c \
+ cups/cups.h cups/sdp.c cups/spp.c cups/hcrp.c
+am__objects_2 = gdbus/mainloop.$(OBJEXT) gdbus/watch.$(OBJEXT) \
+ gdbus/object.$(OBJEXT) gdbus/polkit.$(OBJEXT)
+@CUPS_TRUE@am_cups_bluetooth_OBJECTS = $(am__objects_2) \
+@CUPS_TRUE@ cups/main.$(OBJEXT) cups/sdp.$(OBJEXT) \
+@CUPS_TRUE@ cups/spp.$(OBJEXT) cups/hcrp.$(OBJEXT)
+cups_bluetooth_OBJECTS = $(am_cups_bluetooth_OBJECTS)
+@CUPS_TRUE@cups_bluetooth_DEPENDENCIES = lib/libbluetooth.la
+am__sbc_sbcdec_SOURCES_DIST = sbc/sbcdec.c sbc/formats.h
+@SBC_TRUE@am_sbc_sbcdec_OBJECTS = sbc/sbcdec.$(OBJEXT)
+sbc_sbcdec_OBJECTS = $(am_sbc_sbcdec_OBJECTS)
+@SBC_TRUE@sbc_sbcdec_DEPENDENCIES = sbc/libsbc.la
+am__sbc_sbcenc_SOURCES_DIST = sbc/sbcenc.c sbc/formats.h
+@SBC_TRUE@am_sbc_sbcenc_OBJECTS = sbc/sbcenc.$(OBJEXT)
+sbc_sbcenc_OBJECTS = $(am_sbc_sbcenc_OBJECTS)
+@SBC_TRUE@sbc_sbcenc_DEPENDENCIES = sbc/libsbc.la
+sbc_sbcinfo_SOURCES = sbc/sbcinfo.c
+sbc_sbcinfo_OBJECTS = sbc/sbcinfo.$(OBJEXT)
+sbc_sbcinfo_LDADD = $(LDADD)
+sbc_sbctester_SOURCES = sbc/sbctester.c
+sbc_sbctester_OBJECTS = sbc/sbctester.$(OBJEXT)
+sbc_sbctester_DEPENDENCIES =
+am__src_bluetoothd_SOURCES_DIST = gdbus/gdbus.h gdbus/mainloop.c \
+ gdbus/watch.c gdbus/object.c gdbus/polkit.c plugins/pnat.c \
+ plugins/echo.c audio/main.c audio/manager.h audio/manager.c \
+ audio/gateway.h audio/gateway.c audio/headset.h \
+ audio/headset.c audio/control.h audio/control.c audio/device.h \
+ audio/device.c audio/source.h audio/source.c audio/sink.h \
+ audio/sink.c audio/a2dp.h audio/a2dp.c audio/avdtp.h \
+ audio/avdtp.c audio/ipc.h audio/ipc.c audio/unix.h \
+ audio/unix.c audio/media.h audio/media.c audio/transport.h \
+ audio/transport.c audio/telephony.h audio/a2dp-codecs.h \
+ sap/main.c sap/manager.h sap/manager.c sap/server.h \
+ sap/server.c sap/sap.h input/main.c input/manager.h \
+ input/manager.c input/server.h input/server.c input/device.h \
+ input/device.c input/fakehid.c input/fakehid.h serial/main.c \
+ serial/manager.h serial/manager.c serial/proxy.h \
+ serial/proxy.c serial/port.h serial/port.c network/main.c \
+ network/manager.h network/manager.c network/common.h \
+ network/common.c network/server.h network/server.c \
+ network/connection.h network/connection.c plugins/service.c \
+ attrib/main.c attrib/manager.h attrib/manager.c \
+ attrib/client.h attrib/client.c attrib/example.h \
+ attrib/example.c health/hdp_main.c health/hdp_types.h \
+ health/hdp_manager.h health/hdp_manager.c health/hdp.h \
+ health/hdp.c health/hdp_util.h health/hdp_util.c \
+ plugins/hciops.c plugins/mgmtops.c plugins/hal.c \
+ plugins/formfactor.c plugins/storage.c plugins/maemo6.c \
+ plugins/dbusoob.c attrib/att.h attrib/att.c attrib/gatt.h \
+ attrib/gatt.c attrib/gattrib.h attrib/gattrib.c btio/btio.h \
+ btio/btio.c health/mcap_lib.h health/mcap_internal.h \
+ health/mcap.h health/mcap.c health/mcap_sync.c \
+ src/bluetooth.ver src/main.c src/log.h src/log.c src/rfkill.c \
+ src/hcid.h src/sdpd.h src/sdpd-server.c src/sdpd-request.c \
+ src/sdpd-service.c src/sdpd-database.c src/attrib-server.h \
+ src/attrib-server.c src/sdp-xml.h src/sdp-xml.c src/textfile.h \
+ src/textfile.c src/glib-helper.h src/glib-helper.c src/oui.h \
+ src/oui.c src/uinput.h src/ppoll.h src/plugin.h src/plugin.c \
+ src/storage.h src/storage.c src/agent.h src/agent.c \
+ src/error.h src/error.c src/manager.h src/manager.c \
+ src/adapter.h src/adapter.c src/device.h src/device.c \
+ src/dbus-common.c src/dbus-common.h src/event.h src/event.c \
+ src/oob.h src/oob.c
+@PNATPLUGIN_TRUE@am__objects_3 = plugins/pnat.$(OBJEXT)
+@ECHOPLUGIN_TRUE@am__objects_4 = plugins/echo.$(OBJEXT)
+@AUDIOPLUGIN_TRUE@am__objects_5 = audio/main.$(OBJEXT) \
+@AUDIOPLUGIN_TRUE@ audio/manager.$(OBJEXT) \
+@AUDIOPLUGIN_TRUE@ audio/gateway.$(OBJEXT) \
+@AUDIOPLUGIN_TRUE@ audio/headset.$(OBJEXT) \
+@AUDIOPLUGIN_TRUE@ audio/control.$(OBJEXT) \
+@AUDIOPLUGIN_TRUE@ audio/device.$(OBJEXT) \
+@AUDIOPLUGIN_TRUE@ audio/source.$(OBJEXT) audio/sink.$(OBJEXT) \
+@AUDIOPLUGIN_TRUE@ audio/a2dp.$(OBJEXT) audio/avdtp.$(OBJEXT) \
+@AUDIOPLUGIN_TRUE@ audio/ipc.$(OBJEXT) audio/unix.$(OBJEXT) \
+@AUDIOPLUGIN_TRUE@ audio/media.$(OBJEXT) \
+@AUDIOPLUGIN_TRUE@ audio/transport.$(OBJEXT)
+@SAPPLUGIN_TRUE@am__objects_6 = sap/main.$(OBJEXT) \
+@SAPPLUGIN_TRUE@ sap/manager.$(OBJEXT) sap/server.$(OBJEXT)
+@INPUTPLUGIN_TRUE@am__objects_7 = input/main.$(OBJEXT) \
+@INPUTPLUGIN_TRUE@ input/manager.$(OBJEXT) \
+@INPUTPLUGIN_TRUE@ input/server.$(OBJEXT) \
+@INPUTPLUGIN_TRUE@ input/device.$(OBJEXT) \
+@INPUTPLUGIN_TRUE@ input/fakehid.$(OBJEXT)
+@SERIALPLUGIN_TRUE@am__objects_8 = serial/main.$(OBJEXT) \
+@SERIALPLUGIN_TRUE@ serial/manager.$(OBJEXT) \
+@SERIALPLUGIN_TRUE@ serial/proxy.$(OBJEXT) \
+@SERIALPLUGIN_TRUE@ serial/port.$(OBJEXT)
+@NETWORKPLUGIN_TRUE@am__objects_9 = network/main.$(OBJEXT) \
+@NETWORKPLUGIN_TRUE@ network/manager.$(OBJEXT) \
+@NETWORKPLUGIN_TRUE@ network/common.$(OBJEXT) \
+@NETWORKPLUGIN_TRUE@ network/server.$(OBJEXT) \
+@NETWORKPLUGIN_TRUE@ network/connection.$(OBJEXT)
+@SERVICEPLUGIN_TRUE@am__objects_10 = plugins/service.$(OBJEXT)
+@ATTRIBPLUGIN_TRUE@am__objects_11 = attrib/main.$(OBJEXT) \
+@ATTRIBPLUGIN_TRUE@ attrib/manager.$(OBJEXT) \
+@ATTRIBPLUGIN_TRUE@ attrib/client.$(OBJEXT) \
+@ATTRIBPLUGIN_TRUE@ attrib/example.$(OBJEXT)
+@HEALTHPLUGIN_TRUE@am__objects_12 = health/hdp_main.$(OBJEXT) \
+@HEALTHPLUGIN_TRUE@ health/hdp_manager.$(OBJEXT) \
+@HEALTHPLUGIN_TRUE@ health/hdp.$(OBJEXT) \
+@HEALTHPLUGIN_TRUE@ health/hdp_util.$(OBJEXT)
+@HAL_TRUE@am__objects_13 = plugins/hal.$(OBJEXT)
+@HAL_FALSE@am__objects_14 = plugins/formfactor.$(OBJEXT)
+@MAEMO6PLUGIN_TRUE@am__objects_15 = plugins/maemo6.$(OBJEXT)
+@DBUSOOBPLUGIN_TRUE@am__objects_16 = plugins/dbusoob.$(OBJEXT)
+am__objects_17 = $(am__objects_3) $(am__objects_4) $(am__objects_5) \
+ $(am__objects_6) $(am__objects_7) $(am__objects_8) \
+ $(am__objects_9) $(am__objects_10) $(am__objects_11) \
+ $(am__objects_12) plugins/hciops.$(OBJEXT) \
+ plugins/mgmtops.$(OBJEXT) $(am__objects_13) $(am__objects_14) \
+ plugins/storage.$(OBJEXT) $(am__objects_15) $(am__objects_16)
+am__objects_18 = attrib/att.$(OBJEXT) attrib/gatt.$(OBJEXT) \
+ attrib/gattrib.$(OBJEXT)
+am__objects_19 = btio/btio.$(OBJEXT)
+@MCAP_TRUE@am__objects_20 = health/mcap.$(OBJEXT) \
+@MCAP_TRUE@ health/mcap_sync.$(OBJEXT)
+am__objects_21 = $(am__objects_20)
+am_src_bluetoothd_OBJECTS = $(am__objects_2) $(am__objects_17) \
+ $(am__objects_18) $(am__objects_19) $(am__objects_21) \
+ src/main.$(OBJEXT) src/log.$(OBJEXT) src/rfkill.$(OBJEXT) \
+ src/sdpd-server.$(OBJEXT) src/sdpd-request.$(OBJEXT) \
+ src/sdpd-service.$(OBJEXT) src/sdpd-database.$(OBJEXT) \
+ src/attrib-server.$(OBJEXT) src/sdp-xml.$(OBJEXT) \
+ src/textfile.$(OBJEXT) src/glib-helper.$(OBJEXT) \
+ src/oui.$(OBJEXT) src/plugin.$(OBJEXT) src/storage.$(OBJEXT) \
+ src/agent.$(OBJEXT) src/error.$(OBJEXT) src/manager.$(OBJEXT) \
+ src/adapter.$(OBJEXT) src/device.$(OBJEXT) \
+ src/dbus-common.$(OBJEXT) src/event.$(OBJEXT) \
+ src/oob.$(OBJEXT)
+@AUDIOPLUGIN_TRUE@am__objects_22 = audio/telephony.$(OBJEXT)
+@SAPPLUGIN_TRUE@am__objects_23 = sap/sap.$(OBJEXT)
+am__objects_24 = $(am__objects_22) $(am__objects_23)
+am__objects_25 = $(am__objects_24)
+nodist_src_bluetoothd_OBJECTS = $(am__objects_25)
+src_bluetoothd_OBJECTS = $(am_src_bluetoothd_OBJECTS) \
+ $(nodist_src_bluetoothd_OBJECTS)
+src_bluetoothd_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(src_bluetoothd_LDFLAGS) $(LDFLAGS) -o \
+ $@
+test_agent_SOURCES = test/agent.c
+test_agent_OBJECTS = test/agent.$(OBJEXT)
+test_agent_DEPENDENCIES =
+test_attest_SOURCES = test/attest.c
+test_attest_OBJECTS = test/attest.$(OBJEXT)
+@TEST_TRUE@test_attest_DEPENDENCIES = lib/libbluetooth.la
+test_avtest_SOURCES = test/avtest.c
+test_avtest_OBJECTS = test/avtest.$(OBJEXT)
+@TEST_TRUE@test_avtest_DEPENDENCIES = lib/libbluetooth.la
+am__test_bdaddr_SOURCES_DIST = test/bdaddr.c src/oui.h src/oui.c
+@TEST_TRUE@am_test_bdaddr_OBJECTS = test/bdaddr.$(OBJEXT) \
+@TEST_TRUE@ src/oui.$(OBJEXT)
+test_bdaddr_OBJECTS = $(am_test_bdaddr_OBJECTS)
+@TEST_TRUE@test_bdaddr_DEPENDENCIES = lib/libbluetooth.la
+am__test_btiotest_SOURCES_DIST = test/btiotest.c btio/btio.h \
+ btio/btio.c
+@TEST_TRUE@am_test_btiotest_OBJECTS = test/btiotest.$(OBJEXT) \
+@TEST_TRUE@ btio/btio.$(OBJEXT)
+test_btiotest_OBJECTS = $(am_test_btiotest_OBJECTS)
+@TEST_TRUE@test_btiotest_DEPENDENCIES = lib/libbluetooth.la
+test_gaptest_SOURCES = test/gaptest.c
+test_gaptest_OBJECTS = test/gaptest.$(OBJEXT)
+test_gaptest_DEPENDENCIES =
+test_hciemu_SOURCES = test/hciemu.c
+test_hciemu_OBJECTS = test/hciemu.$(OBJEXT)
+@TEST_TRUE@test_hciemu_DEPENDENCIES = lib/libbluetooth.la
+test_hstest_SOURCES = test/hstest.c
+test_hstest_OBJECTS = test/hstest.$(OBJEXT)
+@TEST_TRUE@test_hstest_DEPENDENCIES = lib/libbluetooth.la
+am__test_ipctest_SOURCES_DIST = test/ipctest.c audio/ipc.h audio/ipc.c
+@TEST_TRUE@am_test_ipctest_OBJECTS = test/ipctest.$(OBJEXT) \
+@TEST_TRUE@ audio/ipc.$(OBJEXT)
+test_ipctest_OBJECTS = $(am_test_ipctest_OBJECTS)
+@TEST_TRUE@test_ipctest_DEPENDENCIES = sbc/libsbc.la
+test_l2test_SOURCES = test/l2test.c
+test_l2test_OBJECTS = test/l2test.$(OBJEXT)
+@TEST_TRUE@test_l2test_DEPENDENCIES = lib/libbluetooth.la
+test_lmptest_SOURCES = test/lmptest.c
+test_lmptest_OBJECTS = test/lmptest.$(OBJEXT)
+@TEST_TRUE@test_lmptest_DEPENDENCIES = lib/libbluetooth.la
+test_rctest_SOURCES = test/rctest.c
+test_rctest_OBJECTS = test/rctest.$(OBJEXT)
+@TEST_TRUE@test_rctest_DEPENDENCIES = lib/libbluetooth.la
+test_scotest_SOURCES = test/scotest.c
+test_scotest_OBJECTS = test/scotest.$(OBJEXT)
+@TEST_TRUE@test_scotest_DEPENDENCIES = lib/libbluetooth.la
+test_sdptest_SOURCES = test/sdptest.c
+test_sdptest_OBJECTS = test/sdptest.$(OBJEXT)
+@TEST_TRUE@test_sdptest_DEPENDENCIES = lib/libbluetooth.la
+am__test_test_textfile_SOURCES_DIST = test/test-textfile.c \
+ src/textfile.h src/textfile.c
+@TEST_TRUE@am_test_test_textfile_OBJECTS = \
+@TEST_TRUE@ test/test-textfile.$(OBJEXT) src/textfile.$(OBJEXT)
+test_test_textfile_OBJECTS = $(am_test_test_textfile_OBJECTS)
+test_test_textfile_LDADD = $(LDADD)
+am__test_uuidtest_SOURCES_DIST = test/uuidtest.c
+@TEST_TRUE@am_test_uuidtest_OBJECTS = test/uuidtest.$(OBJEXT)
+test_uuidtest_OBJECTS = $(am_test_uuidtest_OBJECTS)
+@TEST_TRUE@test_uuidtest_DEPENDENCIES = lib/libbluetooth.la
+tools_avctrl_SOURCES = tools/avctrl.c
+tools_avctrl_OBJECTS = tools/avctrl.$(OBJEXT)
+tools_avctrl_DEPENDENCIES =
+tools_avinfo_SOURCES = tools/avinfo.c
+tools_avinfo_OBJECTS = tools/avinfo.$(OBJEXT)
+@TOOLS_TRUE@tools_avinfo_DEPENDENCIES = lib/libbluetooth.la
+am__tools_bccmd_SOURCES_DIST = tools/bccmd.c tools/csr.h tools/csr.c \
+ tools/csr_hci.c tools/csr_h4.c tools/csr_3wire.c \
+ tools/csr_bcsp.c tools/ubcsp.h tools/ubcsp.c tools/csr_usb.c
+@BCCMD_TRUE@@USB_TRUE@am__objects_26 = tools/csr_usb.$(OBJEXT)
+@BCCMD_TRUE@am_tools_bccmd_OBJECTS = tools/bccmd.$(OBJEXT) \
+@BCCMD_TRUE@ tools/csr.$(OBJEXT) tools/csr_hci.$(OBJEXT) \
+@BCCMD_TRUE@ tools/csr_h4.$(OBJEXT) tools/csr_3wire.$(OBJEXT) \
+@BCCMD_TRUE@ tools/csr_bcsp.$(OBJEXT) tools/ubcsp.$(OBJEXT) \
+@BCCMD_TRUE@ $(am__objects_26)
+tools_bccmd_OBJECTS = $(am_tools_bccmd_OBJECTS)
+am__DEPENDENCIES_1 =
+@BCCMD_TRUE@tools_bccmd_DEPENDENCIES = lib/libbluetooth.la \
+@BCCMD_TRUE@ $(am__DEPENDENCIES_1)
+tools_ciptool_SOURCES = tools/ciptool.c
+tools_ciptool_OBJECTS = tools/ciptool.$(OBJEXT)
+@TOOLS_TRUE@tools_ciptool_DEPENDENCIES = lib/libbluetooth.la
+tools_dfubabel_SOURCES = tools/dfubabel.c
+tools_dfubabel_OBJECTS = tools/dfubabel.$(OBJEXT)
+tools_dfubabel_DEPENDENCIES =
+am__tools_dfutool_SOURCES_DIST = tools/dfutool.c tools/dfu.h \
+ tools/dfu.c
+@DFUTOOL_TRUE@am_tools_dfutool_OBJECTS = tools/dfutool.$(OBJEXT) \
+@DFUTOOL_TRUE@ tools/dfu.$(OBJEXT)
+tools_dfutool_OBJECTS = $(am_tools_dfutool_OBJECTS)
+tools_dfutool_DEPENDENCIES =
+am__tools_hciattach_SOURCES_DIST = tools/hciattach.c tools/hciattach.h \
+ tools/hciattach_st.c tools/hciattach_ti.c \
+ tools/hciattach_tialt.c tools/hciattach_ath3k.c \
+ tools/hciattach_qualcomm.c
+@TOOLS_TRUE@am_tools_hciattach_OBJECTS = tools/hciattach.$(OBJEXT) \
+@TOOLS_TRUE@ tools/hciattach_st.$(OBJEXT) \
+@TOOLS_TRUE@ tools/hciattach_ti.$(OBJEXT) \
+@TOOLS_TRUE@ tools/hciattach_tialt.$(OBJEXT) \
+@TOOLS_TRUE@ tools/hciattach_ath3k.$(OBJEXT) \
+@TOOLS_TRUE@ tools/hciattach_qualcomm.$(OBJEXT)
+tools_hciattach_OBJECTS = $(am_tools_hciattach_OBJECTS)
+@TOOLS_TRUE@tools_hciattach_DEPENDENCIES = lib/libbluetooth.la
+am__tools_hciconfig_SOURCES_DIST = tools/hciconfig.c tools/csr.h \
+ tools/csr.c src/textfile.h src/textfile.c
+@TOOLS_TRUE@am_tools_hciconfig_OBJECTS = tools/hciconfig.$(OBJEXT) \
+@TOOLS_TRUE@ tools/csr.$(OBJEXT) src/textfile.$(OBJEXT)
+tools_hciconfig_OBJECTS = $(am_tools_hciconfig_OBJECTS)
+@TOOLS_TRUE@tools_hciconfig_DEPENDENCIES = lib/libbluetooth.la
+tools_hcieventmask_SOURCES = tools/hcieventmask.c
+tools_hcieventmask_OBJECTS = tools/hcieventmask.$(OBJEXT)
+@TOOLS_TRUE@tools_hcieventmask_DEPENDENCIES = lib/libbluetooth.la
+tools_hcisecfilter_SOURCES = tools/hcisecfilter.c
+tools_hcisecfilter_OBJECTS = tools/hcisecfilter.$(OBJEXT)
+tools_hcisecfilter_LDADD = $(LDADD)
+am__tools_hcitool_SOURCES_DIST = tools/hcitool.c src/oui.h src/oui.c \
+ src/textfile.h src/textfile.c
+@TOOLS_TRUE@am_tools_hcitool_OBJECTS = tools/hcitool.$(OBJEXT) \
+@TOOLS_TRUE@ src/oui.$(OBJEXT) src/textfile.$(OBJEXT)
+tools_hcitool_OBJECTS = $(am_tools_hcitool_OBJECTS)
+@TOOLS_TRUE@tools_hcitool_DEPENDENCIES = lib/libbluetooth.la
+tools_hid2hci_SOURCES = tools/hid2hci.c
+tools_hid2hci_OBJECTS = tools/hid2hci.$(OBJEXT)
+tools_hid2hci_DEPENDENCIES =
+tools_l2ping_SOURCES = tools/l2ping.c
+tools_l2ping_OBJECTS = tools/l2ping.$(OBJEXT)
+@TOOLS_TRUE@tools_l2ping_DEPENDENCIES = lib/libbluetooth.la
+tools_ppporc_SOURCES = tools/ppporc.c
+tools_ppporc_OBJECTS = tools/ppporc.$(OBJEXT)
+@TOOLS_TRUE@tools_ppporc_DEPENDENCIES = lib/libbluetooth.la
+am__tools_rfcomm_SOURCES_DIST = tools/rfcomm.c tools/parser.y \
+ tools/lexer.l tools/kword.h tools/kword.c
+@TOOLS_TRUE@am_tools_rfcomm_OBJECTS = tools/rfcomm.$(OBJEXT) \
+@TOOLS_TRUE@ tools/parser.$(OBJEXT) tools/lexer.$(OBJEXT) \
+@TOOLS_TRUE@ tools/kword.$(OBJEXT)
+am__EXTRA_tools_rfcomm_SOURCES_DIST = tools/parser.h tools/parser.c \
+ tools/lexer.c
+tools_rfcomm_OBJECTS = $(am_tools_rfcomm_OBJECTS)
+@TOOLS_TRUE@tools_rfcomm_DEPENDENCIES = lib/libbluetooth.la
+am__tools_sdptool_SOURCES_DIST = tools/sdptool.c src/sdp-xml.h \
+ src/sdp-xml.c
+@TOOLS_TRUE@am_tools_sdptool_OBJECTS = tools/sdptool.$(OBJEXT) \
+@TOOLS_TRUE@ src/sdp-xml.$(OBJEXT)
+tools_sdptool_OBJECTS = $(am_tools_sdptool_OBJECTS)
+@TOOLS_TRUE@tools_sdptool_DEPENDENCIES = lib/libbluetooth.la
+am__tracer_hcitrace_SOURCES_DIST = tracer/main.c
+@TRACER_TRUE@am_tracer_hcitrace_OBJECTS = tracer/main.$(OBJEXT)
+tracer_hcitrace_OBJECTS = $(am_tracer_hcitrace_OBJECTS)
+am__dist_udev_SCRIPTS_DIST = scripts/bluetooth_serial
+SCRIPTS = $(dist_udev_SCRIPTS)
+DEFAULT_INCLUDES = -I.@am__isrc@
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_$(V))
+am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY))
+am__v_CC_0 = @echo " CC " $@;
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_$(V))
+am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY))
+am__v_CCLD_0 = @echo " CCLD " $@;
+@MAINTAINER_MODE_FALSE@am__skiplex = test -f $@ ||
+LEXCOMPILE = $(LEX) $(LFLAGS) $(AM_LFLAGS)
+LTLEXCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(LEX) $(LFLAGS) $(AM_LFLAGS)
+AM_V_LEX = $(am__v_LEX_$(V))
+am__v_LEX_ = $(am__v_LEX_$(AM_DEFAULT_VERBOSITY))
+am__v_LEX_0 = @echo " LEX " $@;
+YLWRAP = $(top_srcdir)/ylwrap
+@MAINTAINER_MODE_FALSE@am__skipyacc = test -f $@ ||
+YACCCOMPILE = $(YACC) $(YFLAGS) $(AM_YFLAGS)
+LTYACCCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(YACC) $(YFLAGS) $(AM_YFLAGS)
+AM_V_YACC = $(am__v_YACC_$(V))
+am__v_YACC_ = $(am__v_YACC_$(AM_DEFAULT_VERBOSITY))
+am__v_YACC_0 = @echo " YACC " $@;
+AM_V_GEN = $(am__v_GEN_$(V))
+am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
+am__v_GEN_0 = @echo " GEN " $@;
+SOURCES = $(audio_libtelephony_a_SOURCES) $(sap_libsap_a_SOURCES) \
+ $(audio_libasound_module_ctl_bluetooth_la_SOURCES) \
+ $(audio_libasound_module_pcm_bluetooth_la_SOURCES) \
+ $(audio_libgstbluetooth_la_SOURCES) \
+ $(lib_libbluetooth_la_SOURCES) $(sbc_libsbc_la_SOURCES) \
+ $(attrib_gatttool_SOURCES) $(compat_dund_SOURCES) \
+ $(compat_hidd_SOURCES) $(compat_pand_SOURCES) \
+ $(cups_bluetooth_SOURCES) $(sbc_sbcdec_SOURCES) \
+ $(sbc_sbcenc_SOURCES) sbc/sbcinfo.c sbc/sbctester.c \
+ $(src_bluetoothd_SOURCES) $(nodist_src_bluetoothd_SOURCES) \
+ test/agent.c test/attest.c test/avtest.c \
+ $(test_bdaddr_SOURCES) $(test_btiotest_SOURCES) test/gaptest.c \
+ test/hciemu.c test/hstest.c $(test_ipctest_SOURCES) \
+ test/l2test.c test/lmptest.c test/rctest.c test/scotest.c \
+ test/sdptest.c $(test_test_textfile_SOURCES) \
+ $(test_uuidtest_SOURCES) tools/avctrl.c tools/avinfo.c \
+ $(tools_bccmd_SOURCES) tools/ciptool.c tools/dfubabel.c \
+ $(tools_dfutool_SOURCES) $(tools_hciattach_SOURCES) \
+ $(tools_hciconfig_SOURCES) tools/hcieventmask.c \
+ tools/hcisecfilter.c $(tools_hcitool_SOURCES) tools/hid2hci.c \
+ tools/l2ping.c tools/ppporc.c $(tools_rfcomm_SOURCES) \
+ $(EXTRA_tools_rfcomm_SOURCES) $(tools_sdptool_SOURCES) \
+ $(tracer_hcitrace_SOURCES)
+DIST_SOURCES = $(am__audio_libtelephony_a_SOURCES_DIST) \
+ $(am__sap_libsap_a_SOURCES_DIST) \
+ $(am__audio_libasound_module_ctl_bluetooth_la_SOURCES_DIST) \
+ $(am__audio_libasound_module_pcm_bluetooth_la_SOURCES_DIST) \
+ $(am__audio_libgstbluetooth_la_SOURCES_DIST) \
+ $(lib_libbluetooth_la_SOURCES) \
+ $(am__sbc_libsbc_la_SOURCES_DIST) \
+ $(am__attrib_gatttool_SOURCES_DIST) \
+ $(am__compat_dund_SOURCES_DIST) \
+ $(am__compat_hidd_SOURCES_DIST) \
+ $(am__compat_pand_SOURCES_DIST) \
+ $(am__cups_bluetooth_SOURCES_DIST) \
+ $(am__sbc_sbcdec_SOURCES_DIST) $(am__sbc_sbcenc_SOURCES_DIST) \
+ sbc/sbcinfo.c sbc/sbctester.c \
+ $(am__src_bluetoothd_SOURCES_DIST) test/agent.c test/attest.c \
+ test/avtest.c $(am__test_bdaddr_SOURCES_DIST) \
+ $(am__test_btiotest_SOURCES_DIST) test/gaptest.c test/hciemu.c \
+ test/hstest.c $(am__test_ipctest_SOURCES_DIST) test/l2test.c \
+ test/lmptest.c test/rctest.c test/scotest.c test/sdptest.c \
+ $(am__test_test_textfile_SOURCES_DIST) \
+ $(am__test_uuidtest_SOURCES_DIST) tools/avctrl.c \
+ tools/avinfo.c $(am__tools_bccmd_SOURCES_DIST) tools/ciptool.c \
+ tools/dfubabel.c $(am__tools_dfutool_SOURCES_DIST) \
+ $(am__tools_hciattach_SOURCES_DIST) \
+ $(am__tools_hciconfig_SOURCES_DIST) tools/hcieventmask.c \
+ tools/hcisecfilter.c $(am__tools_hcitool_SOURCES_DIST) \
+ tools/hid2hci.c tools/l2ping.c tools/ppporc.c \
+ $(am__tools_rfcomm_SOURCES_DIST) \
+ $(am__EXTRA_tools_rfcomm_SOURCES_DIST) \
+ $(am__tools_sdptool_SOURCES_DIST) \
+ $(am__tracer_hcitrace_SOURCES_DIST)
+man1dir = $(mandir)/man1
+man8dir = $(mandir)/man8
+NROFF = nroff
+MANS = $(dist_man_MANS) $(man_MANS)
+DATA = $(alsaconf_DATA) $(conf_DATA) $(dbus_DATA) $(pkgconfig_DATA) \
+ $(rules_DATA) $(state_DATA)
+HEADERS = $(include_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+distdir = $(PACKAGE)-$(VERSION)
+top_distdir = $(distdir)
+am__remove_distdir = \
+ { test ! -d "$(distdir)" \
+ || { find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \
+ && rm -fr "$(distdir)"; }; }
+DIST_ARCHIVES = $(distdir).tar.gz
+GZIP_ENV = --best
+distuninstallcheck_listfiles = find . -type f -print
+distcleancheck_listfiles = find . -type f -print
+ACLOCAL = @ACLOCAL@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CAPNG_CFLAGS = @CAPNG_CFLAGS@
+CAPNG_LIBS = @CAPNG_LIBS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CONFIGDIR = @CONFIGDIR@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_LIBS = @DBUS_LIBS@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_LIBS = @GLIB_LIBS@
+GREP = @GREP@
+GSTREAMER_CFLAGS = @GSTREAMER_CFLAGS@
+GSTREAMER_LIBS = @GSTREAMER_LIBS@
+GSTREAMER_PLUGINSDIR = @GSTREAMER_PLUGINSDIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LEX = @LEX@
+LEXLIB = @LEXLIB@
+LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+RANLIB = @RANLIB@
+READLINE_LIBS = @READLINE_LIBS@
+SAP_DRIVER = @SAP_DRIVER@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SNDFILE_CFLAGS = @SNDFILE_CFLAGS@
+SNDFILE_LIBS = @SNDFILE_LIBS@
+STORAGEDIR = @STORAGEDIR@
+STRIP = @STRIP@
+TELEPHONY_DRIVER = @TELEPHONY_DRIVER@
+UDEV_DATADIR = @UDEV_DATADIR@
+USB_CFLAGS = @USB_CFLAGS@
+USB_LIBS = @USB_LIBS@
+VERSION = @VERSION@
+YACC = @YACC@
+YFLAGS = @YFLAGS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@/bluetooth
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_MAKEFLAGS = --no-print-directory
+lib_LTLIBRARIES = lib/libbluetooth.la
+noinst_LTLIBRARIES = $(am__append_1)
+dist_man_MANS = $(am__append_40) $(am__append_46) $(am__append_49) \
+ $(am__append_52) $(am__append_58) $(am__append_62) \
+ $(am__append_65) $(am__append_68)
+dist_noinst_MANS =
+CLEANFILES = $(local_headers) $(builtin_files) tools/lexer.c \
+ tools/parser.c tools/parser.h $(rules_DATA)
+EXTRA_DIST = plugins/hal.c plugins/formfactor.c src/genbuiltin \
+ src/bluetooth.conf src/main.conf network/network.conf \
+ input/input.conf serial/serial.conf audio/audio.conf \
+ audio/telephony-dummy.c audio/telephony-maemo5.c \
+ audio/telephony-ofono.c audio/telephony-maemo6.c \
+ sap/sap-dummy.c audio/bluetooth.conf $(am__append_41) \
+ tools/rfcomm.conf $(am__append_47) $(am__append_50) \
+ $(am__append_53) tools/dfubabel.1 tools/avctrl.8 \
+ $(am__append_59) $(am__append_60) test/apitest test/sap-client \
+ test/hsplay test/hsmicro test/dbusdef.py \
+ test/monitor-bluetooth test/list-devices test/test-discovery \
+ test/test-manager test/test-adapter test/test-device \
+ test/test-service test/test-serial test/test-telephony \
+ test/test-network test/simple-agent test/simple-service \
+ test/simple-endpoint test/test-audio test/test-input \
+ test/test-attrib test/test-sap-server test/service-record.dtd \
+ test/service-did.xml test/service-spp.xml test/service-opp.xml \
+ test/service-ftp.xml $(am__append_63) $(am__append_66) \
+ $(am__append_69) scripts/bluetooth.rules \
+ scripts/bluetooth-hid2hci.rules scripts/bluetooth-serial.rules \
+ doc/manager-api.txt doc/adapter-api.txt doc/device-api.txt \
+ doc/service-api.txt doc/agent-api.txt doc/attribute-api.txt \
+ doc/serial-api.txt doc/network-api.txt doc/input-api.txt \
+ doc/audio-api.txt doc/control-api.txt doc/hfp-api.txt \
+ doc/health-api.txt doc/sap-api.txt doc/media-api.txt \
+ doc/assigned-numbers.txt
+include_HEADERS = $(lib_headers)
+@CONFIGFILES_TRUE@dbusdir = $(sysconfdir)/dbus-1/system.d
+@CONFIGFILES_TRUE@dbus_DATA = src/bluetooth.conf
+@CONFIGFILES_TRUE@confdir = $(sysconfdir)/bluetooth
+@CONFIGFILES_TRUE@conf_DATA = src/main.conf $(am__append_36)
+@CONFIGFILES_TRUE@statedir = $(localstatedir)/lib/bluetooth
+@CONFIGFILES_TRUE@state_DATA =
+plugindir = $(libdir)/bluetooth/plugins
+plugin_LTLIBRARIES =
+lib_headers = lib/bluetooth.h lib/hci.h lib/hci_lib.h lib/mgmt.h \
+ lib/sco.h lib/l2cap.h lib/sdp.h lib/sdp_lib.h lib/uuid.h \
+ lib/rfcomm.h lib/bnep.h lib/cmtp.h lib/hidp.h
+
+local_headers = $(foreach file,$(lib_headers), lib/bluetooth/$(notdir $(file)))
+lib_libbluetooth_la_SOURCES = $(lib_headers) \
+ lib/bluetooth.c lib/hci.c lib/sdp.c lib/uuid.c
+
+lib_libbluetooth_la_LDFLAGS = -version-info 14:0:11
+lib_libbluetooth_la_DEPENDENCIES = $(local_headers)
+@SBC_TRUE@sbc_libsbc_la_SOURCES = sbc/sbc.h sbc/sbc.c sbc/sbc_math.h sbc/sbc_tables.h \
+@SBC_TRUE@ sbc/sbc_primitives.h sbc/sbc_primitives.c \
+@SBC_TRUE@ sbc/sbc_primitives_mmx.h sbc/sbc_primitives_mmx.c \
+@SBC_TRUE@ sbc/sbc_primitives_iwmmxt.h sbc/sbc_primitives_iwmmxt.c \
+@SBC_TRUE@ sbc/sbc_primitives_neon.h sbc/sbc_primitives_neon.c \
+@SBC_TRUE@ sbc/sbc_primitives_armv6.h sbc/sbc_primitives_armv6.c
+
+@SBC_TRUE@sbc_libsbc_la_CFLAGS = -finline-functions -fgcse-after-reload \
+@SBC_TRUE@ -funswitch-loops -funroll-loops
+
+@SBC_TRUE@sbc_sbcdec_SOURCES = sbc/sbcdec.c sbc/formats.h
+@SBC_TRUE@sbc_sbcdec_LDADD = sbc/libsbc.la
+@SBC_TRUE@sbc_sbcenc_SOURCES = sbc/sbcenc.c sbc/formats.h
+@SBC_TRUE@sbc_sbcenc_LDADD = sbc/libsbc.la
+@SBC_TRUE@@SNDFILE_TRUE@sbc_sbctester_LDADD = @SNDFILE_LIBS@ -lm
+@SBC_TRUE@@SNDFILE_TRUE@sbc_sbctest_CFLAGS = @SNDFILE_CFLAGS@
+attrib_sources = attrib/att.h attrib/att.c attrib/gatt.h attrib/gatt.c \
+ attrib/gattrib.h attrib/gattrib.c
+
+gdbus_sources = gdbus/gdbus.h gdbus/mainloop.c gdbus/watch.c \
+ gdbus/object.c gdbus/polkit.c
+
+btio_sources = btio/btio.h btio/btio.c
+builtin_modules = $(am__append_5) $(am__append_7) $(am__append_9) \
+ $(am__append_12) $(am__append_15) $(am__append_17) \
+ $(am__append_19) $(am__append_21) $(am__append_24) \
+ $(am__append_26) hciops mgmtops $(am__append_28) \
+ $(am__append_30) storage $(am__append_32) $(am__append_34)
+builtin_sources = $(am__append_6) $(am__append_8) $(am__append_10) \
+ $(am__append_13) $(am__append_16) $(am__append_18) \
+ $(am__append_20) $(am__append_22) $(am__append_25) \
+ $(am__append_27) plugins/hciops.c plugins/mgmtops.c \
+ $(am__append_29) $(am__append_31) plugins/storage.c \
+ $(am__append_33) $(am__append_35)
+builtin_nodist = $(am__append_11) $(am__append_14)
+mcap_sources = $(am__append_4)
+@AUDIOPLUGIN_TRUE@noinst_LIBRARIES = audio/libtelephony.a
+@SAPPLUGIN_TRUE@noinst_LIBRARIES = sap/libsap.a
+@AUDIOPLUGIN_TRUE@audio_libtelephony_a_SOURCES = audio/telephony.h audio/telephony-dummy.c \
+@AUDIOPLUGIN_TRUE@ audio/telephony-maemo5.c audio/telephony-ofono.c \
+@AUDIOPLUGIN_TRUE@ audio/telephony-maemo6.c
+
+@SAPPLUGIN_TRUE@sap_libsap_a_SOURCES = sap/sap.h sap/sap-dummy.c
+@ATTRIBPLUGIN_TRUE@@READLINE_TRUE@attrib_gatttool_SOURCES = attrib/gatttool.c attrib/att.c attrib/gatt.c \
+@ATTRIBPLUGIN_TRUE@@READLINE_TRUE@ attrib/gattrib.c btio/btio.c \
+@ATTRIBPLUGIN_TRUE@@READLINE_TRUE@ src/glib-helper.h src/glib-helper.c \
+@ATTRIBPLUGIN_TRUE@@READLINE_TRUE@ attrib/gatttool.h attrib/interactive.c \
+@ATTRIBPLUGIN_TRUE@@READLINE_TRUE@ attrib/utils.c
+
+@ATTRIBPLUGIN_TRUE@@READLINE_TRUE@attrib_gatttool_LDADD = lib/libbluetooth.la @GLIB_LIBS@ @READLINE_LIBS@
+src_bluetoothd_SOURCES = $(gdbus_sources) $(builtin_sources) \
+ $(attrib_sources) $(btio_sources) \
+ $(mcap_sources) src/bluetooth.ver \
+ src/main.c src/log.h src/log.c \
+ src/rfkill.c src/hcid.h src/sdpd.h \
+ src/sdpd-server.c src/sdpd-request.c \
+ src/sdpd-service.c src/sdpd-database.c \
+ src/attrib-server.h src/attrib-server.c \
+ src/sdp-xml.h src/sdp-xml.c \
+ src/textfile.h src/textfile.c \
+ src/glib-helper.h src/glib-helper.c \
+ src/oui.h src/oui.c src/uinput.h src/ppoll.h \
+ src/plugin.h src/plugin.c \
+ src/storage.h src/storage.c \
+ src/agent.h src/agent.c \
+ src/error.h src/error.c \
+ src/manager.h src/manager.c \
+ src/adapter.h src/adapter.c \
+ src/device.h src/device.c \
+ src/dbus-common.c src/dbus-common.h \
+ src/event.h src/event.c \
+ src/oob.h src/oob.c
+
+src_bluetoothd_LDADD = lib/libbluetooth.la @GLIB_LIBS@ @DBUS_LIBS@ \
+ @CAPNG_LIBS@ -ldl -lrt
+
+src_bluetoothd_LDFLAGS = -Wl,--export-dynamic \
+ -Wl,--version-script=$(srcdir)/src/bluetooth.ver
+
+src_bluetoothd_DEPENDENCIES = lib/libbluetooth.la
+builtin_files = src/builtin.h $(builtin_nodist)
+nodist_src_bluetoothd_SOURCES = $(builtin_files)
+man_MANS = src/bluetoothd.8
+@ALSA_TRUE@alsadir = $(libdir)/alsa-lib
+@ALSA_TRUE@alsa_LTLIBRARIES = audio/libasound_module_pcm_bluetooth.la \
+@ALSA_TRUE@ audio/libasound_module_ctl_bluetooth.la
+
+@ALSA_TRUE@audio_libasound_module_pcm_bluetooth_la_SOURCES = audio/pcm_bluetooth.c \
+@ALSA_TRUE@ audio/rtp.h audio/ipc.h audio/ipc.c
+
+@ALSA_TRUE@audio_libasound_module_pcm_bluetooth_la_LDFLAGS = -module -avoid-version #-export-symbols-regex [_]*snd_pcm_.*
+@ALSA_TRUE@audio_libasound_module_pcm_bluetooth_la_LIBADD = sbc/libsbc.la \
+@ALSA_TRUE@ lib/libbluetooth.la @ALSA_LIBS@
+
+@ALSA_TRUE@audio_libasound_module_pcm_bluetooth_la_CFLAGS = @ALSA_CFLAGS@
+@ALSA_TRUE@audio_libasound_module_ctl_bluetooth_la_SOURCES = audio/ctl_bluetooth.c \
+@ALSA_TRUE@ audio/rtp.h audio/ipc.h audio/ipc.c
+
+@ALSA_TRUE@audio_libasound_module_ctl_bluetooth_la_LDFLAGS = -module -avoid-version #-export-symbols-regex [_]*snd_ctl_.*
+@ALSA_TRUE@audio_libasound_module_ctl_bluetooth_la_LIBADD = lib/libbluetooth.la @ALSA_LIBS@
+@ALSA_TRUE@audio_libasound_module_ctl_bluetooth_la_CFLAGS = @ALSA_CFLAGS@
+@ALSA_TRUE@@CONFIGFILES_TRUE@alsaconfdir = $(datadir)/alsa
+@ALSA_TRUE@@CONFIGFILES_TRUE@alsaconf_DATA = audio/bluetooth.conf
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@gstreamerdir = $(libdir)/gstreamer-0.10
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@gstreamer_LTLIBRARIES = audio/libgstbluetooth.la
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@audio_libgstbluetooth_la_SOURCES = audio/gstbluetooth.c audio/gstpragma.h \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@ audio/gstsbcenc.h audio/gstsbcenc.c \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@ audio/gstsbcdec.h audio/gstsbcdec.c \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@ audio/gstsbcparse.h audio/gstsbcparse.c \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@ audio/gstavdtpsink.h audio/gstavdtpsink.c \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@ audio/gsta2dpsink.h audio/gsta2dpsink.c \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@ audio/gstsbcutil.h audio/gstsbcutil.c \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@ audio/gstrtpsbcpay.h audio/gstrtpsbcpay.c \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@ audio/rtp.h audio/ipc.h audio/ipc.c
+
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@audio_libgstbluetooth_la_LDFLAGS = -module -avoid-version
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@audio_libgstbluetooth_la_LIBADD = sbc/libsbc.la lib/libbluetooth.la \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@ @DBUS_LIBS@ @GSTREAMER_LIBS@ -lgstaudio-0.10 -lgstrtp-0.10
+
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@audio_libgstbluetooth_la_CFLAGS = -fvisibility=hidden -fno-strict-aliasing \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@ $(AM_CFLAGS) @DBUS_CFLAGS@ @GSTREAMER_CFLAGS@
+
+@TOOLS_TRUE@tools_rfcomm_SOURCES = tools/rfcomm.c tools/parser.y tools/lexer.l \
+@TOOLS_TRUE@ tools/kword.h tools/kword.c
+
+@TOOLS_TRUE@EXTRA_tools_rfcomm_SOURCES = tools/parser.h tools/parser.c \
+@TOOLS_TRUE@ tools/lexer.c
+
+@TOOLS_TRUE@tools_rfcomm_LDADD = lib/libbluetooth.la
+@TOOLS_TRUE@tools_l2ping_LDADD = lib/libbluetooth.la
+@TOOLS_TRUE@tools_hciattach_SOURCES = tools/hciattach.c tools/hciattach.h \
+@TOOLS_TRUE@ tools/hciattach_st.c \
+@TOOLS_TRUE@ tools/hciattach_ti.c \
+@TOOLS_TRUE@ tools/hciattach_tialt.c \
+@TOOLS_TRUE@ tools/hciattach_ath3k.c \
+@TOOLS_TRUE@ tools/hciattach_qualcomm.c
+
+@TOOLS_TRUE@tools_hciattach_LDADD = lib/libbluetooth.la
+@TOOLS_TRUE@tools_hciconfig_SOURCES = tools/hciconfig.c tools/csr.h tools/csr.c \
+@TOOLS_TRUE@ src/textfile.h src/textfile.c
+
+@TOOLS_TRUE@tools_hciconfig_LDADD = lib/libbluetooth.la
+@TOOLS_TRUE@tools_hcitool_SOURCES = tools/hcitool.c src/oui.h src/oui.c \
+@TOOLS_TRUE@ src/textfile.h src/textfile.c
+
+@TOOLS_TRUE@tools_hcitool_LDADD = lib/libbluetooth.la
+@TOOLS_TRUE@tools_sdptool_SOURCES = tools/sdptool.c src/sdp-xml.h src/sdp-xml.c
+@TOOLS_TRUE@tools_sdptool_LDADD = lib/libbluetooth.la
+@TOOLS_TRUE@tools_ciptool_LDADD = lib/libbluetooth.la
+@TOOLS_TRUE@tools_avinfo_LDADD = lib/libbluetooth.la
+@TOOLS_TRUE@tools_ppporc_LDADD = lib/libbluetooth.la
+@TOOLS_TRUE@tools_hcieventmask_LDADD = lib/libbluetooth.la
+@TRACER_TRUE@tracer_hcitrace_SOURCES = tracer/main.c
+@TRACER_TRUE@tracer_hcitrace_LDADD = lib/libbluetooth.la \
+@TRACER_TRUE@ @GLIB_LIBS@ @DBUS_LIBS@ @CAPNG_LIBS@
+
+@TRACER_TRUE@tracer_hcitrace_DEPENDENCIES = lib/libbluetooth.la
+@BCCMD_TRUE@tools_bccmd_SOURCES = tools/bccmd.c tools/csr.h \
+@BCCMD_TRUE@ tools/csr.c tools/csr_hci.c tools/csr_h4.c \
+@BCCMD_TRUE@ tools/csr_3wire.c tools/csr_bcsp.c tools/ubcsp.h \
+@BCCMD_TRUE@ tools/ubcsp.c $(am__append_44)
+@BCCMD_TRUE@tools_bccmd_LDADD = lib/libbluetooth.la $(am__append_45)
+@HID2HCI_TRUE@tools_hid2hci_LDADD = @USB_LIBS@
+@DFUTOOL_TRUE@tools_dfutool_SOURCES = tools/dfutool.c tools/dfu.h tools/dfu.c
+@DFUTOOL_TRUE@tools_dfutool_LDADD = @USB_LIBS@
+@USB_TRUE@tools_dfubabel_LDADD = @USB_LIBS@
+@USB_TRUE@tools_avctrl_LDADD = @USB_LIBS@
+@CUPS_TRUE@cupsdir = $(libdir)/cups/backend
+@CUPS_TRUE@cups_bluetooth_SOURCES = $(gdbus_sources) cups/main.c cups/cups.h \
+@CUPS_TRUE@ cups/sdp.c cups/spp.c cups/hcrp.c
+
+@CUPS_TRUE@cups_bluetooth_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ lib/libbluetooth.la
+@TEST_TRUE@test_hciemu_LDADD = @GLIB_LIBS@ lib/libbluetooth.la
+@TEST_TRUE@test_l2test_LDADD = lib/libbluetooth.la
+@TEST_TRUE@test_rctest_LDADD = lib/libbluetooth.la
+@TEST_TRUE@test_gaptest_LDADD = @DBUS_LIBS@
+@TEST_TRUE@test_sdptest_LDADD = lib/libbluetooth.la
+@TEST_TRUE@test_scotest_LDADD = lib/libbluetooth.la
+@TEST_TRUE@test_attest_LDADD = lib/libbluetooth.la
+@TEST_TRUE@test_hstest_LDADD = lib/libbluetooth.la
+@TEST_TRUE@test_avtest_LDADD = lib/libbluetooth.la
+@TEST_TRUE@test_lmptest_LDADD = lib/libbluetooth.la
+@TEST_TRUE@test_ipctest_SOURCES = test/ipctest.c audio/ipc.h audio/ipc.c
+@TEST_TRUE@test_ipctest_LDADD = @GLIB_LIBS@ sbc/libsbc.la
+@TEST_TRUE@test_bdaddr_SOURCES = test/bdaddr.c src/oui.h src/oui.c
+@TEST_TRUE@test_bdaddr_LDADD = lib/libbluetooth.la
+@TEST_TRUE@test_agent_LDADD = @DBUS_LIBS@
+@TEST_TRUE@test_btiotest_SOURCES = test/btiotest.c btio/btio.h btio/btio.c
+@TEST_TRUE@test_btiotest_LDADD = @GLIB_LIBS@ lib/libbluetooth.la
+@TEST_TRUE@test_uuidtest_SOURCES = test/uuidtest.c
+@TEST_TRUE@test_uuidtest_LDADD = lib/libbluetooth.la
+@TEST_TRUE@test_test_textfile_SOURCES = test/test-textfile.c src/textfile.h src/textfile.c
+@HIDD_TRUE@compat_hidd_SOURCES = compat/hidd.c compat/hidd.h src/uinput.h \
+@HIDD_TRUE@ compat/sdp.h compat/sdp.c compat/fakehid.c \
+@HIDD_TRUE@ src/textfile.h src/textfile.c
+
+@HIDD_TRUE@compat_hidd_LDADD = -lm lib/libbluetooth.la
+@PAND_TRUE@compat_pand_SOURCES = compat/pand.c compat/pand.h \
+@PAND_TRUE@ compat/bnep.c compat/sdp.h compat/sdp.c \
+@PAND_TRUE@ src/textfile.h src/textfile.c
+
+@PAND_TRUE@compat_pand_LDADD = lib/libbluetooth.la
+@DUND_TRUE@compat_dund_SOURCES = compat/dund.c compat/dund.h compat/lib.h \
+@DUND_TRUE@ compat/sdp.h compat/sdp.c compat/dun.c compat/msdun.c \
+@DUND_TRUE@ src/textfile.h src/textfile.c
+
+@DUND_TRUE@compat_dund_LDADD = lib/libbluetooth.la
+@UDEVRULES_TRUE@rulesdir = @UDEV_DATADIR@
+@UDEVRULES_TRUE@udev_files = scripts/bluetooth.rules $(am__append_70) \
+@UDEVRULES_TRUE@ $(am__append_71)
+@UDEVRULES_TRUE@rules_DATA = $(foreach file,$(udev_files), scripts/97-$(notdir $(file)))
+@PCMCIA_TRUE@udevdir = $(libexecdir)/udev
+@PCMCIA_TRUE@dist_udev_SCRIPTS = scripts/bluetooth_serial
+AM_YFLAGS = -d
+AM_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ @CAPNG_CFLAGS@ \
+ -DBLUETOOTH_PLUGIN_BUILTIN -DPLUGINDIR=\""$(plugindir)"\"
+
+INCLUDES = -I$(builddir)/lib -I$(builddir)/src -I$(srcdir)/src \
+ -I$(srcdir)/audio -I$(srcdir)/sbc -I$(srcdir)/gdbus \
+ -I$(srcdir)/attrib -I$(srcdir)/btio $(am__append_72)
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = bluez.pc
+DISTCHECK_CONFIGURE_FLAGS = --disable-udevrules --enable-attrib
+DISTCLEANFILES = $(pkgconfig_DATA)
+MAINTAINERCLEANFILES = Makefile.in \
+ aclocal.m4 configure config.h.in config.sub config.guess \
+ ltmain.sh depcomp compile missing install-sh mkinstalldirs ylwrap
+
+all: config.h
+ $(MAKE) $(AM_MAKEFLAGS) all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .l .lo .o .obj .y
+am--refresh:
+ @:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(srcdir)/Makefile.tools $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \
+ $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ echo ' $(SHELL) ./config.status'; \
+ $(SHELL) ./config.status;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ $(SHELL) ./config.status --recheck
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ $(am__cd) $(srcdir) && $(AUTOCONF)
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS)
+$(am__aclocal_m4_deps):
+
+config.h: stamp-h1
+ @if test ! -f $@; then \
+ rm -f stamp-h1; \
+ $(MAKE) $(AM_MAKEFLAGS) stamp-h1; \
+ else :; fi
+
+stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status
+ @rm -f stamp-h1
+ cd $(top_builddir) && $(SHELL) ./config.status config.h
+$(srcdir)/config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ ($(am__cd) $(top_srcdir) && $(AUTOHEADER))
+ rm -f stamp-h1
+ touch $@
+
+distclean-hdr:
+ -rm -f config.h stamp-h1
+scripts/bluetooth.rules: $(top_builddir)/config.status $(top_srcdir)/scripts/bluetooth.rules.in
+ cd $(top_builddir) && $(SHELL) ./config.status $@
+doc/version.xml: $(top_builddir)/config.status $(top_srcdir)/doc/version.xml.in
+ cd $(top_builddir) && $(SHELL) ./config.status $@
+src/bluetoothd.8: $(top_builddir)/config.status $(top_srcdir)/src/bluetoothd.8.in
+ cd $(top_builddir) && $(SHELL) ./config.status $@
+bluez.pc: $(top_builddir)/config.status $(srcdir)/bluez.pc.in
+ cd $(top_builddir) && $(SHELL) ./config.status $@
+
+clean-noinstLIBRARIES:
+ -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+audio/$(am__dirstamp):
+ @$(MKDIR_P) audio
+ @: > audio/$(am__dirstamp)
+audio/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) audio/$(DEPDIR)
+ @: > audio/$(DEPDIR)/$(am__dirstamp)
+audio/telephony-dummy.$(OBJEXT): audio/$(am__dirstamp) \
+ audio/$(DEPDIR)/$(am__dirstamp)
+audio/telephony-maemo5.$(OBJEXT): audio/$(am__dirstamp) \
+ audio/$(DEPDIR)/$(am__dirstamp)
+audio/telephony-ofono.$(OBJEXT): audio/$(am__dirstamp) \
+ audio/$(DEPDIR)/$(am__dirstamp)
+audio/telephony-maemo6.$(OBJEXT): audio/$(am__dirstamp) \
+ audio/$(DEPDIR)/$(am__dirstamp)
+audio/libtelephony.a: $(audio_libtelephony_a_OBJECTS) $(audio_libtelephony_a_DEPENDENCIES) audio/$(am__dirstamp)
+ $(AM_V_at)-rm -f audio/libtelephony.a
+ $(AM_V_AR)$(audio_libtelephony_a_AR) audio/libtelephony.a $(audio_libtelephony_a_OBJECTS) $(audio_libtelephony_a_LIBADD)
+ $(AM_V_at)$(RANLIB) audio/libtelephony.a
+sap/$(am__dirstamp):
+ @$(MKDIR_P) sap
+ @: > sap/$(am__dirstamp)
+sap/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) sap/$(DEPDIR)
+ @: > sap/$(DEPDIR)/$(am__dirstamp)
+sap/sap-dummy.$(OBJEXT): sap/$(am__dirstamp) \
+ sap/$(DEPDIR)/$(am__dirstamp)
+sap/libsap.a: $(sap_libsap_a_OBJECTS) $(sap_libsap_a_DEPENDENCIES) sap/$(am__dirstamp)
+ $(AM_V_at)-rm -f sap/libsap.a
+ $(AM_V_AR)$(sap_libsap_a_AR) sap/libsap.a $(sap_libsap_a_OBJECTS) $(sap_libsap_a_LIBADD)
+ $(AM_V_at)$(RANLIB) sap/libsap.a
+install-alsaLTLIBRARIES: $(alsa_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ test -z "$(alsadir)" || $(MKDIR_P) "$(DESTDIR)$(alsadir)"
+ @list='$(alsa_LTLIBRARIES)'; test -n "$(alsadir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(alsadir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(alsadir)"; \
+ }
+
+uninstall-alsaLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(alsa_LTLIBRARIES)'; test -n "$(alsadir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(alsadir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(alsadir)/$$f"; \
+ done
+
+clean-alsaLTLIBRARIES:
+ -test -z "$(alsa_LTLIBRARIES)" || rm -f $(alsa_LTLIBRARIES)
+ @list='$(alsa_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+install-gstreamerLTLIBRARIES: $(gstreamer_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ test -z "$(gstreamerdir)" || $(MKDIR_P) "$(DESTDIR)$(gstreamerdir)"
+ @list='$(gstreamer_LTLIBRARIES)'; test -n "$(gstreamerdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(gstreamerdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(gstreamerdir)"; \
+ }
+
+uninstall-gstreamerLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(gstreamer_LTLIBRARIES)'; test -n "$(gstreamerdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(gstreamerdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(gstreamerdir)/$$f"; \
+ done
+
+clean-gstreamerLTLIBRARIES:
+ -test -z "$(gstreamer_LTLIBRARIES)" || rm -f $(gstreamer_LTLIBRARIES)
+ @list='$(gstreamer_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+ }
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+install-pluginLTLIBRARIES: $(plugin_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ test -z "$(plugindir)" || $(MKDIR_P) "$(DESTDIR)$(plugindir)"
+ @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(plugindir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(plugindir)"; \
+ }
+
+uninstall-pluginLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(plugindir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(plugindir)/$$f"; \
+ done
+
+clean-pluginLTLIBRARIES:
+ -test -z "$(plugin_LTLIBRARIES)" || rm -f $(plugin_LTLIBRARIES)
+ @list='$(plugin_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+audio/audio_libasound_module_ctl_bluetooth_la-ctl_bluetooth.lo: \
+ audio/$(am__dirstamp) audio/$(DEPDIR)/$(am__dirstamp)
+audio/audio_libasound_module_ctl_bluetooth_la-ipc.lo: \
+ audio/$(am__dirstamp) audio/$(DEPDIR)/$(am__dirstamp)
+audio/libasound_module_ctl_bluetooth.la: $(audio_libasound_module_ctl_bluetooth_la_OBJECTS) $(audio_libasound_module_ctl_bluetooth_la_DEPENDENCIES) audio/$(am__dirstamp)
+ $(AM_V_CCLD)$(audio_libasound_module_ctl_bluetooth_la_LINK) $(am_audio_libasound_module_ctl_bluetooth_la_rpath) $(audio_libasound_module_ctl_bluetooth_la_OBJECTS) $(audio_libasound_module_ctl_bluetooth_la_LIBADD) $(LIBS)
+audio/audio_libasound_module_pcm_bluetooth_la-pcm_bluetooth.lo: \
+ audio/$(am__dirstamp) audio/$(DEPDIR)/$(am__dirstamp)
+audio/audio_libasound_module_pcm_bluetooth_la-ipc.lo: \
+ audio/$(am__dirstamp) audio/$(DEPDIR)/$(am__dirstamp)
+audio/libasound_module_pcm_bluetooth.la: $(audio_libasound_module_pcm_bluetooth_la_OBJECTS) $(audio_libasound_module_pcm_bluetooth_la_DEPENDENCIES) audio/$(am__dirstamp)
+ $(AM_V_CCLD)$(audio_libasound_module_pcm_bluetooth_la_LINK) $(am_audio_libasound_module_pcm_bluetooth_la_rpath) $(audio_libasound_module_pcm_bluetooth_la_OBJECTS) $(audio_libasound_module_pcm_bluetooth_la_LIBADD) $(LIBS)
+audio/audio_libgstbluetooth_la-gstbluetooth.lo: audio/$(am__dirstamp) \
+ audio/$(DEPDIR)/$(am__dirstamp)
+audio/audio_libgstbluetooth_la-gstsbcenc.lo: audio/$(am__dirstamp) \
+ audio/$(DEPDIR)/$(am__dirstamp)
+audio/audio_libgstbluetooth_la-gstsbcdec.lo: audio/$(am__dirstamp) \
+ audio/$(DEPDIR)/$(am__dirstamp)
+audio/audio_libgstbluetooth_la-gstsbcparse.lo: audio/$(am__dirstamp) \
+ audio/$(DEPDIR)/$(am__dirstamp)
+audio/audio_libgstbluetooth_la-gstavdtpsink.lo: audio/$(am__dirstamp) \
+ audio/$(DEPDIR)/$(am__dirstamp)
+audio/audio_libgstbluetooth_la-gsta2dpsink.lo: audio/$(am__dirstamp) \
+ audio/$(DEPDIR)/$(am__dirstamp)
+audio/audio_libgstbluetooth_la-gstsbcutil.lo: audio/$(am__dirstamp) \
+ audio/$(DEPDIR)/$(am__dirstamp)
+audio/audio_libgstbluetooth_la-gstrtpsbcpay.lo: audio/$(am__dirstamp) \
+ audio/$(DEPDIR)/$(am__dirstamp)
+audio/audio_libgstbluetooth_la-ipc.lo: audio/$(am__dirstamp) \
+ audio/$(DEPDIR)/$(am__dirstamp)
+audio/libgstbluetooth.la: $(audio_libgstbluetooth_la_OBJECTS) $(audio_libgstbluetooth_la_DEPENDENCIES) audio/$(am__dirstamp)
+ $(AM_V_CCLD)$(audio_libgstbluetooth_la_LINK) $(am_audio_libgstbluetooth_la_rpath) $(audio_libgstbluetooth_la_OBJECTS) $(audio_libgstbluetooth_la_LIBADD) $(LIBS)
+lib/$(am__dirstamp):
+ @$(MKDIR_P) lib
+ @: > lib/$(am__dirstamp)
+lib/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) lib/$(DEPDIR)
+ @: > lib/$(DEPDIR)/$(am__dirstamp)
+lib/bluetooth.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp)
+lib/hci.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp)
+lib/sdp.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp)
+lib/uuid.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp)
+lib/libbluetooth.la: $(lib_libbluetooth_la_OBJECTS) $(lib_libbluetooth_la_DEPENDENCIES) lib/$(am__dirstamp)
+ $(AM_V_CCLD)$(lib_libbluetooth_la_LINK) -rpath $(libdir) $(lib_libbluetooth_la_OBJECTS) $(lib_libbluetooth_la_LIBADD) $(LIBS)
+sbc/$(am__dirstamp):
+ @$(MKDIR_P) sbc
+ @: > sbc/$(am__dirstamp)
+sbc/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) sbc/$(DEPDIR)
+ @: > sbc/$(DEPDIR)/$(am__dirstamp)
+sbc/sbc_libsbc_la-sbc.lo: sbc/$(am__dirstamp) \
+ sbc/$(DEPDIR)/$(am__dirstamp)
+sbc/sbc_libsbc_la-sbc_primitives.lo: sbc/$(am__dirstamp) \
+ sbc/$(DEPDIR)/$(am__dirstamp)
+sbc/sbc_libsbc_la-sbc_primitives_mmx.lo: sbc/$(am__dirstamp) \
+ sbc/$(DEPDIR)/$(am__dirstamp)
+sbc/sbc_libsbc_la-sbc_primitives_iwmmxt.lo: sbc/$(am__dirstamp) \
+ sbc/$(DEPDIR)/$(am__dirstamp)
+sbc/sbc_libsbc_la-sbc_primitives_neon.lo: sbc/$(am__dirstamp) \
+ sbc/$(DEPDIR)/$(am__dirstamp)
+sbc/sbc_libsbc_la-sbc_primitives_armv6.lo: sbc/$(am__dirstamp) \
+ sbc/$(DEPDIR)/$(am__dirstamp)
+sbc/libsbc.la: $(sbc_libsbc_la_OBJECTS) $(sbc_libsbc_la_DEPENDENCIES) sbc/$(am__dirstamp)
+ $(AM_V_CCLD)$(sbc_libsbc_la_LINK) $(am_sbc_libsbc_la_rpath) $(sbc_libsbc_la_OBJECTS) $(sbc_libsbc_la_LIBADD) $(LIBS)
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p || test -f $$p1; \
+ then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(bindir)" && rm -f $$files
+
+clean-binPROGRAMS:
+ @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-cupsPROGRAMS: $(cups_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(cupsdir)" || $(MKDIR_P) "$(DESTDIR)$(cupsdir)"
+ @list='$(cups_PROGRAMS)'; test -n "$(cupsdir)" || list=; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p || test -f $$p1; \
+ then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(cupsdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(cupsdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-cupsPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(cups_PROGRAMS)'; test -n "$(cupsdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(cupsdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(cupsdir)" && rm -f $$files
+
+clean-cupsPROGRAMS:
+ @list='$(cups_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+clean-noinstPROGRAMS:
+ @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(sbindir)" || $(MKDIR_P) "$(DESTDIR)$(sbindir)"
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p || test -f $$p1; \
+ then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(sbindir)" && rm -f $$files
+
+clean-sbinPROGRAMS:
+ @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+attrib/$(am__dirstamp):
+ @$(MKDIR_P) attrib
+ @: > attrib/$(am__dirstamp)
+attrib/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) attrib/$(DEPDIR)
+ @: > attrib/$(DEPDIR)/$(am__dirstamp)
+attrib/gatttool.$(OBJEXT): attrib/$(am__dirstamp) \
+ attrib/$(DEPDIR)/$(am__dirstamp)
+attrib/att.$(OBJEXT): attrib/$(am__dirstamp) \
+ attrib/$(DEPDIR)/$(am__dirstamp)
+attrib/gatt.$(OBJEXT): attrib/$(am__dirstamp) \
+ attrib/$(DEPDIR)/$(am__dirstamp)
+attrib/gattrib.$(OBJEXT): attrib/$(am__dirstamp) \
+ attrib/$(DEPDIR)/$(am__dirstamp)
+btio/$(am__dirstamp):
+ @$(MKDIR_P) btio
+ @: > btio/$(am__dirstamp)
+btio/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) btio/$(DEPDIR)
+ @: > btio/$(DEPDIR)/$(am__dirstamp)
+btio/btio.$(OBJEXT): btio/$(am__dirstamp) \
+ btio/$(DEPDIR)/$(am__dirstamp)
+src/$(am__dirstamp):
+ @$(MKDIR_P) src
+ @: > src/$(am__dirstamp)
+src/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) src/$(DEPDIR)
+ @: > src/$(DEPDIR)/$(am__dirstamp)
+src/glib-helper.$(OBJEXT): src/$(am__dirstamp) \
+ src/$(DEPDIR)/$(am__dirstamp)
+attrib/interactive.$(OBJEXT): attrib/$(am__dirstamp) \
+ attrib/$(DEPDIR)/$(am__dirstamp)
+attrib/utils.$(OBJEXT): attrib/$(am__dirstamp) \
+ attrib/$(DEPDIR)/$(am__dirstamp)
+attrib/gatttool$(EXEEXT): $(attrib_gatttool_OBJECTS) $(attrib_gatttool_DEPENDENCIES) attrib/$(am__dirstamp)
+ @rm -f attrib/gatttool$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(attrib_gatttool_OBJECTS) $(attrib_gatttool_LDADD) $(LIBS)
+compat/$(am__dirstamp):
+ @$(MKDIR_P) compat
+ @: > compat/$(am__dirstamp)
+compat/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) compat/$(DEPDIR)
+ @: > compat/$(DEPDIR)/$(am__dirstamp)
+compat/dund.$(OBJEXT): compat/$(am__dirstamp) \
+ compat/$(DEPDIR)/$(am__dirstamp)
+compat/sdp.$(OBJEXT): compat/$(am__dirstamp) \
+ compat/$(DEPDIR)/$(am__dirstamp)
+compat/dun.$(OBJEXT): compat/$(am__dirstamp) \
+ compat/$(DEPDIR)/$(am__dirstamp)
+compat/msdun.$(OBJEXT): compat/$(am__dirstamp) \
+ compat/$(DEPDIR)/$(am__dirstamp)
+src/textfile.$(OBJEXT): src/$(am__dirstamp) \
+ src/$(DEPDIR)/$(am__dirstamp)
+compat/dund$(EXEEXT): $(compat_dund_OBJECTS) $(compat_dund_DEPENDENCIES) compat/$(am__dirstamp)
+ @rm -f compat/dund$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(compat_dund_OBJECTS) $(compat_dund_LDADD) $(LIBS)
+compat/hidd.$(OBJEXT): compat/$(am__dirstamp) \
+ compat/$(DEPDIR)/$(am__dirstamp)
+compat/fakehid.$(OBJEXT): compat/$(am__dirstamp) \
+ compat/$(DEPDIR)/$(am__dirstamp)
+compat/hidd$(EXEEXT): $(compat_hidd_OBJECTS) $(compat_hidd_DEPENDENCIES) compat/$(am__dirstamp)
+ @rm -f compat/hidd$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(compat_hidd_OBJECTS) $(compat_hidd_LDADD) $(LIBS)
+compat/pand.$(OBJEXT): compat/$(am__dirstamp) \
+ compat/$(DEPDIR)/$(am__dirstamp)
+compat/bnep.$(OBJEXT): compat/$(am__dirstamp) \
+ compat/$(DEPDIR)/$(am__dirstamp)
+compat/pand$(EXEEXT): $(compat_pand_OBJECTS) $(compat_pand_DEPENDENCIES) compat/$(am__dirstamp)
+ @rm -f compat/pand$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(compat_pand_OBJECTS) $(compat_pand_LDADD) $(LIBS)
+gdbus/$(am__dirstamp):
+ @$(MKDIR_P) gdbus
+ @: > gdbus/$(am__dirstamp)
+gdbus/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) gdbus/$(DEPDIR)
+ @: > gdbus/$(DEPDIR)/$(am__dirstamp)
+gdbus/mainloop.$(OBJEXT): gdbus/$(am__dirstamp) \
+ gdbus/$(DEPDIR)/$(am__dirstamp)
+gdbus/watch.$(OBJEXT): gdbus/$(am__dirstamp) \
+ gdbus/$(DEPDIR)/$(am__dirstamp)
+gdbus/object.$(OBJEXT): gdbus/$(am__dirstamp) \
+ gdbus/$(DEPDIR)/$(am__dirstamp)
+gdbus/polkit.$(OBJEXT): gdbus/$(am__dirstamp) \
+ gdbus/$(DEPDIR)/$(am__dirstamp)
+cups/$(am__dirstamp):
+ @$(MKDIR_P) cups
+ @: > cups/$(am__dirstamp)
+cups/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) cups/$(DEPDIR)
+ @: > cups/$(DEPDIR)/$(am__dirstamp)
+cups/main.$(OBJEXT): cups/$(am__dirstamp) \
+ cups/$(DEPDIR)/$(am__dirstamp)
+cups/sdp.$(OBJEXT): cups/$(am__dirstamp) \
+ cups/$(DEPDIR)/$(am__dirstamp)
+cups/spp.$(OBJEXT): cups/$(am__dirstamp) \
+ cups/$(DEPDIR)/$(am__dirstamp)
+cups/hcrp.$(OBJEXT): cups/$(am__dirstamp) \
+ cups/$(DEPDIR)/$(am__dirstamp)
+cups/bluetooth$(EXEEXT): $(cups_bluetooth_OBJECTS) $(cups_bluetooth_DEPENDENCIES) cups/$(am__dirstamp)
+ @rm -f cups/bluetooth$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(cups_bluetooth_OBJECTS) $(cups_bluetooth_LDADD) $(LIBS)
+sbc/sbcdec.$(OBJEXT): sbc/$(am__dirstamp) \
+ sbc/$(DEPDIR)/$(am__dirstamp)
+sbc/sbcdec$(EXEEXT): $(sbc_sbcdec_OBJECTS) $(sbc_sbcdec_DEPENDENCIES) sbc/$(am__dirstamp)
+ @rm -f sbc/sbcdec$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(sbc_sbcdec_OBJECTS) $(sbc_sbcdec_LDADD) $(LIBS)
+sbc/sbcenc.$(OBJEXT): sbc/$(am__dirstamp) \
+ sbc/$(DEPDIR)/$(am__dirstamp)
+sbc/sbcenc$(EXEEXT): $(sbc_sbcenc_OBJECTS) $(sbc_sbcenc_DEPENDENCIES) sbc/$(am__dirstamp)
+ @rm -f sbc/sbcenc$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(sbc_sbcenc_OBJECTS) $(sbc_sbcenc_LDADD) $(LIBS)
+sbc/sbcinfo.$(OBJEXT): sbc/$(am__dirstamp) \
+ sbc/$(DEPDIR)/$(am__dirstamp)
+sbc/sbcinfo$(EXEEXT): $(sbc_sbcinfo_OBJECTS) $(sbc_sbcinfo_DEPENDENCIES) sbc/$(am__dirstamp)
+ @rm -f sbc/sbcinfo$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(sbc_sbcinfo_OBJECTS) $(sbc_sbcinfo_LDADD) $(LIBS)
+sbc/sbctester.$(OBJEXT): sbc/$(am__dirstamp) \
+ sbc/$(DEPDIR)/$(am__dirstamp)
+sbc/sbctester$(EXEEXT): $(sbc_sbctester_OBJECTS) $(sbc_sbctester_DEPENDENCIES) sbc/$(am__dirstamp)
+ @rm -f sbc/sbctester$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(sbc_sbctester_OBJECTS) $(sbc_sbctester_LDADD) $(LIBS)
+plugins/$(am__dirstamp):
+ @$(MKDIR_P) plugins
+ @: > plugins/$(am__dirstamp)
+plugins/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) plugins/$(DEPDIR)
+ @: > plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/pnat.$(OBJEXT): plugins/$(am__dirstamp) \
+ plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/echo.$(OBJEXT): plugins/$(am__dirstamp) \
+ plugins/$(DEPDIR)/$(am__dirstamp)
+audio/main.$(OBJEXT): audio/$(am__dirstamp) \
+ audio/$(DEPDIR)/$(am__dirstamp)
+audio/manager.$(OBJEXT): audio/$(am__dirstamp) \
+ audio/$(DEPDIR)/$(am__dirstamp)
+audio/gateway.$(OBJEXT): audio/$(am__dirstamp) \
+ audio/$(DEPDIR)/$(am__dirstamp)
+audio/headset.$(OBJEXT): audio/$(am__dirstamp) \
+ audio/$(DEPDIR)/$(am__dirstamp)
+audio/control.$(OBJEXT): audio/$(am__dirstamp) \
+ audio/$(DEPDIR)/$(am__dirstamp)
+audio/device.$(OBJEXT): audio/$(am__dirstamp) \
+ audio/$(DEPDIR)/$(am__dirstamp)
+audio/source.$(OBJEXT): audio/$(am__dirstamp) \
+ audio/$(DEPDIR)/$(am__dirstamp)
+audio/sink.$(OBJEXT): audio/$(am__dirstamp) \
+ audio/$(DEPDIR)/$(am__dirstamp)
+audio/a2dp.$(OBJEXT): audio/$(am__dirstamp) \
+ audio/$(DEPDIR)/$(am__dirstamp)
+audio/avdtp.$(OBJEXT): audio/$(am__dirstamp) \
+ audio/$(DEPDIR)/$(am__dirstamp)
+audio/ipc.$(OBJEXT): audio/$(am__dirstamp) \
+ audio/$(DEPDIR)/$(am__dirstamp)
+audio/unix.$(OBJEXT): audio/$(am__dirstamp) \
+ audio/$(DEPDIR)/$(am__dirstamp)
+audio/media.$(OBJEXT): audio/$(am__dirstamp) \
+ audio/$(DEPDIR)/$(am__dirstamp)
+audio/transport.$(OBJEXT): audio/$(am__dirstamp) \
+ audio/$(DEPDIR)/$(am__dirstamp)
+sap/main.$(OBJEXT): sap/$(am__dirstamp) sap/$(DEPDIR)/$(am__dirstamp)
+sap/manager.$(OBJEXT): sap/$(am__dirstamp) \
+ sap/$(DEPDIR)/$(am__dirstamp)
+sap/server.$(OBJEXT): sap/$(am__dirstamp) \
+ sap/$(DEPDIR)/$(am__dirstamp)
+input/$(am__dirstamp):
+ @$(MKDIR_P) input
+ @: > input/$(am__dirstamp)
+input/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) input/$(DEPDIR)
+ @: > input/$(DEPDIR)/$(am__dirstamp)
+input/main.$(OBJEXT): input/$(am__dirstamp) \
+ input/$(DEPDIR)/$(am__dirstamp)
+input/manager.$(OBJEXT): input/$(am__dirstamp) \
+ input/$(DEPDIR)/$(am__dirstamp)
+input/server.$(OBJEXT): input/$(am__dirstamp) \
+ input/$(DEPDIR)/$(am__dirstamp)
+input/device.$(OBJEXT): input/$(am__dirstamp) \
+ input/$(DEPDIR)/$(am__dirstamp)
+input/fakehid.$(OBJEXT): input/$(am__dirstamp) \
+ input/$(DEPDIR)/$(am__dirstamp)
+serial/$(am__dirstamp):
+ @$(MKDIR_P) serial
+ @: > serial/$(am__dirstamp)
+serial/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) serial/$(DEPDIR)
+ @: > serial/$(DEPDIR)/$(am__dirstamp)
+serial/main.$(OBJEXT): serial/$(am__dirstamp) \
+ serial/$(DEPDIR)/$(am__dirstamp)
+serial/manager.$(OBJEXT): serial/$(am__dirstamp) \
+ serial/$(DEPDIR)/$(am__dirstamp)
+serial/proxy.$(OBJEXT): serial/$(am__dirstamp) \
+ serial/$(DEPDIR)/$(am__dirstamp)
+serial/port.$(OBJEXT): serial/$(am__dirstamp) \
+ serial/$(DEPDIR)/$(am__dirstamp)
+network/$(am__dirstamp):
+ @$(MKDIR_P) network
+ @: > network/$(am__dirstamp)
+network/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) network/$(DEPDIR)
+ @: > network/$(DEPDIR)/$(am__dirstamp)
+network/main.$(OBJEXT): network/$(am__dirstamp) \
+ network/$(DEPDIR)/$(am__dirstamp)
+network/manager.$(OBJEXT): network/$(am__dirstamp) \
+ network/$(DEPDIR)/$(am__dirstamp)
+network/common.$(OBJEXT): network/$(am__dirstamp) \
+ network/$(DEPDIR)/$(am__dirstamp)
+network/server.$(OBJEXT): network/$(am__dirstamp) \
+ network/$(DEPDIR)/$(am__dirstamp)
+network/connection.$(OBJEXT): network/$(am__dirstamp) \
+ network/$(DEPDIR)/$(am__dirstamp)
+plugins/service.$(OBJEXT): plugins/$(am__dirstamp) \
+ plugins/$(DEPDIR)/$(am__dirstamp)
+attrib/main.$(OBJEXT): attrib/$(am__dirstamp) \
+ attrib/$(DEPDIR)/$(am__dirstamp)
+attrib/manager.$(OBJEXT): attrib/$(am__dirstamp) \
+ attrib/$(DEPDIR)/$(am__dirstamp)
+attrib/client.$(OBJEXT): attrib/$(am__dirstamp) \
+ attrib/$(DEPDIR)/$(am__dirstamp)
+attrib/example.$(OBJEXT): attrib/$(am__dirstamp) \
+ attrib/$(DEPDIR)/$(am__dirstamp)
+health/$(am__dirstamp):
+ @$(MKDIR_P) health
+ @: > health/$(am__dirstamp)
+health/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) health/$(DEPDIR)
+ @: > health/$(DEPDIR)/$(am__dirstamp)
+health/hdp_main.$(OBJEXT): health/$(am__dirstamp) \
+ health/$(DEPDIR)/$(am__dirstamp)
+health/hdp_manager.$(OBJEXT): health/$(am__dirstamp) \
+ health/$(DEPDIR)/$(am__dirstamp)
+health/hdp.$(OBJEXT): health/$(am__dirstamp) \
+ health/$(DEPDIR)/$(am__dirstamp)
+health/hdp_util.$(OBJEXT): health/$(am__dirstamp) \
+ health/$(DEPDIR)/$(am__dirstamp)
+plugins/hciops.$(OBJEXT): plugins/$(am__dirstamp) \
+ plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/mgmtops.$(OBJEXT): plugins/$(am__dirstamp) \
+ plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/hal.$(OBJEXT): plugins/$(am__dirstamp) \
+ plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/formfactor.$(OBJEXT): plugins/$(am__dirstamp) \
+ plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/storage.$(OBJEXT): plugins/$(am__dirstamp) \
+ plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/maemo6.$(OBJEXT): plugins/$(am__dirstamp) \
+ plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/dbusoob.$(OBJEXT): plugins/$(am__dirstamp) \
+ plugins/$(DEPDIR)/$(am__dirstamp)
+health/mcap.$(OBJEXT): health/$(am__dirstamp) \
+ health/$(DEPDIR)/$(am__dirstamp)
+health/mcap_sync.$(OBJEXT): health/$(am__dirstamp) \
+ health/$(DEPDIR)/$(am__dirstamp)
+src/main.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/log.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/rfkill.$(OBJEXT): src/$(am__dirstamp) \
+ src/$(DEPDIR)/$(am__dirstamp)
+src/sdpd-server.$(OBJEXT): src/$(am__dirstamp) \
+ src/$(DEPDIR)/$(am__dirstamp)
+src/sdpd-request.$(OBJEXT): src/$(am__dirstamp) \
+ src/$(DEPDIR)/$(am__dirstamp)
+src/sdpd-service.$(OBJEXT): src/$(am__dirstamp) \
+ src/$(DEPDIR)/$(am__dirstamp)
+src/sdpd-database.$(OBJEXT): src/$(am__dirstamp) \
+ src/$(DEPDIR)/$(am__dirstamp)
+src/attrib-server.$(OBJEXT): src/$(am__dirstamp) \
+ src/$(DEPDIR)/$(am__dirstamp)
+src/sdp-xml.$(OBJEXT): src/$(am__dirstamp) \
+ src/$(DEPDIR)/$(am__dirstamp)
+src/oui.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/plugin.$(OBJEXT): src/$(am__dirstamp) \
+ src/$(DEPDIR)/$(am__dirstamp)
+src/storage.$(OBJEXT): src/$(am__dirstamp) \
+ src/$(DEPDIR)/$(am__dirstamp)
+src/agent.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/error.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/manager.$(OBJEXT): src/$(am__dirstamp) \
+ src/$(DEPDIR)/$(am__dirstamp)
+src/adapter.$(OBJEXT): src/$(am__dirstamp) \
+ src/$(DEPDIR)/$(am__dirstamp)
+src/device.$(OBJEXT): src/$(am__dirstamp) \
+ src/$(DEPDIR)/$(am__dirstamp)
+src/dbus-common.$(OBJEXT): src/$(am__dirstamp) \
+ src/$(DEPDIR)/$(am__dirstamp)
+src/event.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+src/oob.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+audio/telephony.$(OBJEXT): audio/$(am__dirstamp) \
+ audio/$(DEPDIR)/$(am__dirstamp)
+sap/sap.$(OBJEXT): sap/$(am__dirstamp) sap/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd$(EXEEXT): $(src_bluetoothd_OBJECTS) $(src_bluetoothd_DEPENDENCIES) src/$(am__dirstamp)
+ @rm -f src/bluetoothd$(EXEEXT)
+ $(AM_V_CCLD)$(src_bluetoothd_LINK) $(src_bluetoothd_OBJECTS) $(src_bluetoothd_LDADD) $(LIBS)
+test/$(am__dirstamp):
+ @$(MKDIR_P) test
+ @: > test/$(am__dirstamp)
+test/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) test/$(DEPDIR)
+ @: > test/$(DEPDIR)/$(am__dirstamp)
+test/agent.$(OBJEXT): test/$(am__dirstamp) \
+ test/$(DEPDIR)/$(am__dirstamp)
+test/agent$(EXEEXT): $(test_agent_OBJECTS) $(test_agent_DEPENDENCIES) test/$(am__dirstamp)
+ @rm -f test/agent$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_agent_OBJECTS) $(test_agent_LDADD) $(LIBS)
+test/attest.$(OBJEXT): test/$(am__dirstamp) \
+ test/$(DEPDIR)/$(am__dirstamp)
+test/attest$(EXEEXT): $(test_attest_OBJECTS) $(test_attest_DEPENDENCIES) test/$(am__dirstamp)
+ @rm -f test/attest$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_attest_OBJECTS) $(test_attest_LDADD) $(LIBS)
+test/avtest.$(OBJEXT): test/$(am__dirstamp) \
+ test/$(DEPDIR)/$(am__dirstamp)
+test/avtest$(EXEEXT): $(test_avtest_OBJECTS) $(test_avtest_DEPENDENCIES) test/$(am__dirstamp)
+ @rm -f test/avtest$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_avtest_OBJECTS) $(test_avtest_LDADD) $(LIBS)
+test/bdaddr.$(OBJEXT): test/$(am__dirstamp) \
+ test/$(DEPDIR)/$(am__dirstamp)
+test/bdaddr$(EXEEXT): $(test_bdaddr_OBJECTS) $(test_bdaddr_DEPENDENCIES) test/$(am__dirstamp)
+ @rm -f test/bdaddr$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_bdaddr_OBJECTS) $(test_bdaddr_LDADD) $(LIBS)
+test/btiotest.$(OBJEXT): test/$(am__dirstamp) \
+ test/$(DEPDIR)/$(am__dirstamp)
+test/btiotest$(EXEEXT): $(test_btiotest_OBJECTS) $(test_btiotest_DEPENDENCIES) test/$(am__dirstamp)
+ @rm -f test/btiotest$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_btiotest_OBJECTS) $(test_btiotest_LDADD) $(LIBS)
+test/gaptest.$(OBJEXT): test/$(am__dirstamp) \
+ test/$(DEPDIR)/$(am__dirstamp)
+test/gaptest$(EXEEXT): $(test_gaptest_OBJECTS) $(test_gaptest_DEPENDENCIES) test/$(am__dirstamp)
+ @rm -f test/gaptest$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_gaptest_OBJECTS) $(test_gaptest_LDADD) $(LIBS)
+test/hciemu.$(OBJEXT): test/$(am__dirstamp) \
+ test/$(DEPDIR)/$(am__dirstamp)
+test/hciemu$(EXEEXT): $(test_hciemu_OBJECTS) $(test_hciemu_DEPENDENCIES) test/$(am__dirstamp)
+ @rm -f test/hciemu$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_hciemu_OBJECTS) $(test_hciemu_LDADD) $(LIBS)
+test/hstest.$(OBJEXT): test/$(am__dirstamp) \
+ test/$(DEPDIR)/$(am__dirstamp)
+test/hstest$(EXEEXT): $(test_hstest_OBJECTS) $(test_hstest_DEPENDENCIES) test/$(am__dirstamp)
+ @rm -f test/hstest$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_hstest_OBJECTS) $(test_hstest_LDADD) $(LIBS)
+test/ipctest.$(OBJEXT): test/$(am__dirstamp) \
+ test/$(DEPDIR)/$(am__dirstamp)
+test/ipctest$(EXEEXT): $(test_ipctest_OBJECTS) $(test_ipctest_DEPENDENCIES) test/$(am__dirstamp)
+ @rm -f test/ipctest$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_ipctest_OBJECTS) $(test_ipctest_LDADD) $(LIBS)
+test/l2test.$(OBJEXT): test/$(am__dirstamp) \
+ test/$(DEPDIR)/$(am__dirstamp)
+test/l2test$(EXEEXT): $(test_l2test_OBJECTS) $(test_l2test_DEPENDENCIES) test/$(am__dirstamp)
+ @rm -f test/l2test$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_l2test_OBJECTS) $(test_l2test_LDADD) $(LIBS)
+test/lmptest.$(OBJEXT): test/$(am__dirstamp) \
+ test/$(DEPDIR)/$(am__dirstamp)
+test/lmptest$(EXEEXT): $(test_lmptest_OBJECTS) $(test_lmptest_DEPENDENCIES) test/$(am__dirstamp)
+ @rm -f test/lmptest$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_lmptest_OBJECTS) $(test_lmptest_LDADD) $(LIBS)
+test/rctest.$(OBJEXT): test/$(am__dirstamp) \
+ test/$(DEPDIR)/$(am__dirstamp)
+test/rctest$(EXEEXT): $(test_rctest_OBJECTS) $(test_rctest_DEPENDENCIES) test/$(am__dirstamp)
+ @rm -f test/rctest$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_rctest_OBJECTS) $(test_rctest_LDADD) $(LIBS)
+test/scotest.$(OBJEXT): test/$(am__dirstamp) \
+ test/$(DEPDIR)/$(am__dirstamp)
+test/scotest$(EXEEXT): $(test_scotest_OBJECTS) $(test_scotest_DEPENDENCIES) test/$(am__dirstamp)
+ @rm -f test/scotest$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_scotest_OBJECTS) $(test_scotest_LDADD) $(LIBS)
+test/sdptest.$(OBJEXT): test/$(am__dirstamp) \
+ test/$(DEPDIR)/$(am__dirstamp)
+test/sdptest$(EXEEXT): $(test_sdptest_OBJECTS) $(test_sdptest_DEPENDENCIES) test/$(am__dirstamp)
+ @rm -f test/sdptest$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_sdptest_OBJECTS) $(test_sdptest_LDADD) $(LIBS)
+test/test-textfile.$(OBJEXT): test/$(am__dirstamp) \
+ test/$(DEPDIR)/$(am__dirstamp)
+test/test-textfile$(EXEEXT): $(test_test_textfile_OBJECTS) $(test_test_textfile_DEPENDENCIES) test/$(am__dirstamp)
+ @rm -f test/test-textfile$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_test_textfile_OBJECTS) $(test_test_textfile_LDADD) $(LIBS)
+test/uuidtest.$(OBJEXT): test/$(am__dirstamp) \
+ test/$(DEPDIR)/$(am__dirstamp)
+test/uuidtest$(EXEEXT): $(test_uuidtest_OBJECTS) $(test_uuidtest_DEPENDENCIES) test/$(am__dirstamp)
+ @rm -f test/uuidtest$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_uuidtest_OBJECTS) $(test_uuidtest_LDADD) $(LIBS)
+tools/$(am__dirstamp):
+ @$(MKDIR_P) tools
+ @: > tools/$(am__dirstamp)
+tools/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) tools/$(DEPDIR)
+ @: > tools/$(DEPDIR)/$(am__dirstamp)
+tools/avctrl.$(OBJEXT): tools/$(am__dirstamp) \
+ tools/$(DEPDIR)/$(am__dirstamp)
+tools/avctrl$(EXEEXT): $(tools_avctrl_OBJECTS) $(tools_avctrl_DEPENDENCIES) tools/$(am__dirstamp)
+ @rm -f tools/avctrl$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tools_avctrl_OBJECTS) $(tools_avctrl_LDADD) $(LIBS)
+tools/avinfo.$(OBJEXT): tools/$(am__dirstamp) \
+ tools/$(DEPDIR)/$(am__dirstamp)
+tools/avinfo$(EXEEXT): $(tools_avinfo_OBJECTS) $(tools_avinfo_DEPENDENCIES) tools/$(am__dirstamp)
+ @rm -f tools/avinfo$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tools_avinfo_OBJECTS) $(tools_avinfo_LDADD) $(LIBS)
+tools/bccmd.$(OBJEXT): tools/$(am__dirstamp) \
+ tools/$(DEPDIR)/$(am__dirstamp)
+tools/csr.$(OBJEXT): tools/$(am__dirstamp) \
+ tools/$(DEPDIR)/$(am__dirstamp)
+tools/csr_hci.$(OBJEXT): tools/$(am__dirstamp) \
+ tools/$(DEPDIR)/$(am__dirstamp)
+tools/csr_h4.$(OBJEXT): tools/$(am__dirstamp) \
+ tools/$(DEPDIR)/$(am__dirstamp)
+tools/csr_3wire.$(OBJEXT): tools/$(am__dirstamp) \
+ tools/$(DEPDIR)/$(am__dirstamp)
+tools/csr_bcsp.$(OBJEXT): tools/$(am__dirstamp) \
+ tools/$(DEPDIR)/$(am__dirstamp)
+tools/ubcsp.$(OBJEXT): tools/$(am__dirstamp) \
+ tools/$(DEPDIR)/$(am__dirstamp)
+tools/csr_usb.$(OBJEXT): tools/$(am__dirstamp) \
+ tools/$(DEPDIR)/$(am__dirstamp)
+tools/bccmd$(EXEEXT): $(tools_bccmd_OBJECTS) $(tools_bccmd_DEPENDENCIES) tools/$(am__dirstamp)
+ @rm -f tools/bccmd$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tools_bccmd_OBJECTS) $(tools_bccmd_LDADD) $(LIBS)
+tools/ciptool.$(OBJEXT): tools/$(am__dirstamp) \
+ tools/$(DEPDIR)/$(am__dirstamp)
+tools/ciptool$(EXEEXT): $(tools_ciptool_OBJECTS) $(tools_ciptool_DEPENDENCIES) tools/$(am__dirstamp)
+ @rm -f tools/ciptool$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tools_ciptool_OBJECTS) $(tools_ciptool_LDADD) $(LIBS)
+tools/dfubabel.$(OBJEXT): tools/$(am__dirstamp) \
+ tools/$(DEPDIR)/$(am__dirstamp)
+tools/dfubabel$(EXEEXT): $(tools_dfubabel_OBJECTS) $(tools_dfubabel_DEPENDENCIES) tools/$(am__dirstamp)
+ @rm -f tools/dfubabel$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tools_dfubabel_OBJECTS) $(tools_dfubabel_LDADD) $(LIBS)
+tools/dfutool.$(OBJEXT): tools/$(am__dirstamp) \
+ tools/$(DEPDIR)/$(am__dirstamp)
+tools/dfu.$(OBJEXT): tools/$(am__dirstamp) \
+ tools/$(DEPDIR)/$(am__dirstamp)
+tools/dfutool$(EXEEXT): $(tools_dfutool_OBJECTS) $(tools_dfutool_DEPENDENCIES) tools/$(am__dirstamp)
+ @rm -f tools/dfutool$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tools_dfutool_OBJECTS) $(tools_dfutool_LDADD) $(LIBS)
+tools/hciattach.$(OBJEXT): tools/$(am__dirstamp) \
+ tools/$(DEPDIR)/$(am__dirstamp)
+tools/hciattach_st.$(OBJEXT): tools/$(am__dirstamp) \
+ tools/$(DEPDIR)/$(am__dirstamp)
+tools/hciattach_ti.$(OBJEXT): tools/$(am__dirstamp) \
+ tools/$(DEPDIR)/$(am__dirstamp)
+tools/hciattach_tialt.$(OBJEXT): tools/$(am__dirstamp) \
+ tools/$(DEPDIR)/$(am__dirstamp)
+tools/hciattach_ath3k.$(OBJEXT): tools/$(am__dirstamp) \
+ tools/$(DEPDIR)/$(am__dirstamp)
+tools/hciattach_qualcomm.$(OBJEXT): tools/$(am__dirstamp) \
+ tools/$(DEPDIR)/$(am__dirstamp)
+tools/hciattach$(EXEEXT): $(tools_hciattach_OBJECTS) $(tools_hciattach_DEPENDENCIES) tools/$(am__dirstamp)
+ @rm -f tools/hciattach$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tools_hciattach_OBJECTS) $(tools_hciattach_LDADD) $(LIBS)
+tools/hciconfig.$(OBJEXT): tools/$(am__dirstamp) \
+ tools/$(DEPDIR)/$(am__dirstamp)
+tools/hciconfig$(EXEEXT): $(tools_hciconfig_OBJECTS) $(tools_hciconfig_DEPENDENCIES) tools/$(am__dirstamp)
+ @rm -f tools/hciconfig$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tools_hciconfig_OBJECTS) $(tools_hciconfig_LDADD) $(LIBS)
+tools/hcieventmask.$(OBJEXT): tools/$(am__dirstamp) \
+ tools/$(DEPDIR)/$(am__dirstamp)
+tools/hcieventmask$(EXEEXT): $(tools_hcieventmask_OBJECTS) $(tools_hcieventmask_DEPENDENCIES) tools/$(am__dirstamp)
+ @rm -f tools/hcieventmask$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tools_hcieventmask_OBJECTS) $(tools_hcieventmask_LDADD) $(LIBS)
+tools/hcisecfilter.$(OBJEXT): tools/$(am__dirstamp) \
+ tools/$(DEPDIR)/$(am__dirstamp)
+tools/hcisecfilter$(EXEEXT): $(tools_hcisecfilter_OBJECTS) $(tools_hcisecfilter_DEPENDENCIES) tools/$(am__dirstamp)
+ @rm -f tools/hcisecfilter$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tools_hcisecfilter_OBJECTS) $(tools_hcisecfilter_LDADD) $(LIBS)
+tools/hcitool.$(OBJEXT): tools/$(am__dirstamp) \
+ tools/$(DEPDIR)/$(am__dirstamp)
+tools/hcitool$(EXEEXT): $(tools_hcitool_OBJECTS) $(tools_hcitool_DEPENDENCIES) tools/$(am__dirstamp)
+ @rm -f tools/hcitool$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tools_hcitool_OBJECTS) $(tools_hcitool_LDADD) $(LIBS)
+tools/hid2hci.$(OBJEXT): tools/$(am__dirstamp) \
+ tools/$(DEPDIR)/$(am__dirstamp)
+tools/hid2hci$(EXEEXT): $(tools_hid2hci_OBJECTS) $(tools_hid2hci_DEPENDENCIES) tools/$(am__dirstamp)
+ @rm -f tools/hid2hci$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tools_hid2hci_OBJECTS) $(tools_hid2hci_LDADD) $(LIBS)
+tools/l2ping.$(OBJEXT): tools/$(am__dirstamp) \
+ tools/$(DEPDIR)/$(am__dirstamp)
+tools/l2ping$(EXEEXT): $(tools_l2ping_OBJECTS) $(tools_l2ping_DEPENDENCIES) tools/$(am__dirstamp)
+ @rm -f tools/l2ping$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tools_l2ping_OBJECTS) $(tools_l2ping_LDADD) $(LIBS)
+tools/ppporc.$(OBJEXT): tools/$(am__dirstamp) \
+ tools/$(DEPDIR)/$(am__dirstamp)
+tools/ppporc$(EXEEXT): $(tools_ppporc_OBJECTS) $(tools_ppporc_DEPENDENCIES) tools/$(am__dirstamp)
+ @rm -f tools/ppporc$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tools_ppporc_OBJECTS) $(tools_ppporc_LDADD) $(LIBS)
+tools/rfcomm.$(OBJEXT): tools/$(am__dirstamp) \
+ tools/$(DEPDIR)/$(am__dirstamp)
+tools/parser.h: tools/parser.c
+ @if test ! -f $@; then \
+ rm -f tools/parser.c; \
+ $(MAKE) $(AM_MAKEFLAGS) tools/parser.c; \
+ else :; fi
+tools/parser.$(OBJEXT): tools/$(am__dirstamp) \
+ tools/$(DEPDIR)/$(am__dirstamp)
+tools/lexer.$(OBJEXT): tools/$(am__dirstamp) \
+ tools/$(DEPDIR)/$(am__dirstamp)
+tools/kword.$(OBJEXT): tools/$(am__dirstamp) \
+ tools/$(DEPDIR)/$(am__dirstamp)
+tools/rfcomm$(EXEEXT): $(tools_rfcomm_OBJECTS) $(tools_rfcomm_DEPENDENCIES) tools/$(am__dirstamp)
+ @rm -f tools/rfcomm$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tools_rfcomm_OBJECTS) $(tools_rfcomm_LDADD) $(LIBS)
+tools/sdptool.$(OBJEXT): tools/$(am__dirstamp) \
+ tools/$(DEPDIR)/$(am__dirstamp)
+tools/sdptool$(EXEEXT): $(tools_sdptool_OBJECTS) $(tools_sdptool_DEPENDENCIES) tools/$(am__dirstamp)
+ @rm -f tools/sdptool$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tools_sdptool_OBJECTS) $(tools_sdptool_LDADD) $(LIBS)
+tracer/$(am__dirstamp):
+ @$(MKDIR_P) tracer
+ @: > tracer/$(am__dirstamp)
+tracer/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) tracer/$(DEPDIR)
+ @: > tracer/$(DEPDIR)/$(am__dirstamp)
+tracer/main.$(OBJEXT): tracer/$(am__dirstamp) \
+ tracer/$(DEPDIR)/$(am__dirstamp)
+tracer/hcitrace$(EXEEXT): $(tracer_hcitrace_OBJECTS) $(tracer_hcitrace_DEPENDENCIES) tracer/$(am__dirstamp)
+ @rm -f tracer/hcitrace$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tracer_hcitrace_OBJECTS) $(tracer_hcitrace_LDADD) $(LIBS)
+install-dist_udevSCRIPTS: $(dist_udev_SCRIPTS)
+ @$(NORMAL_INSTALL)
+ test -z "$(udevdir)" || $(MKDIR_P) "$(DESTDIR)$(udevdir)"
+ @list='$(dist_udev_SCRIPTS)'; test -n "$(udevdir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n' \
+ -e 'h;s|.*|.|' \
+ -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) { files[d] = files[d] " " $$1; \
+ if (++n[d] == $(am__install_max)) { \
+ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \
+ else { print "f", d "/" $$4, $$1 } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(udevdir)$$dir'"; \
+ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(udevdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-dist_udevSCRIPTS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(dist_udev_SCRIPTS)'; test -n "$(udevdir)" || exit 0; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 's,.*/,,;$(transform)'`; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(udevdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(udevdir)" && rm -f $$files
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+ -rm -f attrib/att.$(OBJEXT)
+ -rm -f attrib/client.$(OBJEXT)
+ -rm -f attrib/example.$(OBJEXT)
+ -rm -f attrib/gatt.$(OBJEXT)
+ -rm -f attrib/gattrib.$(OBJEXT)
+ -rm -f attrib/gatttool.$(OBJEXT)
+ -rm -f attrib/interactive.$(OBJEXT)
+ -rm -f attrib/main.$(OBJEXT)
+ -rm -f attrib/manager.$(OBJEXT)
+ -rm -f attrib/utils.$(OBJEXT)
+ -rm -f audio/a2dp.$(OBJEXT)
+ -rm -f audio/audio_libasound_module_ctl_bluetooth_la-ctl_bluetooth.$(OBJEXT)
+ -rm -f audio/audio_libasound_module_ctl_bluetooth_la-ctl_bluetooth.lo
+ -rm -f audio/audio_libasound_module_ctl_bluetooth_la-ipc.$(OBJEXT)
+ -rm -f audio/audio_libasound_module_ctl_bluetooth_la-ipc.lo
+ -rm -f audio/audio_libasound_module_pcm_bluetooth_la-ipc.$(OBJEXT)
+ -rm -f audio/audio_libasound_module_pcm_bluetooth_la-ipc.lo
+ -rm -f audio/audio_libasound_module_pcm_bluetooth_la-pcm_bluetooth.$(OBJEXT)
+ -rm -f audio/audio_libasound_module_pcm_bluetooth_la-pcm_bluetooth.lo
+ -rm -f audio/audio_libgstbluetooth_la-gsta2dpsink.$(OBJEXT)
+ -rm -f audio/audio_libgstbluetooth_la-gsta2dpsink.lo
+ -rm -f audio/audio_libgstbluetooth_la-gstavdtpsink.$(OBJEXT)
+ -rm -f audio/audio_libgstbluetooth_la-gstavdtpsink.lo
+ -rm -f audio/audio_libgstbluetooth_la-gstbluetooth.$(OBJEXT)
+ -rm -f audio/audio_libgstbluetooth_la-gstbluetooth.lo
+ -rm -f audio/audio_libgstbluetooth_la-gstrtpsbcpay.$(OBJEXT)
+ -rm -f audio/audio_libgstbluetooth_la-gstrtpsbcpay.lo
+ -rm -f audio/audio_libgstbluetooth_la-gstsbcdec.$(OBJEXT)
+ -rm -f audio/audio_libgstbluetooth_la-gstsbcdec.lo
+ -rm -f audio/audio_libgstbluetooth_la-gstsbcenc.$(OBJEXT)
+ -rm -f audio/audio_libgstbluetooth_la-gstsbcenc.lo
+ -rm -f audio/audio_libgstbluetooth_la-gstsbcparse.$(OBJEXT)
+ -rm -f audio/audio_libgstbluetooth_la-gstsbcparse.lo
+ -rm -f audio/audio_libgstbluetooth_la-gstsbcutil.$(OBJEXT)
+ -rm -f audio/audio_libgstbluetooth_la-gstsbcutil.lo
+ -rm -f audio/audio_libgstbluetooth_la-ipc.$(OBJEXT)
+ -rm -f audio/audio_libgstbluetooth_la-ipc.lo
+ -rm -f audio/avdtp.$(OBJEXT)
+ -rm -f audio/control.$(OBJEXT)
+ -rm -f audio/device.$(OBJEXT)
+ -rm -f audio/gateway.$(OBJEXT)
+ -rm -f audio/headset.$(OBJEXT)
+ -rm -f audio/ipc.$(OBJEXT)
+ -rm -f audio/main.$(OBJEXT)
+ -rm -f audio/manager.$(OBJEXT)
+ -rm -f audio/media.$(OBJEXT)
+ -rm -f audio/sink.$(OBJEXT)
+ -rm -f audio/source.$(OBJEXT)
+ -rm -f audio/telephony-dummy.$(OBJEXT)
+ -rm -f audio/telephony-maemo5.$(OBJEXT)
+ -rm -f audio/telephony-maemo6.$(OBJEXT)
+ -rm -f audio/telephony-ofono.$(OBJEXT)
+ -rm -f audio/telephony.$(OBJEXT)
+ -rm -f audio/transport.$(OBJEXT)
+ -rm -f audio/unix.$(OBJEXT)
+ -rm -f btio/btio.$(OBJEXT)
+ -rm -f compat/bnep.$(OBJEXT)
+ -rm -f compat/dun.$(OBJEXT)
+ -rm -f compat/dund.$(OBJEXT)
+ -rm -f compat/fakehid.$(OBJEXT)
+ -rm -f compat/hidd.$(OBJEXT)
+ -rm -f compat/msdun.$(OBJEXT)
+ -rm -f compat/pand.$(OBJEXT)
+ -rm -f compat/sdp.$(OBJEXT)
+ -rm -f cups/hcrp.$(OBJEXT)
+ -rm -f cups/main.$(OBJEXT)
+ -rm -f cups/sdp.$(OBJEXT)
+ -rm -f cups/spp.$(OBJEXT)
+ -rm -f gdbus/mainloop.$(OBJEXT)
+ -rm -f gdbus/object.$(OBJEXT)
+ -rm -f gdbus/polkit.$(OBJEXT)
+ -rm -f gdbus/watch.$(OBJEXT)
+ -rm -f health/hdp.$(OBJEXT)
+ -rm -f health/hdp_main.$(OBJEXT)
+ -rm -f health/hdp_manager.$(OBJEXT)
+ -rm -f health/hdp_util.$(OBJEXT)
+ -rm -f health/mcap.$(OBJEXT)
+ -rm -f health/mcap_sync.$(OBJEXT)
+ -rm -f input/device.$(OBJEXT)
+ -rm -f input/fakehid.$(OBJEXT)
+ -rm -f input/main.$(OBJEXT)
+ -rm -f input/manager.$(OBJEXT)
+ -rm -f input/server.$(OBJEXT)
+ -rm -f lib/bluetooth.$(OBJEXT)
+ -rm -f lib/bluetooth.lo
+ -rm -f lib/hci.$(OBJEXT)
+ -rm -f lib/hci.lo
+ -rm -f lib/sdp.$(OBJEXT)
+ -rm -f lib/sdp.lo
+ -rm -f lib/uuid.$(OBJEXT)
+ -rm -f lib/uuid.lo
+ -rm -f network/common.$(OBJEXT)
+ -rm -f network/connection.$(OBJEXT)
+ -rm -f network/main.$(OBJEXT)
+ -rm -f network/manager.$(OBJEXT)
+ -rm -f network/server.$(OBJEXT)
+ -rm -f plugins/dbusoob.$(OBJEXT)
+ -rm -f plugins/echo.$(OBJEXT)
+ -rm -f plugins/formfactor.$(OBJEXT)
+ -rm -f plugins/hal.$(OBJEXT)
+ -rm -f plugins/hciops.$(OBJEXT)
+ -rm -f plugins/maemo6.$(OBJEXT)
+ -rm -f plugins/mgmtops.$(OBJEXT)
+ -rm -f plugins/pnat.$(OBJEXT)
+ -rm -f plugins/service.$(OBJEXT)
+ -rm -f plugins/storage.$(OBJEXT)
+ -rm -f sap/main.$(OBJEXT)
+ -rm -f sap/manager.$(OBJEXT)
+ -rm -f sap/sap-dummy.$(OBJEXT)
+ -rm -f sap/sap.$(OBJEXT)
+ -rm -f sap/server.$(OBJEXT)
+ -rm -f sbc/sbc_libsbc_la-sbc.$(OBJEXT)
+ -rm -f sbc/sbc_libsbc_la-sbc.lo
+ -rm -f sbc/sbc_libsbc_la-sbc_primitives.$(OBJEXT)
+ -rm -f sbc/sbc_libsbc_la-sbc_primitives.lo
+ -rm -f sbc/sbc_libsbc_la-sbc_primitives_armv6.$(OBJEXT)
+ -rm -f sbc/sbc_libsbc_la-sbc_primitives_armv6.lo
+ -rm -f sbc/sbc_libsbc_la-sbc_primitives_iwmmxt.$(OBJEXT)
+ -rm -f sbc/sbc_libsbc_la-sbc_primitives_iwmmxt.lo
+ -rm -f sbc/sbc_libsbc_la-sbc_primitives_mmx.$(OBJEXT)
+ -rm -f sbc/sbc_libsbc_la-sbc_primitives_mmx.lo
+ -rm -f sbc/sbc_libsbc_la-sbc_primitives_neon.$(OBJEXT)
+ -rm -f sbc/sbc_libsbc_la-sbc_primitives_neon.lo
+ -rm -f sbc/sbcdec.$(OBJEXT)
+ -rm -f sbc/sbcenc.$(OBJEXT)
+ -rm -f sbc/sbcinfo.$(OBJEXT)
+ -rm -f sbc/sbctester.$(OBJEXT)
+ -rm -f serial/main.$(OBJEXT)
+ -rm -f serial/manager.$(OBJEXT)
+ -rm -f serial/port.$(OBJEXT)
+ -rm -f serial/proxy.$(OBJEXT)
+ -rm -f src/adapter.$(OBJEXT)
+ -rm -f src/agent.$(OBJEXT)
+ -rm -f src/attrib-server.$(OBJEXT)
+ -rm -f src/dbus-common.$(OBJEXT)
+ -rm -f src/device.$(OBJEXT)
+ -rm -f src/error.$(OBJEXT)
+ -rm -f src/event.$(OBJEXT)
+ -rm -f src/glib-helper.$(OBJEXT)
+ -rm -f src/log.$(OBJEXT)
+ -rm -f src/main.$(OBJEXT)
+ -rm -f src/manager.$(OBJEXT)
+ -rm -f src/oob.$(OBJEXT)
+ -rm -f src/oui.$(OBJEXT)
+ -rm -f src/plugin.$(OBJEXT)
+ -rm -f src/rfkill.$(OBJEXT)
+ -rm -f src/sdp-xml.$(OBJEXT)
+ -rm -f src/sdpd-database.$(OBJEXT)
+ -rm -f src/sdpd-request.$(OBJEXT)
+ -rm -f src/sdpd-server.$(OBJEXT)
+ -rm -f src/sdpd-service.$(OBJEXT)
+ -rm -f src/storage.$(OBJEXT)
+ -rm -f src/textfile.$(OBJEXT)
+ -rm -f test/agent.$(OBJEXT)
+ -rm -f test/attest.$(OBJEXT)
+ -rm -f test/avtest.$(OBJEXT)
+ -rm -f test/bdaddr.$(OBJEXT)
+ -rm -f test/btiotest.$(OBJEXT)
+ -rm -f test/gaptest.$(OBJEXT)
+ -rm -f test/hciemu.$(OBJEXT)
+ -rm -f test/hstest.$(OBJEXT)
+ -rm -f test/ipctest.$(OBJEXT)
+ -rm -f test/l2test.$(OBJEXT)
+ -rm -f test/lmptest.$(OBJEXT)
+ -rm -f test/rctest.$(OBJEXT)
+ -rm -f test/scotest.$(OBJEXT)
+ -rm -f test/sdptest.$(OBJEXT)
+ -rm -f test/test-textfile.$(OBJEXT)
+ -rm -f test/uuidtest.$(OBJEXT)
+ -rm -f tools/avctrl.$(OBJEXT)
+ -rm -f tools/avinfo.$(OBJEXT)
+ -rm -f tools/bccmd.$(OBJEXT)
+ -rm -f tools/ciptool.$(OBJEXT)
+ -rm -f tools/csr.$(OBJEXT)
+ -rm -f tools/csr_3wire.$(OBJEXT)
+ -rm -f tools/csr_bcsp.$(OBJEXT)
+ -rm -f tools/csr_h4.$(OBJEXT)
+ -rm -f tools/csr_hci.$(OBJEXT)
+ -rm -f tools/csr_usb.$(OBJEXT)
+ -rm -f tools/dfu.$(OBJEXT)
+ -rm -f tools/dfubabel.$(OBJEXT)
+ -rm -f tools/dfutool.$(OBJEXT)
+ -rm -f tools/hciattach.$(OBJEXT)
+ -rm -f tools/hciattach_ath3k.$(OBJEXT)
+ -rm -f tools/hciattach_qualcomm.$(OBJEXT)
+ -rm -f tools/hciattach_st.$(OBJEXT)
+ -rm -f tools/hciattach_ti.$(OBJEXT)
+ -rm -f tools/hciattach_tialt.$(OBJEXT)
+ -rm -f tools/hciconfig.$(OBJEXT)
+ -rm -f tools/hcieventmask.$(OBJEXT)
+ -rm -f tools/hcisecfilter.$(OBJEXT)
+ -rm -f tools/hcitool.$(OBJEXT)
+ -rm -f tools/hid2hci.$(OBJEXT)
+ -rm -f tools/kword.$(OBJEXT)
+ -rm -f tools/l2ping.$(OBJEXT)
+ -rm -f tools/lexer.$(OBJEXT)
+ -rm -f tools/parser.$(OBJEXT)
+ -rm -f tools/ppporc.$(OBJEXT)
+ -rm -f tools/rfcomm.$(OBJEXT)
+ -rm -f tools/sdptool.$(OBJEXT)
+ -rm -f tools/ubcsp.$(OBJEXT)
+ -rm -f tracer/main.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/att.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/client.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/example.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/gatt.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/gattrib.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/gatttool.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/interactive.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/manager.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/utils.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/a2dp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/audio_libasound_module_ctl_bluetooth_la-ctl_bluetooth.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/audio_libasound_module_ctl_bluetooth_la-ipc.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/audio_libasound_module_pcm_bluetooth_la-ipc.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/audio_libasound_module_pcm_bluetooth_la-pcm_bluetooth.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/audio_libgstbluetooth_la-gsta2dpsink.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/audio_libgstbluetooth_la-gstavdtpsink.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/audio_libgstbluetooth_la-gstbluetooth.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/audio_libgstbluetooth_la-gstrtpsbcpay.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/audio_libgstbluetooth_la-gstsbcdec.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/audio_libgstbluetooth_la-gstsbcenc.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/audio_libgstbluetooth_la-gstsbcparse.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/audio_libgstbluetooth_la-gstsbcutil.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/audio_libgstbluetooth_la-ipc.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/avdtp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/control.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/device.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/gateway.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/headset.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/ipc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/manager.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/media.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/sink.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/source.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/telephony-dummy.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/telephony-maemo5.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/telephony-maemo6.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/telephony-ofono.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/telephony.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/transport.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/unix.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@btio/$(DEPDIR)/btio.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/bnep.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/dun.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/dund.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/fakehid.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/hidd.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/msdun.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/pand.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/sdp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@cups/$(DEPDIR)/hcrp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@cups/$(DEPDIR)/main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@cups/$(DEPDIR)/sdp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@cups/$(DEPDIR)/spp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gdbus/$(DEPDIR)/mainloop.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gdbus/$(DEPDIR)/object.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gdbus/$(DEPDIR)/polkit.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gdbus/$(DEPDIR)/watch.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@health/$(DEPDIR)/hdp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@health/$(DEPDIR)/hdp_main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@health/$(DEPDIR)/hdp_manager.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@health/$(DEPDIR)/hdp_util.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@health/$(DEPDIR)/mcap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@health/$(DEPDIR)/mcap_sync.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@input/$(DEPDIR)/device.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@input/$(DEPDIR)/fakehid.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@input/$(DEPDIR)/main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@input/$(DEPDIR)/manager.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@input/$(DEPDIR)/server.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/bluetooth.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/hci.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/sdp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/uuid.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@network/$(DEPDIR)/common.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@network/$(DEPDIR)/connection.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@network/$(DEPDIR)/main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@network/$(DEPDIR)/manager.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@network/$(DEPDIR)/server.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/dbusoob.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/echo.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/formfactor.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/hal.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/hciops.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/maemo6.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/mgmtops.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/pnat.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/service.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/storage.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@sap/$(DEPDIR)/main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@sap/$(DEPDIR)/manager.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@sap/$(DEPDIR)/sap-dummy.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@sap/$(DEPDIR)/sap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@sap/$(DEPDIR)/server.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@sbc/$(DEPDIR)/sbc_libsbc_la-sbc.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives_armv6.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives_iwmmxt.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives_mmx.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives_neon.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@sbc/$(DEPDIR)/sbcdec.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@sbc/$(DEPDIR)/sbcenc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@sbc/$(DEPDIR)/sbcinfo.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@sbc/$(DEPDIR)/sbctester.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@serial/$(DEPDIR)/main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@serial/$(DEPDIR)/manager.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@serial/$(DEPDIR)/port.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@serial/$(DEPDIR)/proxy.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/adapter.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/agent.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/attrib-server.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/dbus-common.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/device.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/error.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/event.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/glib-helper.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/log.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/manager.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/oob.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/oui.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/plugin.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/rfkill.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/sdp-xml.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/sdpd-database.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/sdpd-request.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/sdpd-server.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/sdpd-service.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/storage.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/textfile.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@test/$(DEPDIR)/agent.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@test/$(DEPDIR)/attest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@test/$(DEPDIR)/avtest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@test/$(DEPDIR)/bdaddr.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@test/$(DEPDIR)/btiotest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@test/$(DEPDIR)/gaptest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@test/$(DEPDIR)/hciemu.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@test/$(DEPDIR)/hstest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@test/$(DEPDIR)/ipctest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@test/$(DEPDIR)/l2test.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@test/$(DEPDIR)/lmptest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@test/$(DEPDIR)/rctest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@test/$(DEPDIR)/scotest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@test/$(DEPDIR)/sdptest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@test/$(DEPDIR)/test-textfile.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@test/$(DEPDIR)/uuidtest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/avctrl.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/avinfo.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/bccmd.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/ciptool.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/csr.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/csr_3wire.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/csr_bcsp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/csr_h4.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/csr_hci.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/csr_usb.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/dfu.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/dfubabel.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/dfutool.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hciattach.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hciattach_ath3k.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hciattach_qualcomm.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hciattach_st.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hciattach_ti.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hciattach_tialt.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hciconfig.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hcieventmask.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hcisecfilter.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hcitool.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hid2hci.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/kword.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/l2ping.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lexer.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/parser.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/ppporc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/rfcomm.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/sdptool.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/ubcsp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tracer/$(DEPDIR)/main.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+audio/audio_libasound_module_ctl_bluetooth_la-ctl_bluetooth.lo: audio/ctl_bluetooth.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libasound_module_ctl_bluetooth_la_CFLAGS) $(CFLAGS) -MT audio/audio_libasound_module_ctl_bluetooth_la-ctl_bluetooth.lo -MD -MP -MF audio/$(DEPDIR)/audio_libasound_module_ctl_bluetooth_la-ctl_bluetooth.Tpo -c -o audio/audio_libasound_module_ctl_bluetooth_la-ctl_bluetooth.lo `test -f 'audio/ctl_bluetooth.c' || echo '$(srcdir)/'`audio/ctl_bluetooth.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) audio/$(DEPDIR)/audio_libasound_module_ctl_bluetooth_la-ctl_bluetooth.Tpo audio/$(DEPDIR)/audio_libasound_module_ctl_bluetooth_la-ctl_bluetooth.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='audio/ctl_bluetooth.c' object='audio/audio_libasound_module_ctl_bluetooth_la-ctl_bluetooth.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libasound_module_ctl_bluetooth_la_CFLAGS) $(CFLAGS) -c -o audio/audio_libasound_module_ctl_bluetooth_la-ctl_bluetooth.lo `test -f 'audio/ctl_bluetooth.c' || echo '$(srcdir)/'`audio/ctl_bluetooth.c
+
+audio/audio_libasound_module_ctl_bluetooth_la-ipc.lo: audio/ipc.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libasound_module_ctl_bluetooth_la_CFLAGS) $(CFLAGS) -MT audio/audio_libasound_module_ctl_bluetooth_la-ipc.lo -MD -MP -MF audio/$(DEPDIR)/audio_libasound_module_ctl_bluetooth_la-ipc.Tpo -c -o audio/audio_libasound_module_ctl_bluetooth_la-ipc.lo `test -f 'audio/ipc.c' || echo '$(srcdir)/'`audio/ipc.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) audio/$(DEPDIR)/audio_libasound_module_ctl_bluetooth_la-ipc.Tpo audio/$(DEPDIR)/audio_libasound_module_ctl_bluetooth_la-ipc.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='audio/ipc.c' object='audio/audio_libasound_module_ctl_bluetooth_la-ipc.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libasound_module_ctl_bluetooth_la_CFLAGS) $(CFLAGS) -c -o audio/audio_libasound_module_ctl_bluetooth_la-ipc.lo `test -f 'audio/ipc.c' || echo '$(srcdir)/'`audio/ipc.c
+
+audio/audio_libasound_module_pcm_bluetooth_la-pcm_bluetooth.lo: audio/pcm_bluetooth.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libasound_module_pcm_bluetooth_la_CFLAGS) $(CFLAGS) -MT audio/audio_libasound_module_pcm_bluetooth_la-pcm_bluetooth.lo -MD -MP -MF audio/$(DEPDIR)/audio_libasound_module_pcm_bluetooth_la-pcm_bluetooth.Tpo -c -o audio/audio_libasound_module_pcm_bluetooth_la-pcm_bluetooth.lo `test -f 'audio/pcm_bluetooth.c' || echo '$(srcdir)/'`audio/pcm_bluetooth.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) audio/$(DEPDIR)/audio_libasound_module_pcm_bluetooth_la-pcm_bluetooth.Tpo audio/$(DEPDIR)/audio_libasound_module_pcm_bluetooth_la-pcm_bluetooth.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='audio/pcm_bluetooth.c' object='audio/audio_libasound_module_pcm_bluetooth_la-pcm_bluetooth.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libasound_module_pcm_bluetooth_la_CFLAGS) $(CFLAGS) -c -o audio/audio_libasound_module_pcm_bluetooth_la-pcm_bluetooth.lo `test -f 'audio/pcm_bluetooth.c' || echo '$(srcdir)/'`audio/pcm_bluetooth.c
+
+audio/audio_libasound_module_pcm_bluetooth_la-ipc.lo: audio/ipc.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libasound_module_pcm_bluetooth_la_CFLAGS) $(CFLAGS) -MT audio/audio_libasound_module_pcm_bluetooth_la-ipc.lo -MD -MP -MF audio/$(DEPDIR)/audio_libasound_module_pcm_bluetooth_la-ipc.Tpo -c -o audio/audio_libasound_module_pcm_bluetooth_la-ipc.lo `test -f 'audio/ipc.c' || echo '$(srcdir)/'`audio/ipc.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) audio/$(DEPDIR)/audio_libasound_module_pcm_bluetooth_la-ipc.Tpo audio/$(DEPDIR)/audio_libasound_module_pcm_bluetooth_la-ipc.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='audio/ipc.c' object='audio/audio_libasound_module_pcm_bluetooth_la-ipc.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libasound_module_pcm_bluetooth_la_CFLAGS) $(CFLAGS) -c -o audio/audio_libasound_module_pcm_bluetooth_la-ipc.lo `test -f 'audio/ipc.c' || echo '$(srcdir)/'`audio/ipc.c
+
+audio/audio_libgstbluetooth_la-gstbluetooth.lo: audio/gstbluetooth.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) -MT audio/audio_libgstbluetooth_la-gstbluetooth.lo -MD -MP -MF audio/$(DEPDIR)/audio_libgstbluetooth_la-gstbluetooth.Tpo -c -o audio/audio_libgstbluetooth_la-gstbluetooth.lo `test -f 'audio/gstbluetooth.c' || echo '$(srcdir)/'`audio/gstbluetooth.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) audio/$(DEPDIR)/audio_libgstbluetooth_la-gstbluetooth.Tpo audio/$(DEPDIR)/audio_libgstbluetooth_la-gstbluetooth.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='audio/gstbluetooth.c' object='audio/audio_libgstbluetooth_la-gstbluetooth.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) -c -o audio/audio_libgstbluetooth_la-gstbluetooth.lo `test -f 'audio/gstbluetooth.c' || echo '$(srcdir)/'`audio/gstbluetooth.c
+
+audio/audio_libgstbluetooth_la-gstsbcenc.lo: audio/gstsbcenc.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) -MT audio/audio_libgstbluetooth_la-gstsbcenc.lo -MD -MP -MF audio/$(DEPDIR)/audio_libgstbluetooth_la-gstsbcenc.Tpo -c -o audio/audio_libgstbluetooth_la-gstsbcenc.lo `test -f 'audio/gstsbcenc.c' || echo '$(srcdir)/'`audio/gstsbcenc.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) audio/$(DEPDIR)/audio_libgstbluetooth_la-gstsbcenc.Tpo audio/$(DEPDIR)/audio_libgstbluetooth_la-gstsbcenc.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='audio/gstsbcenc.c' object='audio/audio_libgstbluetooth_la-gstsbcenc.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) -c -o audio/audio_libgstbluetooth_la-gstsbcenc.lo `test -f 'audio/gstsbcenc.c' || echo '$(srcdir)/'`audio/gstsbcenc.c
+
+audio/audio_libgstbluetooth_la-gstsbcdec.lo: audio/gstsbcdec.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) -MT audio/audio_libgstbluetooth_la-gstsbcdec.lo -MD -MP -MF audio/$(DEPDIR)/audio_libgstbluetooth_la-gstsbcdec.Tpo -c -o audio/audio_libgstbluetooth_la-gstsbcdec.lo `test -f 'audio/gstsbcdec.c' || echo '$(srcdir)/'`audio/gstsbcdec.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) audio/$(DEPDIR)/audio_libgstbluetooth_la-gstsbcdec.Tpo audio/$(DEPDIR)/audio_libgstbluetooth_la-gstsbcdec.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='audio/gstsbcdec.c' object='audio/audio_libgstbluetooth_la-gstsbcdec.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) -c -o audio/audio_libgstbluetooth_la-gstsbcdec.lo `test -f 'audio/gstsbcdec.c' || echo '$(srcdir)/'`audio/gstsbcdec.c
+
+audio/audio_libgstbluetooth_la-gstsbcparse.lo: audio/gstsbcparse.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) -MT audio/audio_libgstbluetooth_la-gstsbcparse.lo -MD -MP -MF audio/$(DEPDIR)/audio_libgstbluetooth_la-gstsbcparse.Tpo -c -o audio/audio_libgstbluetooth_la-gstsbcparse.lo `test -f 'audio/gstsbcparse.c' || echo '$(srcdir)/'`audio/gstsbcparse.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) audio/$(DEPDIR)/audio_libgstbluetooth_la-gstsbcparse.Tpo audio/$(DEPDIR)/audio_libgstbluetooth_la-gstsbcparse.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='audio/gstsbcparse.c' object='audio/audio_libgstbluetooth_la-gstsbcparse.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) -c -o audio/audio_libgstbluetooth_la-gstsbcparse.lo `test -f 'audio/gstsbcparse.c' || echo '$(srcdir)/'`audio/gstsbcparse.c
+
+audio/audio_libgstbluetooth_la-gstavdtpsink.lo: audio/gstavdtpsink.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) -MT audio/audio_libgstbluetooth_la-gstavdtpsink.lo -MD -MP -MF audio/$(DEPDIR)/audio_libgstbluetooth_la-gstavdtpsink.Tpo -c -o audio/audio_libgstbluetooth_la-gstavdtpsink.lo `test -f 'audio/gstavdtpsink.c' || echo '$(srcdir)/'`audio/gstavdtpsink.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) audio/$(DEPDIR)/audio_libgstbluetooth_la-gstavdtpsink.Tpo audio/$(DEPDIR)/audio_libgstbluetooth_la-gstavdtpsink.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='audio/gstavdtpsink.c' object='audio/audio_libgstbluetooth_la-gstavdtpsink.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) -c -o audio/audio_libgstbluetooth_la-gstavdtpsink.lo `test -f 'audio/gstavdtpsink.c' || echo '$(srcdir)/'`audio/gstavdtpsink.c
+
+audio/audio_libgstbluetooth_la-gsta2dpsink.lo: audio/gsta2dpsink.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) -MT audio/audio_libgstbluetooth_la-gsta2dpsink.lo -MD -MP -MF audio/$(DEPDIR)/audio_libgstbluetooth_la-gsta2dpsink.Tpo -c -o audio/audio_libgstbluetooth_la-gsta2dpsink.lo `test -f 'audio/gsta2dpsink.c' || echo '$(srcdir)/'`audio/gsta2dpsink.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) audio/$(DEPDIR)/audio_libgstbluetooth_la-gsta2dpsink.Tpo audio/$(DEPDIR)/audio_libgstbluetooth_la-gsta2dpsink.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='audio/gsta2dpsink.c' object='audio/audio_libgstbluetooth_la-gsta2dpsink.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) -c -o audio/audio_libgstbluetooth_la-gsta2dpsink.lo `test -f 'audio/gsta2dpsink.c' || echo '$(srcdir)/'`audio/gsta2dpsink.c
+
+audio/audio_libgstbluetooth_la-gstsbcutil.lo: audio/gstsbcutil.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) -MT audio/audio_libgstbluetooth_la-gstsbcutil.lo -MD -MP -MF audio/$(DEPDIR)/audio_libgstbluetooth_la-gstsbcutil.Tpo -c -o audio/audio_libgstbluetooth_la-gstsbcutil.lo `test -f 'audio/gstsbcutil.c' || echo '$(srcdir)/'`audio/gstsbcutil.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) audio/$(DEPDIR)/audio_libgstbluetooth_la-gstsbcutil.Tpo audio/$(DEPDIR)/audio_libgstbluetooth_la-gstsbcutil.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='audio/gstsbcutil.c' object='audio/audio_libgstbluetooth_la-gstsbcutil.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) -c -o audio/audio_libgstbluetooth_la-gstsbcutil.lo `test -f 'audio/gstsbcutil.c' || echo '$(srcdir)/'`audio/gstsbcutil.c
+
+audio/audio_libgstbluetooth_la-gstrtpsbcpay.lo: audio/gstrtpsbcpay.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) -MT audio/audio_libgstbluetooth_la-gstrtpsbcpay.lo -MD -MP -MF audio/$(DEPDIR)/audio_libgstbluetooth_la-gstrtpsbcpay.Tpo -c -o audio/audio_libgstbluetooth_la-gstrtpsbcpay.lo `test -f 'audio/gstrtpsbcpay.c' || echo '$(srcdir)/'`audio/gstrtpsbcpay.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) audio/$(DEPDIR)/audio_libgstbluetooth_la-gstrtpsbcpay.Tpo audio/$(DEPDIR)/audio_libgstbluetooth_la-gstrtpsbcpay.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='audio/gstrtpsbcpay.c' object='audio/audio_libgstbluetooth_la-gstrtpsbcpay.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) -c -o audio/audio_libgstbluetooth_la-gstrtpsbcpay.lo `test -f 'audio/gstrtpsbcpay.c' || echo '$(srcdir)/'`audio/gstrtpsbcpay.c
+
+audio/audio_libgstbluetooth_la-ipc.lo: audio/ipc.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) -MT audio/audio_libgstbluetooth_la-ipc.lo -MD -MP -MF audio/$(DEPDIR)/audio_libgstbluetooth_la-ipc.Tpo -c -o audio/audio_libgstbluetooth_la-ipc.lo `test -f 'audio/ipc.c' || echo '$(srcdir)/'`audio/ipc.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) audio/$(DEPDIR)/audio_libgstbluetooth_la-ipc.Tpo audio/$(DEPDIR)/audio_libgstbluetooth_la-ipc.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='audio/ipc.c' object='audio/audio_libgstbluetooth_la-ipc.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) -c -o audio/audio_libgstbluetooth_la-ipc.lo `test -f 'audio/ipc.c' || echo '$(srcdir)/'`audio/ipc.c
+
+sbc/sbc_libsbc_la-sbc.lo: sbc/sbc.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sbc_libsbc_la_CFLAGS) $(CFLAGS) -MT sbc/sbc_libsbc_la-sbc.lo -MD -MP -MF sbc/$(DEPDIR)/sbc_libsbc_la-sbc.Tpo -c -o sbc/sbc_libsbc_la-sbc.lo `test -f 'sbc/sbc.c' || echo '$(srcdir)/'`sbc/sbc.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) sbc/$(DEPDIR)/sbc_libsbc_la-sbc.Tpo sbc/$(DEPDIR)/sbc_libsbc_la-sbc.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sbc/sbc.c' object='sbc/sbc_libsbc_la-sbc.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sbc_libsbc_la_CFLAGS) $(CFLAGS) -c -o sbc/sbc_libsbc_la-sbc.lo `test -f 'sbc/sbc.c' || echo '$(srcdir)/'`sbc/sbc.c
+
+sbc/sbc_libsbc_la-sbc_primitives.lo: sbc/sbc_primitives.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sbc_libsbc_la_CFLAGS) $(CFLAGS) -MT sbc/sbc_libsbc_la-sbc_primitives.lo -MD -MP -MF sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives.Tpo -c -o sbc/sbc_libsbc_la-sbc_primitives.lo `test -f 'sbc/sbc_primitives.c' || echo '$(srcdir)/'`sbc/sbc_primitives.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives.Tpo sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sbc/sbc_primitives.c' object='sbc/sbc_libsbc_la-sbc_primitives.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sbc_libsbc_la_CFLAGS) $(CFLAGS) -c -o sbc/sbc_libsbc_la-sbc_primitives.lo `test -f 'sbc/sbc_primitives.c' || echo '$(srcdir)/'`sbc/sbc_primitives.c
+
+sbc/sbc_libsbc_la-sbc_primitives_mmx.lo: sbc/sbc_primitives_mmx.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sbc_libsbc_la_CFLAGS) $(CFLAGS) -MT sbc/sbc_libsbc_la-sbc_primitives_mmx.lo -MD -MP -MF sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives_mmx.Tpo -c -o sbc/sbc_libsbc_la-sbc_primitives_mmx.lo `test -f 'sbc/sbc_primitives_mmx.c' || echo '$(srcdir)/'`sbc/sbc_primitives_mmx.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives_mmx.Tpo sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives_mmx.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sbc/sbc_primitives_mmx.c' object='sbc/sbc_libsbc_la-sbc_primitives_mmx.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sbc_libsbc_la_CFLAGS) $(CFLAGS) -c -o sbc/sbc_libsbc_la-sbc_primitives_mmx.lo `test -f 'sbc/sbc_primitives_mmx.c' || echo '$(srcdir)/'`sbc/sbc_primitives_mmx.c
+
+sbc/sbc_libsbc_la-sbc_primitives_iwmmxt.lo: sbc/sbc_primitives_iwmmxt.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sbc_libsbc_la_CFLAGS) $(CFLAGS) -MT sbc/sbc_libsbc_la-sbc_primitives_iwmmxt.lo -MD -MP -MF sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives_iwmmxt.Tpo -c -o sbc/sbc_libsbc_la-sbc_primitives_iwmmxt.lo `test -f 'sbc/sbc_primitives_iwmmxt.c' || echo '$(srcdir)/'`sbc/sbc_primitives_iwmmxt.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives_iwmmxt.Tpo sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives_iwmmxt.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sbc/sbc_primitives_iwmmxt.c' object='sbc/sbc_libsbc_la-sbc_primitives_iwmmxt.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sbc_libsbc_la_CFLAGS) $(CFLAGS) -c -o sbc/sbc_libsbc_la-sbc_primitives_iwmmxt.lo `test -f 'sbc/sbc_primitives_iwmmxt.c' || echo '$(srcdir)/'`sbc/sbc_primitives_iwmmxt.c
+
+sbc/sbc_libsbc_la-sbc_primitives_neon.lo: sbc/sbc_primitives_neon.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sbc_libsbc_la_CFLAGS) $(CFLAGS) -MT sbc/sbc_libsbc_la-sbc_primitives_neon.lo -MD -MP -MF sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives_neon.Tpo -c -o sbc/sbc_libsbc_la-sbc_primitives_neon.lo `test -f 'sbc/sbc_primitives_neon.c' || echo '$(srcdir)/'`sbc/sbc_primitives_neon.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives_neon.Tpo sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives_neon.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sbc/sbc_primitives_neon.c' object='sbc/sbc_libsbc_la-sbc_primitives_neon.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sbc_libsbc_la_CFLAGS) $(CFLAGS) -c -o sbc/sbc_libsbc_la-sbc_primitives_neon.lo `test -f 'sbc/sbc_primitives_neon.c' || echo '$(srcdir)/'`sbc/sbc_primitives_neon.c
+
+sbc/sbc_libsbc_la-sbc_primitives_armv6.lo: sbc/sbc_primitives_armv6.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sbc_libsbc_la_CFLAGS) $(CFLAGS) -MT sbc/sbc_libsbc_la-sbc_primitives_armv6.lo -MD -MP -MF sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives_armv6.Tpo -c -o sbc/sbc_libsbc_la-sbc_primitives_armv6.lo `test -f 'sbc/sbc_primitives_armv6.c' || echo '$(srcdir)/'`sbc/sbc_primitives_armv6.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives_armv6.Tpo sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives_armv6.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sbc/sbc_primitives_armv6.c' object='sbc/sbc_libsbc_la-sbc_primitives_armv6.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sbc_libsbc_la_CFLAGS) $(CFLAGS) -c -o sbc/sbc_libsbc_la-sbc_primitives_armv6.lo `test -f 'sbc/sbc_primitives_armv6.c' || echo '$(srcdir)/'`sbc/sbc_primitives_armv6.c
+
+.l.c:
+ $(AM_V_LEX)$(am__skiplex) $(SHELL) $(YLWRAP) $< $(LEX_OUTPUT_ROOT).c $@ -- $(LEXCOMPILE)
+
+.y.c:
+ $(AM_V_YACC)$(am__skipyacc) $(SHELL) $(YLWRAP) $< y.tab.c $@ y.tab.h $*.h y.output $*.output -- $(YACCCOMPILE)
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+ -rm -rf attrib/.libs attrib/_libs
+ -rm -rf audio/.libs audio/_libs
+ -rm -rf compat/.libs compat/_libs
+ -rm -rf cups/.libs cups/_libs
+ -rm -rf lib/.libs lib/_libs
+ -rm -rf sbc/.libs sbc/_libs
+ -rm -rf src/.libs src/_libs
+ -rm -rf test/.libs test/_libs
+ -rm -rf tools/.libs tools/_libs
+ -rm -rf tracer/.libs tracer/_libs
+
+distclean-libtool:
+ -rm -f libtool config.lt
+install-man1: $(dist_man_MANS) $(man_MANS)
+ @$(NORMAL_INSTALL)
+ test -z "$(man1dir)" || $(MKDIR_P) "$(DESTDIR)$(man1dir)"
+ @list=''; test -n "$(man1dir)" || exit 0; \
+ { for i in $$list; do echo "$$i"; done; \
+ l2='$(dist_man_MANS) $(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+ sed -n '/\.1[a-z]*$$/p'; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man1dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man1dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man1:
+ @$(NORMAL_UNINSTALL)
+ @list=''; test -n "$(man1dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ l2='$(dist_man_MANS) $(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+ sed -n '/\.1[a-z]*$$/p'; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ test -z "$$files" || { \
+ echo " ( cd '$(DESTDIR)$(man1dir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(man1dir)" && rm -f $$files; }
+install-man8: $(dist_man_MANS) $(man_MANS)
+ @$(NORMAL_INSTALL)
+ test -z "$(man8dir)" || $(MKDIR_P) "$(DESTDIR)$(man8dir)"
+ @list=''; test -n "$(man8dir)" || exit 0; \
+ { for i in $$list; do echo "$$i"; done; \
+ l2='$(dist_man_MANS) $(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+ sed -n '/\.8[a-z]*$$/p'; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man8:
+ @$(NORMAL_UNINSTALL)
+ @list=''; test -n "$(man8dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ l2='$(dist_man_MANS) $(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+ sed -n '/\.8[a-z]*$$/p'; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ test -z "$$files" || { \
+ echo " ( cd '$(DESTDIR)$(man8dir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(man8dir)" && rm -f $$files; }
+install-alsaconfDATA: $(alsaconf_DATA)
+ @$(NORMAL_INSTALL)
+ test -z "$(alsaconfdir)" || $(MKDIR_P) "$(DESTDIR)$(alsaconfdir)"
+ @list='$(alsaconf_DATA)'; test -n "$(alsaconfdir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(alsaconfdir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(alsaconfdir)" || exit $$?; \
+ done
+
+uninstall-alsaconfDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(alsaconf_DATA)'; test -n "$(alsaconfdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$files" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(alsaconfdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(alsaconfdir)" && rm -f $$files
+install-confDATA: $(conf_DATA)
+ @$(NORMAL_INSTALL)
+ test -z "$(confdir)" || $(MKDIR_P) "$(DESTDIR)$(confdir)"
+ @list='$(conf_DATA)'; test -n "$(confdir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(confdir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(confdir)" || exit $$?; \
+ done
+
+uninstall-confDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(conf_DATA)'; test -n "$(confdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$files" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(confdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(confdir)" && rm -f $$files
+install-dbusDATA: $(dbus_DATA)
+ @$(NORMAL_INSTALL)
+ test -z "$(dbusdir)" || $(MKDIR_P) "$(DESTDIR)$(dbusdir)"
+ @list='$(dbus_DATA)'; test -n "$(dbusdir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(dbusdir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(dbusdir)" || exit $$?; \
+ done
+
+uninstall-dbusDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(dbus_DATA)'; test -n "$(dbusdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$files" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(dbusdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(dbusdir)" && rm -f $$files
+install-pkgconfigDATA: $(pkgconfig_DATA)
+ @$(NORMAL_INSTALL)
+ test -z "$(pkgconfigdir)" || $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)"
+ @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \
+ done
+
+uninstall-pkgconfigDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$files" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(pkgconfigdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(pkgconfigdir)" && rm -f $$files
+install-rulesDATA: $(rules_DATA)
+ @$(NORMAL_INSTALL)
+ test -z "$(rulesdir)" || $(MKDIR_P) "$(DESTDIR)$(rulesdir)"
+ @list='$(rules_DATA)'; test -n "$(rulesdir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(rulesdir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(rulesdir)" || exit $$?; \
+ done
+
+uninstall-rulesDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(rules_DATA)'; test -n "$(rulesdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$files" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(rulesdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(rulesdir)" && rm -f $$files
+install-stateDATA: $(state_DATA)
+ @$(NORMAL_INSTALL)
+ test -z "$(statedir)" || $(MKDIR_P) "$(DESTDIR)$(statedir)"
+ @list='$(state_DATA)'; test -n "$(statedir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(statedir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(statedir)" || exit $$?; \
+ done
+
+uninstall-stateDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(state_DATA)'; test -n "$(statedir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$files" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(statedir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(statedir)" && rm -f $$files
+install-includeHEADERS: $(include_HEADERS)
+ @$(NORMAL_INSTALL)
+ test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)"
+ @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includedir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(includedir)" || exit $$?; \
+ done
+
+uninstall-includeHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$files" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(includedir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(includedir)" && rm -f $$files
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @list='$(MANS)'; if test -n "$$list"; then \
+ list=`for p in $$list; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ if test -f "$$d$$p"; then echo "$$d$$p"; else :; fi; done`; \
+ if test -n "$$list" && \
+ grep 'ab help2man is required to generate this page' $$list >/dev/null; then \
+ echo "error: found man pages containing the \`missing help2man' replacement text:" >&2; \
+ grep -l 'ab help2man is required to generate this page' $$list | sed 's/^/ /' >&2; \
+ echo " to fix them, install help2man, remove and regenerate the man pages;" >&2; \
+ echo " typically \`make maintainer-clean' will remove them" >&2; \
+ exit 1; \
+ else :; fi; \
+ else :; fi
+ $(am__remove_distdir)
+ test -d "$(distdir)" || mkdir "$(distdir)"
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ -test -n "$(am__skip_mode_fix)" \
+ || find "$(distdir)" -type d ! -perm -755 \
+ -exec chmod u+rwx,go+rx {} \; -o \
+ ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
+ ! -type d ! -perm -400 -exec chmod a+r {} \; -o \
+ ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \
+ || chmod -R a+r "$(distdir)"
+dist-gzip: distdir
+ tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
+ $(am__remove_distdir)
+
+dist-bzip2: distdir
+ tardir=$(distdir) && $(am__tar) | bzip2 -9 -c >$(distdir).tar.bz2
+ $(am__remove_distdir)
+
+dist-lzma: distdir
+ tardir=$(distdir) && $(am__tar) | lzma -9 -c >$(distdir).tar.lzma
+ $(am__remove_distdir)
+
+dist-xz: distdir
+ tardir=$(distdir) && $(am__tar) | xz -c >$(distdir).tar.xz
+ $(am__remove_distdir)
+
+dist-tarZ: distdir
+ tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z
+ $(am__remove_distdir)
+
+dist-shar: distdir
+ shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz
+ $(am__remove_distdir)
+
+dist-zip: distdir
+ -rm -f $(distdir).zip
+ zip -rq $(distdir).zip $(distdir)
+ $(am__remove_distdir)
+
+dist dist-all: distdir
+ tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
+ $(am__remove_distdir)
+
+# This target untars the dist file and tries a VPATH configuration. Then
+# it guarantees that the distribution is self-contained by making another
+# tarfile.
+distcheck: dist
+ case '$(DIST_ARCHIVES)' in \
+ *.tar.gz*) \
+ GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\
+ *.tar.bz2*) \
+ bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\
+ *.tar.lzma*) \
+ lzma -dc $(distdir).tar.lzma | $(am__untar) ;;\
+ *.tar.xz*) \
+ xz -dc $(distdir).tar.xz | $(am__untar) ;;\
+ *.tar.Z*) \
+ uncompress -c $(distdir).tar.Z | $(am__untar) ;;\
+ *.shar.gz*) \
+ GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\
+ *.zip*) \
+ unzip $(distdir).zip ;;\
+ esac
+ chmod -R a-w $(distdir); chmod a+w $(distdir)
+ mkdir $(distdir)/_build
+ mkdir $(distdir)/_inst
+ chmod a-w $(distdir)
+ test -d $(distdir)/_build || exit 0; \
+ dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \
+ && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \
+ && am__cwd=`pwd` \
+ && $(am__cd) $(distdir)/_build \
+ && ../configure --srcdir=.. --prefix="$$dc_install_base" \
+ $(DISTCHECK_CONFIGURE_FLAGS) \
+ && $(MAKE) $(AM_MAKEFLAGS) \
+ && $(MAKE) $(AM_MAKEFLAGS) dvi \
+ && $(MAKE) $(AM_MAKEFLAGS) check \
+ && $(MAKE) $(AM_MAKEFLAGS) install \
+ && $(MAKE) $(AM_MAKEFLAGS) installcheck \
+ && $(MAKE) $(AM_MAKEFLAGS) uninstall \
+ && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \
+ distuninstallcheck \
+ && chmod -R a-w "$$dc_install_base" \
+ && ({ \
+ (cd ../.. && umask 077 && mkdir "$$dc_destdir") \
+ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \
+ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \
+ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \
+ distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \
+ } || { rm -rf "$$dc_destdir"; exit 1; }) \
+ && rm -rf "$$dc_destdir" \
+ && $(MAKE) $(AM_MAKEFLAGS) dist \
+ && rm -rf $(DIST_ARCHIVES) \
+ && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \
+ && cd "$$am__cwd" \
+ || exit 1
+ $(am__remove_distdir)
+ @(echo "$(distdir) archives ready for distribution: "; \
+ list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \
+ sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x'
+distuninstallcheck:
+ @$(am__cd) '$(distuninstallcheck_dir)' \
+ && test `$(distuninstallcheck_listfiles) | wc -l` -le 1 \
+ || { echo "ERROR: files left after uninstall:" ; \
+ if test -n "$(DESTDIR)"; then \
+ echo " (check DESTDIR support)"; \
+ fi ; \
+ $(distuninstallcheck_listfiles) ; \
+ exit 1; } >&2
+distcleancheck: distclean
+ @if test '$(srcdir)' = . ; then \
+ echo "ERROR: distcleancheck can only run from a VPATH build" ; \
+ exit 1 ; \
+ fi
+ @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \
+ || { echo "ERROR: files left in build directory after distclean:" ; \
+ $(distcleancheck_listfiles) ; \
+ exit 1; } >&2
+check-am: all-am
+check: check-am
+all-am: Makefile $(LIBRARIES) $(LTLIBRARIES) $(PROGRAMS) $(SCRIPTS) \
+ $(MANS) $(DATA) $(HEADERS) config.h
+install-binPROGRAMS: install-libLTLIBRARIES
+
+installdirs:
+ for dir in "$(DESTDIR)$(alsadir)" "$(DESTDIR)$(gstreamerdir)" "$(DESTDIR)$(libdir)" "$(DESTDIR)$(plugindir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(cupsdir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(udevdir)" "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(alsaconfdir)" "$(DESTDIR)$(confdir)" "$(DESTDIR)$(dbusdir)" "$(DESTDIR)$(pkgconfigdir)" "$(DESTDIR)$(rulesdir)" "$(DESTDIR)$(statedir)" "$(DESTDIR)$(includedir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+ -rm -f attrib/$(DEPDIR)/$(am__dirstamp)
+ -rm -f attrib/$(am__dirstamp)
+ -rm -f audio/$(DEPDIR)/$(am__dirstamp)
+ -rm -f audio/$(am__dirstamp)
+ -rm -f btio/$(DEPDIR)/$(am__dirstamp)
+ -rm -f btio/$(am__dirstamp)
+ -rm -f compat/$(DEPDIR)/$(am__dirstamp)
+ -rm -f compat/$(am__dirstamp)
+ -rm -f cups/$(DEPDIR)/$(am__dirstamp)
+ -rm -f cups/$(am__dirstamp)
+ -rm -f gdbus/$(DEPDIR)/$(am__dirstamp)
+ -rm -f gdbus/$(am__dirstamp)
+ -rm -f health/$(DEPDIR)/$(am__dirstamp)
+ -rm -f health/$(am__dirstamp)
+ -rm -f input/$(DEPDIR)/$(am__dirstamp)
+ -rm -f input/$(am__dirstamp)
+ -rm -f lib/$(DEPDIR)/$(am__dirstamp)
+ -rm -f lib/$(am__dirstamp)
+ -rm -f network/$(DEPDIR)/$(am__dirstamp)
+ -rm -f network/$(am__dirstamp)
+ -rm -f plugins/$(DEPDIR)/$(am__dirstamp)
+ -rm -f plugins/$(am__dirstamp)
+ -rm -f sap/$(DEPDIR)/$(am__dirstamp)
+ -rm -f sap/$(am__dirstamp)
+ -rm -f sbc/$(DEPDIR)/$(am__dirstamp)
+ -rm -f sbc/$(am__dirstamp)
+ -rm -f serial/$(DEPDIR)/$(am__dirstamp)
+ -rm -f serial/$(am__dirstamp)
+ -rm -f src/$(DEPDIR)/$(am__dirstamp)
+ -rm -f src/$(am__dirstamp)
+ -rm -f test/$(DEPDIR)/$(am__dirstamp)
+ -rm -f test/$(am__dirstamp)
+ -rm -f tools/$(DEPDIR)/$(am__dirstamp)
+ -rm -f tools/$(am__dirstamp)
+ -rm -f tracer/$(DEPDIR)/$(am__dirstamp)
+ -rm -f tracer/$(am__dirstamp)
+ -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -rm -f tools/lexer.c
+ -rm -f tools/parser.c
+ -rm -f tools/parser.h
+ -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+clean: clean-am
+
+clean-am: clean-alsaLTLIBRARIES clean-binPROGRAMS clean-cupsPROGRAMS \
+ clean-generic clean-gstreamerLTLIBRARIES clean-libLTLIBRARIES \
+ clean-libtool clean-local clean-noinstLIBRARIES \
+ clean-noinstLTLIBRARIES clean-noinstPROGRAMS \
+ clean-pluginLTLIBRARIES clean-sbinPROGRAMS mostlyclean-am
+
+distclean: distclean-am
+ -rm -f $(am__CONFIG_DISTCLEAN_FILES)
+ -rm -rf attrib/$(DEPDIR) audio/$(DEPDIR) btio/$(DEPDIR) compat/$(DEPDIR) cups/$(DEPDIR) gdbus/$(DEPDIR) health/$(DEPDIR) input/$(DEPDIR) lib/$(DEPDIR) network/$(DEPDIR) plugins/$(DEPDIR) sap/$(DEPDIR) sbc/$(DEPDIR) serial/$(DEPDIR) src/$(DEPDIR) test/$(DEPDIR) tools/$(DEPDIR) tracer/$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-hdr distclean-libtool distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-alsaLTLIBRARIES install-alsaconfDATA \
+ install-confDATA install-cupsPROGRAMS install-dbusDATA \
+ install-dist_udevSCRIPTS install-gstreamerLTLIBRARIES \
+ install-includeHEADERS install-man install-pkgconfigDATA \
+ install-pluginLTLIBRARIES install-rulesDATA install-stateDATA
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-binPROGRAMS install-libLTLIBRARIES \
+ install-sbinPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man: install-man1 install-man8
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f $(am__CONFIG_DISTCLEAN_FILES)
+ -rm -rf $(top_srcdir)/autom4te.cache
+ -rm -rf attrib/$(DEPDIR) audio/$(DEPDIR) btio/$(DEPDIR) compat/$(DEPDIR) cups/$(DEPDIR) gdbus/$(DEPDIR) health/$(DEPDIR) input/$(DEPDIR) lib/$(DEPDIR) network/$(DEPDIR) plugins/$(DEPDIR) sap/$(DEPDIR) sbc/$(DEPDIR) serial/$(DEPDIR) src/$(DEPDIR) test/$(DEPDIR) tools/$(DEPDIR) tracer/$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-alsaLTLIBRARIES uninstall-alsaconfDATA \
+ uninstall-binPROGRAMS uninstall-confDATA \
+ uninstall-cupsPROGRAMS uninstall-dbusDATA \
+ uninstall-dist_udevSCRIPTS uninstall-gstreamerLTLIBRARIES \
+ uninstall-includeHEADERS uninstall-libLTLIBRARIES \
+ uninstall-man uninstall-pkgconfigDATA \
+ uninstall-pluginLTLIBRARIES uninstall-rulesDATA \
+ uninstall-sbinPROGRAMS uninstall-stateDATA
+
+uninstall-man: uninstall-man1 uninstall-man8
+
+.MAKE: all install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am am--refresh check check-am clean \
+ clean-alsaLTLIBRARIES clean-binPROGRAMS clean-cupsPROGRAMS \
+ clean-generic clean-gstreamerLTLIBRARIES clean-libLTLIBRARIES \
+ clean-libtool clean-local clean-noinstLIBRARIES \
+ clean-noinstLTLIBRARIES clean-noinstPROGRAMS \
+ clean-pluginLTLIBRARIES clean-sbinPROGRAMS ctags dist dist-all \
+ dist-bzip2 dist-gzip dist-lzma dist-shar dist-tarZ dist-xz \
+ dist-zip distcheck distclean distclean-compile \
+ distclean-generic distclean-hdr distclean-libtool \
+ distclean-tags distcleancheck distdir distuninstallcheck dvi \
+ dvi-am html html-am info info-am install \
+ install-alsaLTLIBRARIES install-alsaconfDATA install-am \
+ install-binPROGRAMS install-confDATA install-cupsPROGRAMS \
+ install-data install-data-am install-dbusDATA \
+ install-dist_udevSCRIPTS install-dvi install-dvi-am \
+ install-exec install-exec-am install-gstreamerLTLIBRARIES \
+ install-html install-html-am install-includeHEADERS \
+ install-info install-info-am install-libLTLIBRARIES \
+ install-man install-man1 install-man8 install-pdf \
+ install-pdf-am install-pkgconfigDATA install-pluginLTLIBRARIES \
+ install-ps install-ps-am install-rulesDATA \
+ install-sbinPROGRAMS install-stateDATA install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags uninstall uninstall-alsaLTLIBRARIES \
+ uninstall-alsaconfDATA uninstall-am uninstall-binPROGRAMS \
+ uninstall-confDATA uninstall-cupsPROGRAMS uninstall-dbusDATA \
+ uninstall-dist_udevSCRIPTS uninstall-gstreamerLTLIBRARIES \
+ uninstall-includeHEADERS uninstall-libLTLIBRARIES \
+ uninstall-man uninstall-man1 uninstall-man8 \
+ uninstall-pkgconfigDATA uninstall-pluginLTLIBRARIES \
+ uninstall-rulesDATA uninstall-sbinPROGRAMS uninstall-stateDATA
+
+
+@TOOLS_TRUE@tools/kword.c: tools/parser.h
+
+src/plugin.$(OBJEXT): src/builtin.h
+
+src/builtin.h: src/genbuiltin $(builtin_sources)
+ $(AM_V_GEN)$(srcdir)/src/genbuiltin $(builtin_modules) > $@
+
+audio/telephony.c: audio/@TELEPHONY_DRIVER@
+ $(AM_V_GEN)$(LN_S) $(abs_top_srcdir)/$< $@
+
+sap/sap.c: sap/@SAP_DRIVER@
+ $(AM_V_GEN)$(LN_S) $(abs_top_srcdir)/$< $@
+
+scripts/%.rules:
+ $(AM_V_GEN)cp $(subst 97-,,$@) $@
+
+$(lib_libbluetooth_la_OBJECTS): $(local_headers)
+
+lib/bluetooth/%.h: lib/%.h
+ $(AM_V_at)$(MKDIR_P) lib/bluetooth
+ $(AM_V_GEN)$(LN_S) $(abs_top_srcdir)/$< $@
+
+clean-local:
+ $(RM) -r lib/bluetooth
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/Makefile.tools b/Makefile.tools
new file mode 100644
index 0000000..7c5ff55
--- /dev/null
+++ b/Makefile.tools
@@ -0,0 +1,239 @@
+
+if TOOLS
+if CONFIGFILES
+conf_DATA += tools/rfcomm.conf
+endif
+
+bin_PROGRAMS += tools/rfcomm tools/l2ping \
+ tools/hcitool tools/sdptool tools/ciptool
+
+sbin_PROGRAMS += tools/hciattach tools/hciconfig
+
+noinst_PROGRAMS += tools/avinfo tools/ppporc \
+ tools/hcieventmask tools/hcisecfilter
+
+tools/kword.c: tools/parser.h
+
+tools_rfcomm_SOURCES = tools/rfcomm.c tools/parser.y tools/lexer.l \
+ tools/kword.h tools/kword.c
+EXTRA_tools_rfcomm_SOURCES = tools/parser.h tools/parser.c \
+ tools/lexer.c
+tools_rfcomm_LDADD = lib/libbluetooth.la
+
+tools_l2ping_LDADD = lib/libbluetooth.la
+
+tools_hciattach_SOURCES = tools/hciattach.c tools/hciattach.h \
+ tools/hciattach_st.c \
+ tools/hciattach_ti.c \
+ tools/hciattach_tialt.c \
+ tools/hciattach_ath3k.c \
+ tools/hciattach_qualcomm.c
+tools_hciattach_LDADD = lib/libbluetooth.la
+
+tools_hciconfig_SOURCES = tools/hciconfig.c tools/csr.h tools/csr.c \
+ src/textfile.h src/textfile.c
+tools_hciconfig_LDADD = lib/libbluetooth.la
+
+tools_hcitool_SOURCES = tools/hcitool.c src/oui.h src/oui.c \
+ src/textfile.h src/textfile.c
+tools_hcitool_LDADD = lib/libbluetooth.la
+
+tools_sdptool_SOURCES = tools/sdptool.c src/sdp-xml.h src/sdp-xml.c
+tools_sdptool_LDADD = lib/libbluetooth.la
+
+tools_ciptool_LDADD = lib/libbluetooth.la
+
+tools_avinfo_LDADD = lib/libbluetooth.la
+
+tools_ppporc_LDADD = lib/libbluetooth.la
+
+tools_hcieventmask_LDADD = lib/libbluetooth.la
+
+dist_man_MANS += tools/rfcomm.1 tools/l2ping.8 \
+ tools/hciattach.8 tools/hciconfig.8 \
+ tools/hcitool.1 tools/sdptool.1 tools/ciptool.1
+else
+EXTRA_DIST += tools/rfcomm.1 tools/l2ping.8 \
+ tools/hciattach.8 tools/hciconfig.8 \
+ tools/hcitool.1 tools/sdptool.1 tools/ciptool.1
+endif
+
+CLEANFILES += tools/lexer.c tools/parser.c tools/parser.h
+
+EXTRA_DIST += tools/rfcomm.conf
+
+if TRACER
+sbin_PROGRAMS += tracer/hcitrace
+
+tracer_hcitrace_SOURCES = tracer/main.c
+tracer_hcitrace_LDADD = lib/libbluetooth.la \
+ @GLIB_LIBS@ @DBUS_LIBS@ @CAPNG_LIBS@
+tracer_hcitrace_DEPENDENCIES = lib/libbluetooth.la
+endif
+
+if BCCMD
+sbin_PROGRAMS += tools/bccmd
+
+tools_bccmd_SOURCES = tools/bccmd.c tools/csr.h tools/csr.c \
+ tools/csr_hci.c tools/csr_h4.c tools/csr_3wire.c \
+ tools/csr_bcsp.c tools/ubcsp.h tools/ubcsp.c
+tools_bccmd_LDADD = lib/libbluetooth.la
+
+if USB
+tools_bccmd_SOURCES += tools/csr_usb.c
+tools_bccmd_LDADD += @USB_LIBS@
+endif
+
+dist_man_MANS += tools/bccmd.8
+else
+EXTRA_DIST += tools/bccmd.8
+endif
+
+if HID2HCI
+sbin_PROGRAMS += tools/hid2hci
+
+tools_hid2hci_LDADD = @USB_LIBS@
+
+dist_man_MANS += tools/hid2hci.8
+else
+EXTRA_DIST += tools/hid2hci.8
+endif
+
+if DFUTOOL
+bin_PROGRAMS += tools/dfutool
+
+tools_dfutool_SOURCES = tools/dfutool.c tools/dfu.h tools/dfu.c
+tools_dfutool_LDADD = @USB_LIBS@
+
+dist_man_MANS += tools/dfutool.1
+else
+EXTRA_DIST += tools/dfutool.1
+endif
+
+
+if USB
+noinst_PROGRAMS += tools/dfubabel tools/avctrl
+
+tools_dfubabel_LDADD = @USB_LIBS@
+
+tools_avctrl_LDADD = @USB_LIBS@
+endif
+
+EXTRA_DIST += tools/dfubabel.1 tools/avctrl.8
+
+
+if CUPS
+cupsdir = $(libdir)/cups/backend
+
+cups_PROGRAMS = cups/bluetooth
+
+cups_bluetooth_SOURCES = $(gdbus_sources) cups/main.c cups/cups.h \
+ cups/sdp.c cups/spp.c cups/hcrp.c
+
+cups_bluetooth_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ lib/libbluetooth.la
+endif
+
+
+if TEST
+sbin_PROGRAMS += test/hciemu
+
+bin_PROGRAMS += test/l2test test/rctest
+
+noinst_PROGRAMS += test/gaptest test/sdptest test/scotest \
+ test/attest test/hstest test/avtest test/ipctest \
+ test/lmptest test/bdaddr test/agent \
+ test/btiotest test/test-textfile \
+ test/uuidtest
+
+test_hciemu_LDADD = @GLIB_LIBS@ lib/libbluetooth.la
+
+test_l2test_LDADD = lib/libbluetooth.la
+
+test_rctest_LDADD = lib/libbluetooth.la
+
+test_gaptest_LDADD = @DBUS_LIBS@
+
+test_sdptest_LDADD = lib/libbluetooth.la
+
+test_scotest_LDADD = lib/libbluetooth.la
+
+test_attest_LDADD = lib/libbluetooth.la
+
+test_hstest_LDADD = lib/libbluetooth.la
+
+test_avtest_LDADD = lib/libbluetooth.la
+
+test_lmptest_LDADD = lib/libbluetooth.la
+
+test_ipctest_SOURCES = test/ipctest.c audio/ipc.h audio/ipc.c
+test_ipctest_LDADD= @GLIB_LIBS@ sbc/libsbc.la
+
+test_bdaddr_SOURCES = test/bdaddr.c src/oui.h src/oui.c
+test_bdaddr_LDADD = lib/libbluetooth.la
+
+test_agent_LDADD = @DBUS_LIBS@
+
+test_btiotest_SOURCES = test/btiotest.c btio/btio.h btio/btio.c
+test_btiotest_LDADD = @GLIB_LIBS@ lib/libbluetooth.la
+
+test_uuidtest_SOURCES = test/uuidtest.c
+test_uuidtest_LDADD = lib/libbluetooth.la
+
+test_test_textfile_SOURCES = test/test-textfile.c src/textfile.h src/textfile.c
+
+dist_man_MANS += test/rctest.1 test/hciemu.1
+
+EXTRA_DIST += test/bdaddr.8
+else
+EXTRA_DIST += test/rctest.1 test/hciemu.1 test/bdaddr.8
+endif
+
+EXTRA_DIST += test/apitest test/sap-client test/hsplay test/hsmicro \
+ test/dbusdef.py test/monitor-bluetooth test/list-devices \
+ test/test-discovery test/test-manager test/test-adapter \
+ test/test-device test/test-service test/test-serial \
+ test/test-telephony test/test-network test/simple-agent \
+ test/simple-service test/simple-endpoint test/test-audio \
+ test/test-input test/test-attrib test/test-sap-server \
+ test/service-record.dtd test/service-did.xml test/service-spp.xml \
+ test/service-opp.xml test/service-ftp.xml
+
+
+if HIDD
+bin_PROGRAMS += compat/hidd
+
+compat_hidd_SOURCES = compat/hidd.c compat/hidd.h src/uinput.h \
+ compat/sdp.h compat/sdp.c compat/fakehid.c \
+ src/textfile.h src/textfile.c
+compat_hidd_LDADD = -lm lib/libbluetooth.la
+
+dist_man_MANS += compat/hidd.1
+else
+EXTRA_DIST += compat/hidd.1
+endif
+
+if PAND
+bin_PROGRAMS += compat/pand
+
+compat_pand_SOURCES = compat/pand.c compat/pand.h \
+ compat/bnep.c compat/sdp.h compat/sdp.c \
+ src/textfile.h src/textfile.c
+compat_pand_LDADD = lib/libbluetooth.la
+
+dist_man_MANS += compat/pand.1
+else
+EXTRA_DIST += compat/pand.1
+endif
+
+if DUND
+bin_PROGRAMS += compat/dund
+
+compat_dund_SOURCES = compat/dund.c compat/dund.h compat/lib.h \
+ compat/sdp.h compat/sdp.c compat/dun.c compat/msdun.c \
+ src/textfile.h src/textfile.c
+compat_dund_LDADD = lib/libbluetooth.la
+
+dist_man_MANS += compat/dund.1
+else
+EXTRA_DIST += compat/dund.1
+endif
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/NEWS
diff --git a/README b/README
new file mode 100644
index 0000000..77778e0
--- /dev/null
+++ b/README
@@ -0,0 +1,38 @@
+BlueZ - Bluetooth protocol stack for Linux
+******************************************
+
+Copyright (C) 2000-2001 Qualcomm Incorporated
+Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
+
+
+Compilation and installation
+============================
+
+In order to compile Bluetooth utilities you need following software packages:
+ - Linux Bluetooth protocol stack (BlueZ)
+ - GCC compiler
+ - D-Bus library
+ - GLib library
+ - USB library (optional)
+ - Lexical Analyzer (flex, lex)
+ - YACC (yacc, bison, byacc)
+
+To configure run:
+ ./configure --prefix=/usr --mandir=/usr/share/man \
+ --sysconfdir=/etc --localstatedir=/var --libexecdir=/lib
+
+Configure automatically searches for all required components and packages.
+
+To compile and install run:
+ make && make install
+
+
+Information
+===========
+
+Mailing lists:
+ linux-bluetooth@vger.kernel.org
+
+For additional information about the project visit BlueZ web site:
+ http://www.bluez.org
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..0e06f99
--- /dev/null
+++ b/TODO
@@ -0,0 +1,254 @@
+Background
+==========
+
+- Priority scale: High, Medium and Low
+
+- Complexity scale: C1, C2, C4 and C8. The complexity scale is exponential,
+ with complexity 1 being the lowest complexity. Complexity is a function
+ of both task 'complexity' and task 'scope'.
+
+ The general rule of thumb is that a complexity 1 task should take 1-2 weeks
+ for a person very familiar with BlueZ codebase. Higher complexity tasks
+ require more time and have higher uncertainty.
+
+ Higher complexity tasks should be refined into several lower complexity tasks
+ once the task is better understood.
+
+General
+==========
+
+- UUID handling: Use the new functions created for UUID handling in all parts
+ of BlueZ code. Currently, the new bt_uuid_* functions are being used by
+ GATT-related code only.
+
+ Priority: high
+ Complexity: C4
+
+- Rename glib-helper file to a more convenient name. The ideia is try to keep
+ only sdp helpers functions. bt_* prefix shall be also changed.
+
+ Priority: Low
+ Complexity: C1
+
+Low Energy
+==========
+
+- Advertising management. Adapter interface needs to be changed to manage
+ connection modes, adapter type and advertising policy. See Volume 3,
+ Part C, section 9.3. If Attribute Server is enabled the LE capable
+ adapter shall to start advertising. Further investigation is necessary
+ to define which connectable mode needs to be supported: Non-connectable,
+ directed connectable and undirected connectable. Basically, two connectable
+ scenarios shall be addressed:
+ 1. GATT client is disconnected, but intends to become a Peripheral to
+ receive indications/notifications.
+ 2. GATT server intends to accept connections.
+
+ Priority: Medium
+ Complexity: C2
+
+- Define Auto Connection Establishment Procedure. Some profiles such as
+ Proximity requires an active link to identify path lost situation. It is
+ necessary to define how to manage connections, it seems that White List
+ is appropriated to address auto connections, however is not clear if the
+ this procedure shall be a profile specific detail or if the remote device
+ object can expose a property "WhiteList", maybe "Trusted" property can be
+ also used for this purpose. Another alternative is to define a method to
+ allow application to request/register the wanted scanning/connection
+ parameters. Before start this task, a RFC/PATCH shall be sent to the ML.
+ See Volume 3, Part C, section 9.3.5 for more information.
+
+ Priority: Medium
+ Complexity: C2
+
+- Implement a tool(or extend hciconfig) to setup the advertising parameters
+ and data. Extend hciconfig passing extra arguments when enabling the
+ advertises is not the right approach, it will be almost impossible to
+ address all arguments needed in an acceptable way. For testing, we need
+ a tool to change easily the AD Flags, the UUIDs and other data that can be
+ exported through the advertising data field. Suggestions: 1) extend hciconfig
+ passing a config file when enabling advertises; 2) write a ncurses based tool
+
+ Priority: Medium
+ Complexity: C2
+
+- Add new property in the DeviceFound signal to report the device type:
+ BR/EDR, single mode or dual-mode.
+
+ Priority: Medium
+ Complexity: C1
+
+- Privacy: When privacy is enabled in the adapter, LE scanning/connection
+ should use a private address. StartDiscovery method shall be changed and
+ new adapter property shall be added.
+
+ Priority: Medium
+ Complexity: C1
+
+- Static random address setup and storage. Once this address is written
+ in the a given remote, the address can not be changed anymore.
+
+ Priority: Low
+ Complexity: C1
+
+- Reconnection address: Reconnection address is a non resolvable private
+ address that the central writes in the peripheral. BlueZ will support
+ multiple profiles, it is not clear how it needs to be implemented.
+ Further discussion is necessary.
+
+ Priority: Low
+ Complexity: C2
+
+- Device Name Characteristic is a GAP characteristic for Low Energy. This
+ characteristic shall be integrated/used in the discovery procedure. The
+ ideia is to report the value of this characteristic using DeviceFound signals.
+ Discussion with the community is needed before to start this task. Other GAP
+ characteristics for LE needs to follow a similar approach. It is not clear
+ if all GAP characteristics can be exposed using properties instead of a primary
+ service characteristics.
+ See Volume 3, Part C, section 12.1 for more information.
+
+ Priority: Low
+ Complexity: C2
+
+ATT/GATT
+========
+
+- For BR/EDR, primary services can be registered based on the information
+ extracted from the service records. UUIDs, start and end handles information
+ are available in the record, Discover All Primary Services procedure is not
+ necessary. If a GATT service doesn't export a service record means that
+ it should not be used over BR/EDR. Don't start this task before to move the
+ attribute client code to the bluetoothd core.
+
+ Priority: Medium
+ Complexity: C1
+
+- At the moment authentication and authorization is not supported at the
+ same time, read/write requirements in the attribute server needs to
+ be extended. According to Bluetooth Specification a server shall check
+ authentication and authorization requirements before any other check is
+ performed.
+
+ Priority: Medium
+ Complexity: C1
+
+- ATT/GATT parsing to hcidump. Partially implemented, missing to fix
+ multiple advertises in the same event and RSSI.
+
+ Priority: Medium
+ Complexity: C2
+
+- Implement ATT PDU validation. Malformed PDUs can cause division by zero
+ when decoding PDUs. A proper error PDU should be returned for this case.
+ See decoding function in att.c file.
+
+ Priority: Medium
+ Complexity: C1
+
+- Refactor read_by_group() and read_by_type() in src/attrib-server.c
+ (they've grown simply too big). First step could be to move out the
+ long for-loops to new functions called e.g. get_groups() and get_types().
+
+ Priority: Low
+ Complexity: C1
+
+- Agent for characteristics: Agent interface should be extended to support
+ authorization per characteristic if the remote is not in the trusted list.
+
+ Priority: Low
+ Complexity: C1
+
+- gatttool should have the ability to wait for req responses before
+ quitting (some servers require a small sleep even with cmd's). Maybe a
+ --delay-exit or --timeout command line switch.
+
+ Priority: Low
+ Complexity: C1
+
+- Refactoring of gatt.c functions. Currently, the callbacks of the services
+ and characteristics discovery functions return the ATT PDU and the caller
+ needs to call again the same function to fetch the remaining data when
+ necessary. Investigate if all results can be returned in the callback
+ result to avoid repeated code. Before change the code, please analyze
+ if this change will not break the GATT/ATT qualification tests. Maybe
+ an interactive fetch/query is necessary to pass the tests.
+
+ Priority: Low
+ Complexity: C1
+
+- Client needs to export a property in the Device Characteristic hierarchy
+ to manage characteristic value changes reports in the remote device.
+ Currently, Client Characteristic Configuration attribute is not exposed
+ as an object. The user needs to use gatttool to change the value of the
+ this attribute to receive notification/indications. Export this attribute
+ as a property is a proposal that needs further discussion.
+
+ Priority: Low
+ Complexity: C1
+
+- Attribute server should process queued GATT/ATT commands if the
+ client disconnects. The client can simply send a command and quit,
+ without wait for a response(ex: Write Command). For this scenario
+ that the client disconnects the link quickly the queued received
+ command is ignored.
+
+ Priority: Low
+ Complecity: C1
+
+- Add sdp discovery support to gattool with BR (--sdp, default is 0x1f)
+
+ Priority: Low
+ Complexity: C1
+
+- Implement Server characteristic Configuration support in the attribute
+ server to manage characteristic value broadcasting. There is a single
+ instance of the Server Characteristic Configuration for all clients.
+ See Volume 3, Part G, section 3.3.3.4 for more information.
+
+ Priority: Low
+ Complexity: C1
+
+- Long write is not implemented. Attribute server, client and command line
+ tool shall be changed to support this feature.
+
+ Priority: Low
+ Complexity: C2
+
+- Define attribute server API. External applications needs to register,
+ change attributes and to be notified about changes. Example: Proximity,
+ Time and Alert Profiles. "Local Service hierarchy" in the attribute-api
+ needs to be proposed and a RFC shall be sent to the ML.
+
+ Priority: Low
+ Complexity: C2
+ Owner: Anderson Lizardo <anderson.lizardo@openbossa.org>
+
+Management Interface
+====================
+
+- Device discovery support (both for BR/EDR & LE)
+
+ Priority: High
+ Complexity: C3
+
+- EIR generation support
+
+ Priority: High
+ Complexity: C2
+
+- Blacklist support
+
+ Priority: Medium
+ Complexity: C1
+
+- mgmt_set_fast_connectable
+
+ Priority: Medium
+ Complexity: C1
+
+- Whitelist support (initially only for LE)
+
+ Priority: Medium
+ Complexity: C2
+ Owner: Andre Guedes <andre.guedes@openbossa.org>
diff --git a/acinclude.m4 b/acinclude.m4
new file mode 100644
index 0000000..69e0740
--- /dev/null
+++ b/acinclude.m4
@@ -0,0 +1,405 @@
+AC_DEFUN([AC_PROG_CC_PIE], [
+ AC_CACHE_CHECK([whether ${CC-cc} accepts -fPIE], ac_cv_prog_cc_pie, [
+ echo 'void f(){}' > conftest.c
+ if test -z "`${CC-cc} -fPIE -pie -c conftest.c 2>&1`"; then
+ ac_cv_prog_cc_pie=yes
+ else
+ ac_cv_prog_cc_pie=no
+ fi
+ rm -rf conftest*
+ ])
+])
+
+AC_DEFUN([COMPILER_FLAGS], [
+ if (test "${CFLAGS}" = ""); then
+ CFLAGS="-Wall -O2"
+ fi
+ if (test "$USE_MAINTAINER_MODE" = "yes"); then
+ CFLAGS="$CFLAGS -Werror -Wextra"
+ CFLAGS="$CFLAGS -Wno-unused-parameter"
+ CFLAGS="$CFLAGS -Wno-missing-field-initializers"
+ CFLAGS="$CFLAGS -Wdeclaration-after-statement"
+ CFLAGS="$CFLAGS -Wmissing-declarations"
+ CFLAGS="$CFLAGS -Wredundant-decls"
+ CFLAGS="$CFLAGS -Wcast-align"
+ CFLAGS="$CFLAGS -DG_DISABLE_DEPRECATED"
+ fi
+])
+
+AC_DEFUN([AC_FUNC_PPOLL], [
+ AC_CHECK_FUNC(ppoll, dummy=yes, AC_DEFINE(NEED_PPOLL, 1,
+ [Define to 1 if you need the ppoll() function.]))
+])
+
+AC_DEFUN([AC_INIT_BLUEZ], [
+ AC_PREFIX_DEFAULT(/usr/local)
+
+ if (test "${prefix}" = "NONE"); then
+ dnl no prefix and no sysconfdir, so default to /etc
+ if (test "$sysconfdir" = '${prefix}/etc'); then
+ AC_SUBST([sysconfdir], ['/etc'])
+ fi
+
+ dnl no prefix and no localstatedir, so default to /var
+ if (test "$localstatedir" = '${prefix}/var'); then
+ AC_SUBST([localstatedir], ['/var'])
+ fi
+
+ dnl no prefix and no libexecdir, so default to /lib
+ if (test "$libexecdir" = '${exec_prefix}/libexec'); then
+ AC_SUBST([libexecdir], ['/lib'])
+ fi
+
+ dnl no prefix and no mandir, so use ${prefix}/share/man as default
+ if (test "$mandir" = '${prefix}/man'); then
+ AC_SUBST([mandir], ['${prefix}/share/man'])
+ fi
+
+ prefix="${ac_default_prefix}"
+ fi
+
+ if (test "${libdir}" = '${exec_prefix}/lib'); then
+ libdir="${prefix}/lib"
+ fi
+
+ plugindir="${libdir}/bluetooth/plugins"
+
+ if (test "$sysconfdir" = '${prefix}/etc'); then
+ configdir="${prefix}/etc/bluetooth"
+ else
+ configdir="${sysconfdir}/bluetooth"
+ fi
+
+ if (test "$localstatedir" = '${prefix}/var'); then
+ storagedir="${prefix}/var/lib/bluetooth"
+ else
+ storagedir="${localstatedir}/lib/bluetooth"
+ fi
+
+ AC_DEFINE_UNQUOTED(CONFIGDIR, "${configdir}",
+ [Directory for the configuration files])
+ AC_DEFINE_UNQUOTED(STORAGEDIR, "${storagedir}",
+ [Directory for the storage files])
+
+ AC_SUBST(CONFIGDIR, "${configdir}")
+ AC_SUBST(STORAGEDIR, "${storagedir}")
+
+ UDEV_DATADIR="`$PKG_CONFIG --variable=udevdir udev`"
+ if (test -z "${UDEV_DATADIR}"); then
+ UDEV_DATADIR="${sysconfdir}/udev/rules.d"
+ else
+ UDEV_DATADIR="${UDEV_DATADIR}/rules.d"
+ fi
+ AC_SUBST(UDEV_DATADIR)
+])
+
+AC_DEFUN([AC_PATH_DBUS], [
+ PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.0, dummy=yes,
+ AC_MSG_ERROR(D-Bus library is required))
+ AC_CHECK_LIB(dbus-1, dbus_watch_get_unix_fd, dummy=yes,
+ AC_DEFINE(NEED_DBUS_WATCH_GET_UNIX_FD, 1,
+ [Define to 1 if you need the dbus_watch_get_unix_fd() function.]))
+ AC_CHECK_LIB(dbus-1, dbus_connection_can_send_type, dummy=yes,
+ AC_DEFINE(NEED_DBUS_CONNECTION_CAN_SEND_TYPE, 1,
+ [Define to 1 if you need the dbus_connection_can_send_type() function.]
+))
+ AC_SUBST(DBUS_CFLAGS)
+ AC_SUBST(DBUS_LIBS)
+])
+
+AC_DEFUN([AC_PATH_GLIB], [
+ PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.16, dummy=yes,
+ AC_MSG_ERROR(GLib library version 2.16 or later is required))
+ AC_SUBST(GLIB_CFLAGS)
+ AC_SUBST(GLIB_LIBS)
+])
+
+AC_DEFUN([AC_PATH_GSTREAMER], [
+ PKG_CHECK_MODULES(GSTREAMER, gstreamer-0.10 >= 0.10.30 gstreamer-plugins-base-0.10, gstreamer_found=yes,
+ AC_MSG_WARN(GStreamer library version 0.10.30 or later is required);gstreamer_found=no)
+ AC_SUBST(GSTREAMER_CFLAGS)
+ AC_SUBST(GSTREAMER_LIBS)
+ GSTREAMER_PLUGINSDIR=`$PKG_CONFIG --variable=pluginsdir gstreamer-0.10`
+ AC_SUBST(GSTREAMER_PLUGINSDIR)
+])
+
+AC_DEFUN([AC_PATH_PULSE], [
+ PKG_CHECK_MODULES(PULSE, libpulse, pulse_found=yes, pulse_found=no)
+ AC_SUBST(PULSE_CFLAGS)
+ AC_SUBST(PULSE_LIBS)
+])
+
+AC_DEFUN([AC_PATH_ALSA], [
+ PKG_CHECK_MODULES(ALSA, alsa, alsa_found=yes, alsa_found=no)
+ AC_CHECK_LIB(rt, clock_gettime, ALSA_LIBS="$ALSA_LIBS -lrt", alsa_found=no)
+ AC_SUBST(ALSA_CFLAGS)
+ AC_SUBST(ALSA_LIBS)
+])
+
+AC_DEFUN([AC_PATH_USB], [
+ PKG_CHECK_MODULES(USB, libusb, usb_found=yes, usb_found=no)
+ AC_SUBST(USB_CFLAGS)
+ AC_SUBST(USB_LIBS)
+ AC_CHECK_LIB(usb, usb_get_busses, dummy=yes,
+ AC_DEFINE(NEED_USB_GET_BUSSES, 1,
+ [Define to 1 if you need the usb_get_busses() function.]))
+ AC_CHECK_LIB(usb, usb_interrupt_read, dummy=yes,
+ AC_DEFINE(NEED_USB_INTERRUPT_READ, 1,
+ [Define to 1 if you need the usb_interrupt_read() function.]))
+])
+
+AC_DEFUN([AC_PATH_SNDFILE], [
+ PKG_CHECK_MODULES(SNDFILE, sndfile, sndfile_found=yes, sndfile_found=no)
+ AC_SUBST(SNDFILE_CFLAGS)
+ AC_SUBST(SNDFILE_LIBS)
+])
+
+AC_DEFUN([AC_PATH_READLINE], [
+ AC_CHECK_HEADER(readline/readline.h,
+ AC_CHECK_LIB(readline, main,
+ [ readline_found=yes
+ AC_SUBST(READLINE_LIBS, "-lreadline")
+ ], readline_found=no),
+ [])
+])
+
+AC_DEFUN([AC_PATH_OUI], [
+ AC_ARG_WITH(ouifile,
+ AS_HELP_STRING([--with-ouifile=PATH],[Path to the oui.txt file @<:@auto@:>@]),
+ [ac_with_ouifile=$withval],
+ [ac_with_ouifile="/var/lib/misc/oui.txt"])
+ AC_DEFINE_UNQUOTED(OUIFILE, ["$ac_with_ouifile"], [Define the OUI file path])
+])
+
+AC_DEFUN([AC_ARG_BLUEZ], [
+ debug_enable=no
+ optimization_enable=yes
+ fortify_enable=yes
+ pie_enable=yes
+ sndfile_enable=${sndfile_found}
+ hal_enable=no
+ usb_enable=${usb_found}
+ alsa_enable=${alsa_found}
+ gstreamer_enable=${gstreamer_found}
+ audio_enable=yes
+ input_enable=yes
+ serial_enable=yes
+ network_enable=yes
+ sap_enable=no
+ service_enable=yes
+ health_enable=no
+ pnat_enable=no
+ attrib_enable=no
+ tracer_enable=no
+ tools_enable=yes
+ hidd_enable=no
+ pand_enable=no
+ dund_enable=no
+ cups_enable=no
+ test_enable=no
+ bccmd_enable=no
+ pcmcia_enable=no
+ hid2hci_enable=no
+ dfutool_enable=no
+ udevrules_enable=yes
+ configfiles_enable=yes
+ telephony_driver=dummy
+ maemo6_enable=no
+ sap_driver=dummy
+ dbusoob_enable=no
+
+ AC_ARG_ENABLE(optimization, AC_HELP_STRING([--disable-optimization], [disable code optimization]), [
+ optimization_enable=${enableval}
+ ])
+
+ AC_ARG_ENABLE(fortify, AC_HELP_STRING([--disable-fortify], [disable compile time buffer checks]), [
+ fortify_enable=${enableval}
+ ])
+
+ AC_ARG_ENABLE(pie, AC_HELP_STRING([--disable-pie], [disable position independent executables flag]), [
+ pie_enable=${enableval}
+ ])
+
+ AC_ARG_ENABLE(network, AC_HELP_STRING([--disable-network], [disable network plugin]), [
+ network_enable=${enableval}
+ ])
+
+ AC_ARG_ENABLE(sap, AC_HELP_STRING([--enable-sap], [enable sap plugin]), [
+ sap_enable=${enableval}
+ ])
+
+ AC_ARG_WITH(sap, AC_HELP_STRING([--with-sap=DRIVER], [select SAP driver]), [
+ sap_driver=${withval}
+ ])
+ AC_SUBST([SAP_DRIVER], [sap-${sap_driver}.c])
+
+ AC_ARG_ENABLE(serial, AC_HELP_STRING([--disable-serial], [disable serial plugin]), [
+ serial_enable=${enableval}
+ ])
+
+ AC_ARG_ENABLE(input, AC_HELP_STRING([--disable-input], [disable input plugin]), [
+ input_enable=${enableval}
+ ])
+
+ AC_ARG_ENABLE(audio, AC_HELP_STRING([--disable-audio], [disable audio plugin]), [
+ audio_enable=${enableval}
+ ])
+
+ AC_ARG_ENABLE(service, AC_HELP_STRING([--disable-service], [disable service plugin]), [
+ service_enable=${enableval}
+ ])
+
+ AC_ARG_ENABLE(health, AC_HELP_STRING([--enable-health], [enable health plugin]), [
+ health_enable=${enableval}
+ ])
+
+ AC_ARG_ENABLE(pnat, AC_HELP_STRING([--enable-pnat], [enable pnat plugin]), [
+ pnat_enable=${enableval}
+ ])
+
+ AC_ARG_ENABLE(attrib, AC_HELP_STRING([--enable-attrib], [enable attrib plugin]), [
+ attrib_enable=${enableval}
+ ])
+
+ AC_ARG_ENABLE(gstreamer, AC_HELP_STRING([--enable-gstreamer], [enable GStreamer support]), [
+ gstreamer_enable=${enableval}
+ ])
+
+ AC_ARG_ENABLE(alsa, AC_HELP_STRING([--enable-alsa], [enable ALSA support]), [
+ alsa_enable=${enableval}
+ ])
+
+ AC_ARG_ENABLE(usb, AC_HELP_STRING([--enable-usb], [enable USB support]), [
+ usb_enable=${enableval}
+ ])
+
+ AC_ARG_ENABLE(tracer, AC_HELP_STRING([--enable-tracer], [install Tracing daemon]), [
+ tracer_enable=${enableval}
+ ])
+
+ AC_ARG_ENABLE(tools, AC_HELP_STRING([--enable-tools], [install Bluetooth utilities]), [
+ tools_enable=${enableval}
+ ])
+
+ AC_ARG_ENABLE(bccmd, AC_HELP_STRING([--enable-bccmd], [install BCCMD interface utility]), [
+ bccmd_enable=${enableval}
+ ])
+
+ AC_ARG_ENABLE(pcmcia, AC_HELP_STRING([--enable-pcmcia], [install PCMCIA serial script]), [
+ pcmcia_enable=${enableval}
+ ])
+
+ AC_ARG_ENABLE(hid2hci, AC_HELP_STRING([--enable-hid2hci], [install HID mode switching utility]), [
+ hid2hci_enable=${enableval}
+ ])
+
+ AC_ARG_ENABLE(dfutool, AC_HELP_STRING([--enable-dfutool], [install DFU firmware upgrade utility]), [
+ dfutool_enable=${enableval}
+ ])
+
+ AC_ARG_ENABLE(hidd, AC_HELP_STRING([--enable-hidd], [install HID daemon]), [
+ hidd_enable=${enableval}
+ ])
+
+ AC_ARG_ENABLE(pand, AC_HELP_STRING([--enable-pand], [install PAN daemon]), [
+ pand_enable=${enableval}
+ ])
+
+ AC_ARG_ENABLE(dund, AC_HELP_STRING([--enable-dund], [install DUN daemon]), [
+ dund_enable=${enableval}
+ ])
+
+ AC_ARG_ENABLE(cups, AC_HELP_STRING([--enable-cups], [install CUPS backend support]), [
+ cups_enable=${enableval}
+ ])
+
+ AC_ARG_ENABLE(test, AC_HELP_STRING([--enable-test], [install test programs]), [
+ test_enable=${enableval}
+ ])
+
+ AC_ARG_ENABLE(udevrules, AC_HELP_STRING([--enable-udevrules], [install Bluetooth udev rules]), [
+ udevrules_enable=${enableval}
+ ])
+
+ AC_ARG_ENABLE(configfiles, AC_HELP_STRING([--enable-configfiles], [install Bluetooth configuration files]), [
+ configfiles_enable=${enableval}
+ ])
+
+ AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug], [enable compiling with debugging information]), [
+ debug_enable=${enableval}
+ ])
+
+ AC_ARG_WITH(telephony, AC_HELP_STRING([--with-telephony=DRIVER], [select telephony driver]), [
+ telephony_driver=${withval}
+ ])
+
+ AC_SUBST([TELEPHONY_DRIVER], [telephony-${telephony_driver}.c])
+
+ AC_ARG_ENABLE(maemo6, AC_HELP_STRING([--enable-maemo6], [compile with maemo6 plugin]), [
+ maemo6_enable=${enableval}
+ ])
+
+ AC_ARG_ENABLE(dbusoob, AC_HELP_STRING([--enable-dbusoob], [compile with D-Bus OOB plugin]), [
+ dbusoob_enable=${enableval}
+ ])
+
+ AC_ARG_ENABLE(hal, AC_HELP_STRING([--enable-hal], [Use HAL to determine adapter class]), [
+ hal_enable=${enableval}
+ ])
+
+ if (test "${fortify_enable}" = "yes"); then
+ CFLAGS="$CFLAGS -D_FORTIFY_SOURCE=2"
+ fi
+
+ if (test "${pie_enable}" = "yes" && test "${ac_cv_prog_cc_pie}" = "yes"); then
+ CFLAGS="$CFLAGS -fPIC"
+ LDFLAGS="$LDFLAGS -pie"
+ fi
+
+ if (test "${debug_enable}" = "yes" && test "${ac_cv_prog_cc_g}" = "yes"); then
+ CFLAGS="$CFLAGS -g"
+ fi
+
+ if (test "${optimization_enable}" = "no"); then
+ CFLAGS="$CFLAGS -O0"
+ fi
+
+ if (test "${usb_enable}" = "yes" && test "${usb_found}" = "yes"); then
+ AC_DEFINE(HAVE_LIBUSB, 1, [Define to 1 if you have USB library.])
+ fi
+
+ AM_CONDITIONAL(SNDFILE, test "${sndfile_enable}" = "yes" && test "${sndfile_found}" = "yes")
+ AM_CONDITIONAL(USB, test "${usb_enable}" = "yes" && test "${usb_found}" = "yes")
+ AM_CONDITIONAL(SBC, test "${alsa_enable}" = "yes" || test "${gstreamer_enable}" = "yes" ||
+ test "${test_enable}" = "yes")
+ AM_CONDITIONAL(ALSA, test "${alsa_enable}" = "yes" && test "${alsa_found}" = "yes")
+ AM_CONDITIONAL(GSTREAMER, test "${gstreamer_enable}" = "yes" && test "${gstreamer_found}" = "yes")
+ AM_CONDITIONAL(AUDIOPLUGIN, test "${audio_enable}" = "yes")
+ AM_CONDITIONAL(INPUTPLUGIN, test "${input_enable}" = "yes")
+ AM_CONDITIONAL(SERIALPLUGIN, test "${serial_enable}" = "yes")
+ AM_CONDITIONAL(NETWORKPLUGIN, test "${network_enable}" = "yes")
+ AM_CONDITIONAL(SAPPLUGIN, test "${sap_enable}" = "yes")
+ AM_CONDITIONAL(SERVICEPLUGIN, test "${service_enable}" = "yes")
+ AM_CONDITIONAL(HEALTHPLUGIN, test "${health_enable}" = "yes")
+ AM_CONDITIONAL(MCAP, test "${health_enable}" = "yes")
+ AM_CONDITIONAL(HAL, test "${hal_enable}" = "yes")
+ AM_CONDITIONAL(READLINE, test "${readline_found}" = "yes")
+ AM_CONDITIONAL(ATTRIBPLUGIN, test "${attrib_enable}" = "yes")
+ AM_CONDITIONAL(ECHOPLUGIN, test "no" = "yes")
+ AM_CONDITIONAL(PNATPLUGIN, test "${pnat_enable}" = "yes")
+ AM_CONDITIONAL(TRACER, test "${tracer_enable}" = "yes")
+ AM_CONDITIONAL(HIDD, test "${hidd_enable}" = "yes")
+ AM_CONDITIONAL(PAND, test "${pand_enable}" = "yes")
+ AM_CONDITIONAL(DUND, test "${dund_enable}" = "yes")
+ AM_CONDITIONAL(CUPS, test "${cups_enable}" = "yes")
+ AM_CONDITIONAL(TEST, test "${test_enable}" = "yes")
+ AM_CONDITIONAL(TOOLS, test "${tools_enable}" = "yes")
+ AM_CONDITIONAL(BCCMD, test "${bccmd_enable}" = "yes")
+ AM_CONDITIONAL(PCMCIA, test "${pcmcia_enable}" = "yes")
+ AM_CONDITIONAL(HID2HCI, test "${hid2hci_enable}" = "yes" && test "${usb_found}" = "yes")
+ AM_CONDITIONAL(DFUTOOL, test "${dfutool_enable}" = "yes" && test "${usb_found}" = "yes")
+ AM_CONDITIONAL(UDEVRULES, test "${udevrules_enable}" = "yes")
+ AM_CONDITIONAL(CONFIGFILES, test "${configfiles_enable}" = "yes")
+ AM_CONDITIONAL(MAEMO6PLUGIN, test "${maemo6_enable}" = "yes")
+ AM_CONDITIONAL(DBUSOOBPLUGIN, test "${dbusoob_enable}" = "yes")
+])
diff --git a/aclocal.m4 b/aclocal.m4
new file mode 100644
index 0000000..342d0bd
--- /dev/null
+++ b/aclocal.m4
@@ -0,0 +1,9192 @@
+# generated automatically by aclocal 1.11.1 -*- Autoconf -*-
+
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+# 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+m4_ifndef([AC_AUTOCONF_VERSION],
+ [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
+m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.63],,
+[m4_warning([this file was generated for autoconf 2.63.
+You have another version of autoconf. It may work, but is not guaranteed to.
+If you have problems, you may need to regenerate the build system entirely.
+To do so, use the procedure documented by the package, typically `autoreconf'.])])
+
+# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*-
+#
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005,
+# 2006, 2007, 2008 Free Software Foundation, Inc.
+# Written by Gordon Matzigkeit, 1996
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+m4_define([_LT_COPYING], [dnl
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005,
+# 2006, 2007, 2008 Free Software Foundation, Inc.
+# Written by Gordon Matzigkeit, 1996
+#
+# This file is part of GNU Libtool.
+#
+# GNU Libtool 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.
+#
+# As a special exception to the GNU General Public License,
+# if you distribute this file as part of a program or library that
+# is built using GNU Libtool, you may include this file under the
+# same distribution terms that you use for the rest of that program.
+#
+# GNU Libtool 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 GNU Libtool; see the file COPYING. If not, a copy
+# can be downloaded from http://www.gnu.org/licenses/gpl.html, or
+# obtained by writing to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+])
+
+# serial 56 LT_INIT
+
+
+# LT_PREREQ(VERSION)
+# ------------------
+# Complain and exit if this libtool version is less that VERSION.
+m4_defun([LT_PREREQ],
+[m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1,
+ [m4_default([$3],
+ [m4_fatal([Libtool version $1 or higher is required],
+ 63)])],
+ [$2])])
+
+
+# _LT_CHECK_BUILDDIR
+# ------------------
+# Complain if the absolute build directory name contains unusual characters
+m4_defun([_LT_CHECK_BUILDDIR],
+[case `pwd` in
+ *\ * | *\ *)
+ AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;;
+esac
+])
+
+
+# LT_INIT([OPTIONS])
+# ------------------
+AC_DEFUN([LT_INIT],
+[AC_PREREQ([2.58])dnl We use AC_INCLUDES_DEFAULT
+AC_BEFORE([$0], [LT_LANG])dnl
+AC_BEFORE([$0], [LT_OUTPUT])dnl
+AC_BEFORE([$0], [LTDL_INIT])dnl
+m4_require([_LT_CHECK_BUILDDIR])dnl
+
+dnl Autoconf doesn't catch unexpanded LT_ macros by default:
+m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl
+m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl
+dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4
+dnl unless we require an AC_DEFUNed macro:
+AC_REQUIRE([LTOPTIONS_VERSION])dnl
+AC_REQUIRE([LTSUGAR_VERSION])dnl
+AC_REQUIRE([LTVERSION_VERSION])dnl
+AC_REQUIRE([LTOBSOLETE_VERSION])dnl
+m4_require([_LT_PROG_LTMAIN])dnl
+
+dnl Parse OPTIONS
+_LT_SET_OPTIONS([$0], [$1])
+
+# This can be used to rebuild libtool when needed
+LIBTOOL_DEPS="$ltmain"
+
+# Always use our own libtool.
+LIBTOOL='$(SHELL) $(top_builddir)/libtool'
+AC_SUBST(LIBTOOL)dnl
+
+_LT_SETUP
+
+# Only expand once:
+m4_define([LT_INIT])
+])# LT_INIT
+
+# Old names:
+AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT])
+AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_PROG_LIBTOOL], [])
+dnl AC_DEFUN([AM_PROG_LIBTOOL], [])
+
+
+# _LT_CC_BASENAME(CC)
+# -------------------
+# Calculate cc_basename. Skip known compiler wrappers and cross-prefix.
+m4_defun([_LT_CC_BASENAME],
+[for cc_temp in $1""; do
+ case $cc_temp in
+ compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;;
+ distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;;
+ \-*) ;;
+ *) break;;
+ esac
+done
+cc_basename=`$ECHO "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"`
+])
+
+
+# _LT_FILEUTILS_DEFAULTS
+# ----------------------
+# It is okay to use these file commands and assume they have been set
+# sensibly after `m4_require([_LT_FILEUTILS_DEFAULTS])'.
+m4_defun([_LT_FILEUTILS_DEFAULTS],
+[: ${CP="cp -f"}
+: ${MV="mv -f"}
+: ${RM="rm -f"}
+])# _LT_FILEUTILS_DEFAULTS
+
+
+# _LT_SETUP
+# ---------
+m4_defun([_LT_SETUP],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+_LT_DECL([], [host_alias], [0], [The host system])dnl
+_LT_DECL([], [host], [0])dnl
+_LT_DECL([], [host_os], [0])dnl
+dnl
+_LT_DECL([], [build_alias], [0], [The build system])dnl
+_LT_DECL([], [build], [0])dnl
+_LT_DECL([], [build_os], [0])dnl
+dnl
+AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([LT_PATH_LD])dnl
+AC_REQUIRE([LT_PATH_NM])dnl
+dnl
+AC_REQUIRE([AC_PROG_LN_S])dnl
+test -z "$LN_S" && LN_S="ln -s"
+_LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl
+dnl
+AC_REQUIRE([LT_CMD_MAX_LEN])dnl
+_LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl
+_LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl
+dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_CHECK_SHELL_FEATURES])dnl
+m4_require([_LT_CMD_RELOAD])dnl
+m4_require([_LT_CHECK_MAGIC_METHOD])dnl
+m4_require([_LT_CMD_OLD_ARCHIVE])dnl
+m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl
+
+_LT_CONFIG_LIBTOOL_INIT([
+# See if we are running on zsh, and set the options which allow our
+# commands through without removal of \ escapes INIT.
+if test -n "\${ZSH_VERSION+set}" ; then
+ setopt NO_GLOB_SUBST
+fi
+])
+if test -n "${ZSH_VERSION+set}" ; then
+ setopt NO_GLOB_SUBST
+fi
+
+_LT_CHECK_OBJDIR
+
+m4_require([_LT_TAG_COMPILER])dnl
+_LT_PROG_ECHO_BACKSLASH
+
+case $host_os in
+aix3*)
+ # AIX sometimes has problems with the GCC collect2 program. For some
+ # reason, if we set the COLLECT_NAMES environment variable, the problems
+ # vanish in a puff of smoke.
+ if test "X${COLLECT_NAMES+set}" != Xset; then
+ COLLECT_NAMES=
+ export COLLECT_NAMES
+ fi
+ ;;
+esac
+
+# Sed substitution that helps us do robust quoting. It backslashifies
+# metacharacters that are still active within double-quoted strings.
+sed_quote_subst='s/\([["`$\\]]\)/\\\1/g'
+
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\([["`\\]]\)/\\\1/g'
+
+# Sed substitution to delay expansion of an escaped shell variable in a
+# double_quote_subst'ed string.
+delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
+
+# Sed substitution to delay expansion of an escaped single quote.
+delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g'
+
+# Sed substitution to avoid accidental globbing in evaled expressions
+no_glob_subst='s/\*/\\\*/g'
+
+# Global variables:
+ofile=libtool
+can_build_shared=yes
+
+# All known linkers require a `.a' archive for static linking (except MSVC,
+# which needs '.lib').
+libext=a
+
+with_gnu_ld="$lt_cv_prog_gnu_ld"
+
+old_CC="$CC"
+old_CFLAGS="$CFLAGS"
+
+# Set sane defaults for various variables
+test -z "$CC" && CC=cc
+test -z "$LTCC" && LTCC=$CC
+test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS
+test -z "$LD" && LD=ld
+test -z "$ac_objext" && ac_objext=o
+
+_LT_CC_BASENAME([$compiler])
+
+# Only perform the check for file, if the check method requires it
+test -z "$MAGIC_CMD" && MAGIC_CMD=file
+case $deplibs_check_method in
+file_magic*)
+ if test "$file_magic_cmd" = '$MAGIC_CMD'; then
+ _LT_PATH_MAGIC
+ fi
+ ;;
+esac
+
+# Use C for the default configuration in the libtool script
+LT_SUPPORTED_TAG([CC])
+_LT_LANG_C_CONFIG
+_LT_LANG_DEFAULT_CONFIG
+_LT_CONFIG_COMMANDS
+])# _LT_SETUP
+
+
+# _LT_PROG_LTMAIN
+# ---------------
+# Note that this code is called both from `configure', and `config.status'
+# now that we use AC_CONFIG_COMMANDS to generate libtool. Notably,
+# `config.status' has no value for ac_aux_dir unless we are using Automake,
+# so we pass a copy along to make sure it has a sensible value anyway.
+m4_defun([_LT_PROG_LTMAIN],
+[m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl
+_LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir'])
+ltmain="$ac_aux_dir/ltmain.sh"
+])# _LT_PROG_LTMAIN
+
+
+
+# So that we can recreate a full libtool script including additional
+# tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS
+# in macros and then make a single call at the end using the `libtool'
+# label.
+
+
+# _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS])
+# ----------------------------------------
+# Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later.
+m4_define([_LT_CONFIG_LIBTOOL_INIT],
+[m4_ifval([$1],
+ [m4_append([_LT_OUTPUT_LIBTOOL_INIT],
+ [$1
+])])])
+
+# Initialize.
+m4_define([_LT_OUTPUT_LIBTOOL_INIT])
+
+
+# _LT_CONFIG_LIBTOOL([COMMANDS])
+# ------------------------------
+# Register COMMANDS to be passed to AC_CONFIG_COMMANDS later.
+m4_define([_LT_CONFIG_LIBTOOL],
+[m4_ifval([$1],
+ [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS],
+ [$1
+])])])
+
+# Initialize.
+m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS])
+
+
+# _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS])
+# -----------------------------------------------------
+m4_defun([_LT_CONFIG_SAVE_COMMANDS],
+[_LT_CONFIG_LIBTOOL([$1])
+_LT_CONFIG_LIBTOOL_INIT([$2])
+])
+
+
+# _LT_FORMAT_COMMENT([COMMENT])
+# -----------------------------
+# Add leading comment marks to the start of each line, and a trailing
+# full-stop to the whole comment if one is not present already.
+m4_define([_LT_FORMAT_COMMENT],
+[m4_ifval([$1], [
+m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])],
+ [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.])
+)])
+
+
+
+
+
+# _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?])
+# -------------------------------------------------------------------
+# CONFIGNAME is the name given to the value in the libtool script.
+# VARNAME is the (base) name used in the configure script.
+# VALUE may be 0, 1 or 2 for a computed quote escaped value based on
+# VARNAME. Any other value will be used directly.
+m4_define([_LT_DECL],
+[lt_if_append_uniq([lt_decl_varnames], [$2], [, ],
+ [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name],
+ [m4_ifval([$1], [$1], [$2])])
+ lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3])
+ m4_ifval([$4],
+ [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])])
+ lt_dict_add_subkey([lt_decl_dict], [$2],
+ [tagged?], [m4_ifval([$5], [yes], [no])])])
+])
+
+
+# _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION])
+# --------------------------------------------------------
+m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])])
+
+
+# lt_decl_tag_varnames([SEPARATOR], [VARNAME1...])
+# ------------------------------------------------
+m4_define([lt_decl_tag_varnames],
+[_lt_decl_filter([tagged?], [yes], $@)])
+
+
+# _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..])
+# ---------------------------------------------------------
+m4_define([_lt_decl_filter],
+[m4_case([$#],
+ [0], [m4_fatal([$0: too few arguments: $#])],
+ [1], [m4_fatal([$0: too few arguments: $#: $1])],
+ [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)],
+ [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)],
+ [lt_dict_filter([lt_decl_dict], $@)])[]dnl
+])
+
+
+# lt_decl_quote_varnames([SEPARATOR], [VARNAME1...])
+# --------------------------------------------------
+m4_define([lt_decl_quote_varnames],
+[_lt_decl_filter([value], [1], $@)])
+
+
+# lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...])
+# ---------------------------------------------------
+m4_define([lt_decl_dquote_varnames],
+[_lt_decl_filter([value], [2], $@)])
+
+
+# lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...])
+# ---------------------------------------------------
+m4_define([lt_decl_varnames_tagged],
+[m4_assert([$# <= 2])dnl
+_$0(m4_quote(m4_default([$1], [[, ]])),
+ m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]),
+ m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))])
+m4_define([_lt_decl_varnames_tagged],
+[m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])])
+
+
+# lt_decl_all_varnames([SEPARATOR], [VARNAME1...])
+# ------------------------------------------------
+m4_define([lt_decl_all_varnames],
+[_$0(m4_quote(m4_default([$1], [[, ]])),
+ m4_if([$2], [],
+ m4_quote(lt_decl_varnames),
+ m4_quote(m4_shift($@))))[]dnl
+])
+m4_define([_lt_decl_all_varnames],
+[lt_join($@, lt_decl_varnames_tagged([$1],
+ lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl
+])
+
+
+# _LT_CONFIG_STATUS_DECLARE([VARNAME])
+# ------------------------------------
+# Quote a variable value, and forward it to `config.status' so that its
+# declaration there will have the same value as in `configure'. VARNAME
+# must have a single quote delimited value for this to work.
+m4_define([_LT_CONFIG_STATUS_DECLARE],
+[$1='`$ECHO "X$][$1" | $Xsed -e "$delay_single_quote_subst"`'])
+
+
+# _LT_CONFIG_STATUS_DECLARATIONS
+# ------------------------------
+# We delimit libtool config variables with single quotes, so when
+# we write them to config.status, we have to be sure to quote all
+# embedded single quotes properly. In configure, this macro expands
+# each variable declared with _LT_DECL (and _LT_TAGDECL) into:
+#
+# <var>='`$ECHO "X$<var>" | $Xsed -e "$delay_single_quote_subst"`'
+m4_defun([_LT_CONFIG_STATUS_DECLARATIONS],
+[m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames),
+ [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])])
+
+
+# _LT_LIBTOOL_TAGS
+# ----------------
+# Output comment and list of tags supported by the script
+m4_defun([_LT_LIBTOOL_TAGS],
+[_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl
+available_tags="_LT_TAGS"dnl
+])
+
+
+# _LT_LIBTOOL_DECLARE(VARNAME, [TAG])
+# -----------------------------------
+# Extract the dictionary values for VARNAME (optionally with TAG) and
+# expand to a commented shell variable setting:
+#
+# # Some comment about what VAR is for.
+# visible_name=$lt_internal_name
+m4_define([_LT_LIBTOOL_DECLARE],
+[_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1],
+ [description])))[]dnl
+m4_pushdef([_libtool_name],
+ m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl
+m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])),
+ [0], [_libtool_name=[$]$1],
+ [1], [_libtool_name=$lt_[]$1],
+ [2], [_libtool_name=$lt_[]$1],
+ [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl
+m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl
+])
+
+
+# _LT_LIBTOOL_CONFIG_VARS
+# -----------------------
+# Produce commented declarations of non-tagged libtool config variables
+# suitable for insertion in the LIBTOOL CONFIG section of the `libtool'
+# script. Tagged libtool config variables (even for the LIBTOOL CONFIG
+# section) are produced by _LT_LIBTOOL_TAG_VARS.
+m4_defun([_LT_LIBTOOL_CONFIG_VARS],
+[m4_foreach([_lt_var],
+ m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)),
+ [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])])
+
+
+# _LT_LIBTOOL_TAG_VARS(TAG)
+# -------------------------
+m4_define([_LT_LIBTOOL_TAG_VARS],
+[m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames),
+ [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])])
+
+
+# _LT_TAGVAR(VARNAME, [TAGNAME])
+# ------------------------------
+m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])])
+
+
+# _LT_CONFIG_COMMANDS
+# -------------------
+# Send accumulated output to $CONFIG_STATUS. Thanks to the lists of
+# variables for single and double quote escaping we saved from calls
+# to _LT_DECL, we can put quote escaped variables declarations
+# into `config.status', and then the shell code to quote escape them in
+# for loops in `config.status'. Finally, any additional code accumulated
+# from calls to _LT_CONFIG_LIBTOOL_INIT is expanded.
+m4_defun([_LT_CONFIG_COMMANDS],
+[AC_PROVIDE_IFELSE([LT_OUTPUT],
+ dnl If the libtool generation code has been placed in $CONFIG_LT,
+ dnl instead of duplicating it all over again into config.status,
+ dnl then we will have config.status run $CONFIG_LT later, so it
+ dnl needs to know what name is stored there:
+ [AC_CONFIG_COMMANDS([libtool],
+ [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])],
+ dnl If the libtool generation code is destined for config.status,
+ dnl expand the accumulated commands and init code now:
+ [AC_CONFIG_COMMANDS([libtool],
+ [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])])
+])#_LT_CONFIG_COMMANDS
+
+
+# Initialize.
+m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT],
+[
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+sed_quote_subst='$sed_quote_subst'
+double_quote_subst='$double_quote_subst'
+delay_variable_subst='$delay_variable_subst'
+_LT_CONFIG_STATUS_DECLARATIONS
+LTCC='$LTCC'
+LTCFLAGS='$LTCFLAGS'
+compiler='$compiler_DEFAULT'
+
+# Quote evaled strings.
+for var in lt_decl_all_varnames([[ \
+]], lt_decl_quote_varnames); do
+ case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in
+ *[[\\\\\\\`\\"\\\$]]*)
+ eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$sed_quote_subst\\"\\\`\\\\\\""
+ ;;
+ *)
+ eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+ ;;
+ esac
+done
+
+# Double-quote double-evaled strings.
+for var in lt_decl_all_varnames([[ \
+]], lt_decl_dquote_varnames); do
+ case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in
+ *[[\\\\\\\`\\"\\\$]]*)
+ eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\""
+ ;;
+ *)
+ eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+ ;;
+ esac
+done
+
+# Fix-up fallback echo if it was mangled by the above quoting rules.
+case \$lt_ECHO in
+*'\\\[$]0 --fallback-echo"')dnl "
+ lt_ECHO=\`\$ECHO "X\$lt_ECHO" | \$Xsed -e 's/\\\\\\\\\\\\\\\[$]0 --fallback-echo"\[$]/\[$]0 --fallback-echo"/'\`
+ ;;
+esac
+
+_LT_OUTPUT_LIBTOOL_INIT
+])
+
+
+# LT_OUTPUT
+# ---------
+# This macro allows early generation of the libtool script (before
+# AC_OUTPUT is called), incase it is used in configure for compilation
+# tests.
+AC_DEFUN([LT_OUTPUT],
+[: ${CONFIG_LT=./config.lt}
+AC_MSG_NOTICE([creating $CONFIG_LT])
+cat >"$CONFIG_LT" <<_LTEOF
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate a libtool stub with the current configuration.
+
+lt_cl_silent=false
+SHELL=\${CONFIG_SHELL-$SHELL}
+_LTEOF
+
+cat >>"$CONFIG_LT" <<\_LTEOF
+AS_SHELL_SANITIZE
+_AS_PREPARE
+
+exec AS_MESSAGE_FD>&1
+exec AS_MESSAGE_LOG_FD>>config.log
+{
+ echo
+ AS_BOX([Running $as_me.])
+} >&AS_MESSAGE_LOG_FD
+
+lt_cl_help="\
+\`$as_me' creates a local libtool stub from the current configuration,
+for use in further configure time tests before the real libtool is
+generated.
+
+Usage: $[0] [[OPTIONS]]
+
+ -h, --help print this help, then exit
+ -V, --version print version number, then exit
+ -q, --quiet do not print progress messages
+ -d, --debug don't remove temporary files
+
+Report bugs to <bug-libtool@gnu.org>."
+
+lt_cl_version="\
+m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl
+m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION])
+configured by $[0], generated by m4_PACKAGE_STRING.
+
+Copyright (C) 2008 Free Software Foundation, Inc.
+This config.lt script is free software; the Free Software Foundation
+gives unlimited permision to copy, distribute and modify it."
+
+while test $[#] != 0
+do
+ case $[1] in
+ --version | --v* | -V )
+ echo "$lt_cl_version"; exit 0 ;;
+ --help | --h* | -h )
+ echo "$lt_cl_help"; exit 0 ;;
+ --debug | --d* | -d )
+ debug=: ;;
+ --quiet | --q* | --silent | --s* | -q )
+ lt_cl_silent=: ;;
+
+ -*) AC_MSG_ERROR([unrecognized option: $[1]
+Try \`$[0] --help' for more information.]) ;;
+
+ *) AC_MSG_ERROR([unrecognized argument: $[1]
+Try \`$[0] --help' for more information.]) ;;
+ esac
+ shift
+done
+
+if $lt_cl_silent; then
+ exec AS_MESSAGE_FD>/dev/null
+fi
+_LTEOF
+
+cat >>"$CONFIG_LT" <<_LTEOF
+_LT_OUTPUT_LIBTOOL_COMMANDS_INIT
+_LTEOF
+
+cat >>"$CONFIG_LT" <<\_LTEOF
+AC_MSG_NOTICE([creating $ofile])
+_LT_OUTPUT_LIBTOOL_COMMANDS
+AS_EXIT(0)
+_LTEOF
+chmod +x "$CONFIG_LT"
+
+# configure is writing to config.log, but config.lt does its own redirection,
+# appending to config.log, which fails on DOS, as config.log is still kept
+# open by configure. Here we exec the FD to /dev/null, effectively closing
+# config.log, so it can be properly (re)opened and appended to by config.lt.
+if test "$no_create" != yes; then
+ lt_cl_success=:
+ test "$silent" = yes &&
+ lt_config_lt_args="$lt_config_lt_args --quiet"
+ exec AS_MESSAGE_LOG_FD>/dev/null
+ $SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false
+ exec AS_MESSAGE_LOG_FD>>config.log
+ $lt_cl_success || AS_EXIT(1)
+fi
+])# LT_OUTPUT
+
+
+# _LT_CONFIG(TAG)
+# ---------------
+# If TAG is the built-in tag, create an initial libtool script with a
+# default configuration from the untagged config vars. Otherwise add code
+# to config.status for appending the configuration named by TAG from the
+# matching tagged config vars.
+m4_defun([_LT_CONFIG],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+_LT_CONFIG_SAVE_COMMANDS([
+ m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl
+ m4_if(_LT_TAG, [C], [
+ # See if we are running on zsh, and set the options which allow our
+ # commands through without removal of \ escapes.
+ if test -n "${ZSH_VERSION+set}" ; then
+ setopt NO_GLOB_SUBST
+ fi
+
+ cfgfile="${ofile}T"
+ trap "$RM \"$cfgfile\"; exit 1" 1 2 15
+ $RM "$cfgfile"
+
+ cat <<_LT_EOF >> "$cfgfile"
+#! $SHELL
+
+# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services.
+# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION
+# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+# NOTE: Changes made to this file will be lost: look at ltmain.sh.
+#
+_LT_COPYING
+_LT_LIBTOOL_TAGS
+
+# ### BEGIN LIBTOOL CONFIG
+_LT_LIBTOOL_CONFIG_VARS
+_LT_LIBTOOL_TAG_VARS
+# ### END LIBTOOL CONFIG
+
+_LT_EOF
+
+ case $host_os in
+ aix3*)
+ cat <<\_LT_EOF >> "$cfgfile"
+# AIX sometimes has problems with the GCC collect2 program. For some
+# reason, if we set the COLLECT_NAMES environment variable, the problems
+# vanish in a puff of smoke.
+if test "X${COLLECT_NAMES+set}" != Xset; then
+ COLLECT_NAMES=
+ export COLLECT_NAMES
+fi
+_LT_EOF
+ ;;
+ esac
+
+ _LT_PROG_LTMAIN
+
+ # We use sed instead of cat because bash on DJGPP gets confused if
+ # if finds mixed CR/LF and LF-only lines. Since sed operates in
+ # text mode, it properly converts lines to CR/LF. This bash problem
+ # is reportedly fixed, but why not run on old versions too?
+ sed '/^# Generated shell functions inserted here/q' "$ltmain" >> "$cfgfile" \
+ || (rm -f "$cfgfile"; exit 1)
+
+ _LT_PROG_XSI_SHELLFNS
+
+ sed -n '/^# Generated shell functions inserted here/,$p' "$ltmain" >> "$cfgfile" \
+ || (rm -f "$cfgfile"; exit 1)
+
+ mv -f "$cfgfile" "$ofile" ||
+ (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile")
+ chmod +x "$ofile"
+],
+[cat <<_LT_EOF >> "$ofile"
+
+dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded
+dnl in a comment (ie after a #).
+# ### BEGIN LIBTOOL TAG CONFIG: $1
+_LT_LIBTOOL_TAG_VARS(_LT_TAG)
+# ### END LIBTOOL TAG CONFIG: $1
+_LT_EOF
+])dnl /m4_if
+],
+[m4_if([$1], [], [
+ PACKAGE='$PACKAGE'
+ VERSION='$VERSION'
+ TIMESTAMP='$TIMESTAMP'
+ RM='$RM'
+ ofile='$ofile'], [])
+])dnl /_LT_CONFIG_SAVE_COMMANDS
+])# _LT_CONFIG
+
+
+# LT_SUPPORTED_TAG(TAG)
+# ---------------------
+# Trace this macro to discover what tags are supported by the libtool
+# --tag option, using:
+# autoconf --trace 'LT_SUPPORTED_TAG:$1'
+AC_DEFUN([LT_SUPPORTED_TAG], [])
+
+
+# C support is built-in for now
+m4_define([_LT_LANG_C_enabled], [])
+m4_define([_LT_TAGS], [])
+
+
+# LT_LANG(LANG)
+# -------------
+# Enable libtool support for the given language if not already enabled.
+AC_DEFUN([LT_LANG],
+[AC_BEFORE([$0], [LT_OUTPUT])dnl
+m4_case([$1],
+ [C], [_LT_LANG(C)],
+ [C++], [_LT_LANG(CXX)],
+ [Java], [_LT_LANG(GCJ)],
+ [Fortran 77], [_LT_LANG(F77)],
+ [Fortran], [_LT_LANG(FC)],
+ [Windows Resource], [_LT_LANG(RC)],
+ [m4_ifdef([_LT_LANG_]$1[_CONFIG],
+ [_LT_LANG($1)],
+ [m4_fatal([$0: unsupported language: "$1"])])])dnl
+])# LT_LANG
+
+
+# _LT_LANG(LANGNAME)
+# ------------------
+m4_defun([_LT_LANG],
+[m4_ifdef([_LT_LANG_]$1[_enabled], [],
+ [LT_SUPPORTED_TAG([$1])dnl
+ m4_append([_LT_TAGS], [$1 ])dnl
+ m4_define([_LT_LANG_]$1[_enabled], [])dnl
+ _LT_LANG_$1_CONFIG($1)])dnl
+])# _LT_LANG
+
+
+# _LT_LANG_DEFAULT_CONFIG
+# -----------------------
+m4_defun([_LT_LANG_DEFAULT_CONFIG],
+[AC_PROVIDE_IFELSE([AC_PROG_CXX],
+ [LT_LANG(CXX)],
+ [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])])
+
+AC_PROVIDE_IFELSE([AC_PROG_F77],
+ [LT_LANG(F77)],
+ [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])])
+
+AC_PROVIDE_IFELSE([AC_PROG_FC],
+ [LT_LANG(FC)],
+ [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])])
+
+dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal
+dnl pulling things in needlessly.
+AC_PROVIDE_IFELSE([AC_PROG_GCJ],
+ [LT_LANG(GCJ)],
+ [AC_PROVIDE_IFELSE([A][M_PROG_GCJ],
+ [LT_LANG(GCJ)],
+ [AC_PROVIDE_IFELSE([LT_PROG_GCJ],
+ [LT_LANG(GCJ)],
+ [m4_ifdef([AC_PROG_GCJ],
+ [m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])])
+ m4_ifdef([A][M_PROG_GCJ],
+ [m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])])
+ m4_ifdef([LT_PROG_GCJ],
+ [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])])
+
+AC_PROVIDE_IFELSE([LT_PROG_RC],
+ [LT_LANG(RC)],
+ [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])])
+])# _LT_LANG_DEFAULT_CONFIG
+
+# Obsolete macros:
+AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)])
+AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)])
+AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)])
+AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_CXX], [])
+dnl AC_DEFUN([AC_LIBTOOL_F77], [])
+dnl AC_DEFUN([AC_LIBTOOL_FC], [])
+dnl AC_DEFUN([AC_LIBTOOL_GCJ], [])
+
+
+# _LT_TAG_COMPILER
+# ----------------
+m4_defun([_LT_TAG_COMPILER],
+[AC_REQUIRE([AC_PROG_CC])dnl
+
+_LT_DECL([LTCC], [CC], [1], [A C compiler])dnl
+_LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl
+_LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl
+_LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+])# _LT_TAG_COMPILER
+
+
+# _LT_COMPILER_BOILERPLATE
+# ------------------------
+# Check for compiler boilerplate output or warnings with
+# the simple compiler test code.
+m4_defun([_LT_COMPILER_BOILERPLATE],
+[m4_require([_LT_DECL_SED])dnl
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_compile_test_code" >conftest.$ac_ext
+eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_compiler_boilerplate=`cat conftest.err`
+$RM conftest*
+])# _LT_COMPILER_BOILERPLATE
+
+
+# _LT_LINKER_BOILERPLATE
+# ----------------------
+# Check for linker boilerplate output or warnings with
+# the simple link test code.
+m4_defun([_LT_LINKER_BOILERPLATE],
+[m4_require([_LT_DECL_SED])dnl
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_link_test_code" >conftest.$ac_ext
+eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_linker_boilerplate=`cat conftest.err`
+$RM -r conftest*
+])# _LT_LINKER_BOILERPLATE
+
+# _LT_REQUIRED_DARWIN_CHECKS
+# -------------------------
+m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[
+ case $host_os in
+ rhapsody* | darwin*)
+ AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:])
+ AC_CHECK_TOOL([NMEDIT], [nmedit], [:])
+ AC_CHECK_TOOL([LIPO], [lipo], [:])
+ AC_CHECK_TOOL([OTOOL], [otool], [:])
+ AC_CHECK_TOOL([OTOOL64], [otool64], [:])
+ _LT_DECL([], [DSYMUTIL], [1],
+ [Tool to manipulate archived DWARF debug symbol files on Mac OS X])
+ _LT_DECL([], [NMEDIT], [1],
+ [Tool to change global to local symbols on Mac OS X])
+ _LT_DECL([], [LIPO], [1],
+ [Tool to manipulate fat objects and archives on Mac OS X])
+ _LT_DECL([], [OTOOL], [1],
+ [ldd/readelf like tool for Mach-O binaries on Mac OS X])
+ _LT_DECL([], [OTOOL64], [1],
+ [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4])
+
+ AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod],
+ [lt_cv_apple_cc_single_mod=no
+ if test -z "${LT_MULTI_MODULE}"; then
+ # By default we will add the -single_module flag. You can override
+ # by either setting the environment variable LT_MULTI_MODULE
+ # non-empty at configure time, or by adding -multi_module to the
+ # link flags.
+ rm -rf libconftest.dylib*
+ echo "int foo(void){return 1;}" > conftest.c
+ echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+-dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD
+ $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err
+ _lt_result=$?
+ if test -f libconftest.dylib && test ! -s conftest.err && test $_lt_result = 0; then
+ lt_cv_apple_cc_single_mod=yes
+ else
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ fi
+ rm -rf libconftest.dylib*
+ rm -f conftest.*
+ fi])
+ AC_CACHE_CHECK([for -exported_symbols_list linker flag],
+ [lt_cv_ld_exported_symbols_list],
+ [lt_cv_ld_exported_symbols_list=no
+ save_LDFLAGS=$LDFLAGS
+ echo "_main" > conftest.sym
+ LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym"
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])],
+ [lt_cv_ld_exported_symbols_list=yes],
+ [lt_cv_ld_exported_symbols_list=no])
+ LDFLAGS="$save_LDFLAGS"
+ ])
+ case $host_os in
+ rhapsody* | darwin1.[[012]])
+ _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;;
+ darwin1.*)
+ _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+ darwin*) # darwin 5.x on
+ # if running on 10.5 or later, the deployment target defaults
+ # to the OS version, if on x86, and 10.4, the deployment
+ # target defaults to 10.4. Don't you love it?
+ case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
+ 10.0,*86*-darwin8*|10.0,*-darwin[[91]]*)
+ _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
+ 10.[[012]]*)
+ _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+ 10.*)
+ _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
+ esac
+ ;;
+ esac
+ if test "$lt_cv_apple_cc_single_mod" = "yes"; then
+ _lt_dar_single_mod='$single_module'
+ fi
+ if test "$lt_cv_ld_exported_symbols_list" = "yes"; then
+ _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym'
+ else
+ _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}'
+ fi
+ if test "$DSYMUTIL" != ":"; then
+ _lt_dsymutil='~$DSYMUTIL $lib || :'
+ else
+ _lt_dsymutil=
+ fi
+ ;;
+ esac
+])
+
+
+# _LT_DARWIN_LINKER_FEATURES
+# --------------------------
+# Checks for linker and compiler features on darwin
+m4_defun([_LT_DARWIN_LINKER_FEATURES],
+[
+ m4_require([_LT_REQUIRED_DARWIN_CHECKS])
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_automatic, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=''
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ _LT_TAGVAR(allow_undefined_flag, $1)="$_lt_dar_allow_undefined"
+ case $cc_basename in
+ ifort*) _lt_dar_can_shared=yes ;;
+ *) _lt_dar_can_shared=$GCC ;;
+ esac
+ if test "$_lt_dar_can_shared" = "yes"; then
+ output_verbose_link_cmd=echo
+ _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}"
+ _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}"
+ _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}"
+ _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}"
+ m4_if([$1], [CXX],
+[ if test "$lt_cv_apple_cc_single_mod" != "yes"; then
+ _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}"
+ _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}"
+ fi
+],[])
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+])
+
+# _LT_SYS_MODULE_PATH_AIX
+# -----------------------
+# Links a minimal program and checks the executable
+# for the system default hardcoded library path. In most cases,
+# this is /usr/lib:/lib, but when the MPI compilers are used
+# the location of the communication and MPI libs are included too.
+# If we don't find anything, use the default library path according
+# to the aix ld manual.
+m4_defun([_LT_SYS_MODULE_PATH_AIX],
+[m4_require([_LT_DECL_SED])dnl
+AC_LINK_IFELSE(AC_LANG_PROGRAM,[
+lt_aix_libpath_sed='
+ /Import File Strings/,/^$/ {
+ /^0/ {
+ s/^0 *\(.*\)$/\1/
+ p
+ }
+ }'
+aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+# Check for a 64-bit object if we didn't find anything.
+if test -z "$aix_libpath"; then
+ aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+fi],[])
+if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
+])# _LT_SYS_MODULE_PATH_AIX
+
+
+# _LT_SHELL_INIT(ARG)
+# -------------------
+m4_define([_LT_SHELL_INIT],
+[ifdef([AC_DIVERSION_NOTICE],
+ [AC_DIVERT_PUSH(AC_DIVERSION_NOTICE)],
+ [AC_DIVERT_PUSH(NOTICE)])
+$1
+AC_DIVERT_POP
+])# _LT_SHELL_INIT
+
+
+# _LT_PROG_ECHO_BACKSLASH
+# -----------------------
+# Add some code to the start of the generated configure script which
+# will find an echo command which doesn't interpret backslashes.
+m4_defun([_LT_PROG_ECHO_BACKSLASH],
+[_LT_SHELL_INIT([
+# Check that we are running under the correct shell.
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+case X$lt_ECHO in
+X*--fallback-echo)
+ # Remove one level of quotation (which was required for Make).
+ ECHO=`echo "$lt_ECHO" | sed 's,\\\\\[$]\\[$]0,'[$]0','`
+ ;;
+esac
+
+ECHO=${lt_ECHO-echo}
+if test "X[$]1" = X--no-reexec; then
+ # Discard the --no-reexec flag, and continue.
+ shift
+elif test "X[$]1" = X--fallback-echo; then
+ # Avoid inline document here, it may be left over
+ :
+elif test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' ; then
+ # Yippee, $ECHO works!
+ :
+else
+ # Restart under the correct shell.
+ exec $SHELL "[$]0" --no-reexec ${1+"[$]@"}
+fi
+
+if test "X[$]1" = X--fallback-echo; then
+ # used as fallback echo
+ shift
+ cat <<_LT_EOF
+[$]*
+_LT_EOF
+ exit 0
+fi
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+if test -z "$lt_ECHO"; then
+ if test "X${echo_test_string+set}" != Xset; then
+ # find a string as large as possible, as long as the shell can cope with it
+ for cmd in 'sed 50q "[$]0"' 'sed 20q "[$]0"' 'sed 10q "[$]0"' 'sed 2q "[$]0"' 'echo test'; do
+ # expected sizes: less than 2Kb, 1Kb, 512 bytes, 16 bytes, ...
+ if { echo_test_string=`eval $cmd`; } 2>/dev/null &&
+ { test "X$echo_test_string" = "X$echo_test_string"; } 2>/dev/null
+ then
+ break
+ fi
+ done
+ fi
+
+ if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' &&
+ echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ :
+ else
+ # The Solaris, AIX, and Digital Unix default echo programs unquote
+ # backslashes. This makes it impossible to quote backslashes using
+ # echo "$something" | sed 's/\\/\\\\/g'
+ #
+ # So, first we look for a working echo in the user's PATH.
+
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ for dir in $PATH /usr/ucb; do
+ IFS="$lt_save_ifs"
+ if (test -f $dir/echo || test -f $dir/echo$ac_exeext) &&
+ test "X`($dir/echo '\t') 2>/dev/null`" = 'X\t' &&
+ echo_testing_string=`($dir/echo "$echo_test_string") 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ ECHO="$dir/echo"
+ break
+ fi
+ done
+ IFS="$lt_save_ifs"
+
+ if test "X$ECHO" = Xecho; then
+ # We didn't find a better echo, so look for alternatives.
+ if test "X`{ print -r '\t'; } 2>/dev/null`" = 'X\t' &&
+ echo_testing_string=`{ print -r "$echo_test_string"; } 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ # This shell has a builtin print -r that does the trick.
+ ECHO='print -r'
+ elif { test -f /bin/ksh || test -f /bin/ksh$ac_exeext; } &&
+ test "X$CONFIG_SHELL" != X/bin/ksh; then
+ # If we have ksh, try running configure again with it.
+ ORIGINAL_CONFIG_SHELL=${CONFIG_SHELL-/bin/sh}
+ export ORIGINAL_CONFIG_SHELL
+ CONFIG_SHELL=/bin/ksh
+ export CONFIG_SHELL
+ exec $CONFIG_SHELL "[$]0" --no-reexec ${1+"[$]@"}
+ else
+ # Try using printf.
+ ECHO='printf %s\n'
+ if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' &&
+ echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ # Cool, printf works
+ :
+ elif echo_testing_string=`($ORIGINAL_CONFIG_SHELL "[$]0" --fallback-echo '\t') 2>/dev/null` &&
+ test "X$echo_testing_string" = 'X\t' &&
+ echo_testing_string=`($ORIGINAL_CONFIG_SHELL "[$]0" --fallback-echo "$echo_test_string") 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ CONFIG_SHELL=$ORIGINAL_CONFIG_SHELL
+ export CONFIG_SHELL
+ SHELL="$CONFIG_SHELL"
+ export SHELL
+ ECHO="$CONFIG_SHELL [$]0 --fallback-echo"
+ elif echo_testing_string=`($CONFIG_SHELL "[$]0" --fallback-echo '\t') 2>/dev/null` &&
+ test "X$echo_testing_string" = 'X\t' &&
+ echo_testing_string=`($CONFIG_SHELL "[$]0" --fallback-echo "$echo_test_string") 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ ECHO="$CONFIG_SHELL [$]0 --fallback-echo"
+ else
+ # maybe with a smaller string...
+ prev=:
+
+ for cmd in 'echo test' 'sed 2q "[$]0"' 'sed 10q "[$]0"' 'sed 20q "[$]0"' 'sed 50q "[$]0"'; do
+ if { test "X$echo_test_string" = "X`eval $cmd`"; } 2>/dev/null
+ then
+ break
+ fi
+ prev="$cmd"
+ done
+
+ if test "$prev" != 'sed 50q "[$]0"'; then
+ echo_test_string=`eval $prev`
+ export echo_test_string
+ exec ${ORIGINAL_CONFIG_SHELL-${CONFIG_SHELL-/bin/sh}} "[$]0" ${1+"[$]@"}
+ else
+ # Oops. We lost completely, so just stick with echo.
+ ECHO=echo
+ fi
+ fi
+ fi
+ fi
+ fi
+fi
+
+# Copy echo and quote the copy suitably for passing to libtool from
+# the Makefile, instead of quoting the original, which is used later.
+lt_ECHO=$ECHO
+if test "X$lt_ECHO" = "X$CONFIG_SHELL [$]0 --fallback-echo"; then
+ lt_ECHO="$CONFIG_SHELL \\\$\[$]0 --fallback-echo"
+fi
+
+AC_SUBST(lt_ECHO)
+])
+_LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts])
+_LT_DECL([], [ECHO], [1],
+ [An echo program that does not interpret backslashes])
+])# _LT_PROG_ECHO_BACKSLASH
+
+
+# _LT_ENABLE_LOCK
+# ---------------
+m4_defun([_LT_ENABLE_LOCK],
+[AC_ARG_ENABLE([libtool-lock],
+ [AS_HELP_STRING([--disable-libtool-lock],
+ [avoid locking (might break parallel builds)])])
+test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes
+
+# Some flags need to be propagated to the compiler or linker for good
+# libtool support.
+case $host in
+ia64-*-hpux*)
+ # Find out which ABI we are using.
+ echo 'int i;' > conftest.$ac_ext
+ if AC_TRY_EVAL(ac_compile); then
+ case `/usr/bin/file conftest.$ac_objext` in
+ *ELF-32*)
+ HPUX_IA64_MODE="32"
+ ;;
+ *ELF-64*)
+ HPUX_IA64_MODE="64"
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+*-*-irix6*)
+ # Find out which ABI we are using.
+ echo '[#]line __oline__ "configure"' > conftest.$ac_ext
+ if AC_TRY_EVAL(ac_compile); then
+ if test "$lt_cv_prog_gnu_ld" = yes; then
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ LD="${LD-ld} -melf32bsmip"
+ ;;
+ *N32*)
+ LD="${LD-ld} -melf32bmipn32"
+ ;;
+ *64-bit*)
+ LD="${LD-ld} -melf64bmip"
+ ;;
+ esac
+ else
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ LD="${LD-ld} -32"
+ ;;
+ *N32*)
+ LD="${LD-ld} -n32"
+ ;;
+ *64-bit*)
+ LD="${LD-ld} -64"
+ ;;
+ esac
+ fi
+ fi
+ rm -rf conftest*
+ ;;
+
+x86_64-*kfreebsd*-gnu|x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*| \
+s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
+ # Find out which ABI we are using.
+ echo 'int i;' > conftest.$ac_ext
+ if AC_TRY_EVAL(ac_compile); then
+ case `/usr/bin/file conftest.o` in
+ *32-bit*)
+ case $host in
+ x86_64-*kfreebsd*-gnu)
+ LD="${LD-ld} -m elf_i386_fbsd"
+ ;;
+ x86_64-*linux*)
+ LD="${LD-ld} -m elf_i386"
+ ;;
+ ppc64-*linux*|powerpc64-*linux*)
+ LD="${LD-ld} -m elf32ppclinux"
+ ;;
+ s390x-*linux*)
+ LD="${LD-ld} -m elf_s390"
+ ;;
+ sparc64-*linux*)
+ LD="${LD-ld} -m elf32_sparc"
+ ;;
+ esac
+ ;;
+ *64-bit*)
+ case $host in
+ x86_64-*kfreebsd*-gnu)
+ LD="${LD-ld} -m elf_x86_64_fbsd"
+ ;;
+ x86_64-*linux*)
+ LD="${LD-ld} -m elf_x86_64"
+ ;;
+ ppc*-*linux*|powerpc*-*linux*)
+ LD="${LD-ld} -m elf64ppc"
+ ;;
+ s390*-*linux*|s390*-*tpf*)
+ LD="${LD-ld} -m elf64_s390"
+ ;;
+ sparc*-*linux*)
+ LD="${LD-ld} -m elf64_sparc"
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+
+*-*-sco3.2v5*)
+ # On SCO OpenServer 5, we need -belf to get full-featured binaries.
+ SAVE_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS -belf"
+ AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf,
+ [AC_LANG_PUSH(C)
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no])
+ AC_LANG_POP])
+ if test x"$lt_cv_cc_needs_belf" != x"yes"; then
+ # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf
+ CFLAGS="$SAVE_CFLAGS"
+ fi
+ ;;
+sparc*-*solaris*)
+ # Find out which ABI we are using.
+ echo 'int i;' > conftest.$ac_ext
+ if AC_TRY_EVAL(ac_compile); then
+ case `/usr/bin/file conftest.o` in
+ *64-bit*)
+ case $lt_cv_prog_gnu_ld in
+ yes*) LD="${LD-ld} -m elf64_sparc" ;;
+ *)
+ if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then
+ LD="${LD-ld} -64"
+ fi
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+esac
+
+need_locks="$enable_libtool_lock"
+])# _LT_ENABLE_LOCK
+
+
+# _LT_CMD_OLD_ARCHIVE
+# -------------------
+m4_defun([_LT_CMD_OLD_ARCHIVE],
+[AC_CHECK_TOOL(AR, ar, false)
+test -z "$AR" && AR=ar
+test -z "$AR_FLAGS" && AR_FLAGS=cru
+_LT_DECL([], [AR], [1], [The archiver])
+_LT_DECL([], [AR_FLAGS], [1])
+
+AC_CHECK_TOOL(STRIP, strip, :)
+test -z "$STRIP" && STRIP=:
+_LT_DECL([], [STRIP], [1], [A symbol stripping program])
+
+AC_CHECK_TOOL(RANLIB, ranlib, :)
+test -z "$RANLIB" && RANLIB=:
+_LT_DECL([], [RANLIB], [1],
+ [Commands used to install an old-style archive])
+
+# Determine commands to create old-style static archives.
+old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs'
+old_postinstall_cmds='chmod 644 $oldlib'
+old_postuninstall_cmds=
+
+if test -n "$RANLIB"; then
+ case $host_os in
+ openbsd*)
+ old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$oldlib"
+ ;;
+ *)
+ old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$oldlib"
+ ;;
+ esac
+ old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib"
+fi
+_LT_DECL([], [old_postinstall_cmds], [2])
+_LT_DECL([], [old_postuninstall_cmds], [2])
+_LT_TAGDECL([], [old_archive_cmds], [2],
+ [Commands used to build an old-style archive])
+])# _LT_CMD_OLD_ARCHIVE
+
+
+# _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS,
+# [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE])
+# ----------------------------------------------------------------
+# Check whether the given compiler option works
+AC_DEFUN([_LT_COMPILER_OPTION],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_SED])dnl
+AC_CACHE_CHECK([$1], [$2],
+ [$2=no
+ m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4])
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+ lt_compiler_flag="$3"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ # The option is referenced via a variable to avoid confusing sed.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD)
+ (eval "$lt_compile" 2>conftest.err)
+ ac_status=$?
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ echo "$as_me:__oline__: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
+ if (exit $ac_status) && test -s "$ac_outfile"; then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings other than the usual output.
+ $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+ $2=yes
+ fi
+ fi
+ $RM conftest*
+])
+
+if test x"[$]$2" = xyes; then
+ m4_if([$5], , :, [$5])
+else
+ m4_if([$6], , :, [$6])
+fi
+])# _LT_COMPILER_OPTION
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], [])
+
+
+# _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS,
+# [ACTION-SUCCESS], [ACTION-FAILURE])
+# ----------------------------------------------------
+# Check whether the given linker option works
+AC_DEFUN([_LT_LINKER_OPTION],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_SED])dnl
+AC_CACHE_CHECK([$1], [$2],
+ [$2=no
+ save_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS $3"
+ echo "$lt_simple_link_test_code" > conftest.$ac_ext
+ if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+ # The linker can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ if test -s conftest.err; then
+ # Append any errors to the config.log.
+ cat conftest.err 1>&AS_MESSAGE_LOG_FD
+ $ECHO "X$_lt_linker_boilerplate" | $Xsed -e '/^$/d' > conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if diff conftest.exp conftest.er2 >/dev/null; then
+ $2=yes
+ fi
+ else
+ $2=yes
+ fi
+ fi
+ $RM -r conftest*
+ LDFLAGS="$save_LDFLAGS"
+])
+
+if test x"[$]$2" = xyes; then
+ m4_if([$4], , :, [$4])
+else
+ m4_if([$5], , :, [$5])
+fi
+])# _LT_LINKER_OPTION
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], [])
+
+
+# LT_CMD_MAX_LEN
+#---------------
+AC_DEFUN([LT_CMD_MAX_LEN],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+# find the maximum length of command line arguments
+AC_MSG_CHECKING([the maximum length of command line arguments])
+AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl
+ i=0
+ teststring="ABCD"
+
+ case $build_os in
+ msdosdjgpp*)
+ # On DJGPP, this test can blow up pretty badly due to problems in libc
+ # (any single argument exceeding 2000 bytes causes a buffer overrun
+ # during glob expansion). Even if it were fixed, the result of this
+ # check would be larger than it should be.
+ lt_cv_sys_max_cmd_len=12288; # 12K is about right
+ ;;
+
+ gnu*)
+ # Under GNU Hurd, this test is not required because there is
+ # no limit to the length of command line arguments.
+ # Libtool will interpret -1 as no limit whatsoever
+ lt_cv_sys_max_cmd_len=-1;
+ ;;
+
+ cygwin* | mingw* | cegcc*)
+ # On Win9x/ME, this test blows up -- it succeeds, but takes
+ # about 5 minutes as the teststring grows exponentially.
+ # Worse, since 9x/ME are not pre-emptively multitasking,
+ # you end up with a "frozen" computer, even though with patience
+ # the test eventually succeeds (with a max line length of 256k).
+ # Instead, let's just punt: use the minimum linelength reported by
+ # all of the supported platforms: 8192 (on NT/2K/XP).
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ amigaos*)
+ # On AmigaOS with pdksh, this test takes hours, literally.
+ # So we just punt and use a minimum line length of 8192.
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ netbsd* | freebsd* | openbsd* | darwin* | dragonfly*)
+ # This has been around since 386BSD, at least. Likely further.
+ if test -x /sbin/sysctl; then
+ lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax`
+ elif test -x /usr/sbin/sysctl; then
+ lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax`
+ else
+ lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs
+ fi
+ # And add a safety zone
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+ ;;
+
+ interix*)
+ # We know the value 262144 and hardcode it with a safety zone (like BSD)
+ lt_cv_sys_max_cmd_len=196608
+ ;;
+
+ osf*)
+ # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure
+ # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not
+ # nice to cause kernel panics so lets avoid the loop below.
+ # First set a reasonable default.
+ lt_cv_sys_max_cmd_len=16384
+ #
+ if test -x /sbin/sysconfig; then
+ case `/sbin/sysconfig -q proc exec_disable_arg_limit` in
+ *1*) lt_cv_sys_max_cmd_len=-1 ;;
+ esac
+ fi
+ ;;
+ sco3.2v5*)
+ lt_cv_sys_max_cmd_len=102400
+ ;;
+ sysv5* | sco5v6* | sysv4.2uw2*)
+ kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null`
+ if test -n "$kargmax"; then
+ lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'`
+ else
+ lt_cv_sys_max_cmd_len=32768
+ fi
+ ;;
+ *)
+ lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null`
+ if test -n "$lt_cv_sys_max_cmd_len"; then
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+ else
+ # Make teststring a little bigger before we do anything with it.
+ # a 1K string should be a reasonable start.
+ for i in 1 2 3 4 5 6 7 8 ; do
+ teststring=$teststring$teststring
+ done
+ SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}}
+ # If test is not a shell built-in, we'll probably end up computing a
+ # maximum length that is only half of the actual maximum length, but
+ # we can't tell.
+ while { test "X"`$SHELL [$]0 --fallback-echo "X$teststring$teststring" 2>/dev/null` \
+ = "XX$teststring$teststring"; } >/dev/null 2>&1 &&
+ test $i != 17 # 1/2 MB should be enough
+ do
+ i=`expr $i + 1`
+ teststring=$teststring$teststring
+ done
+ # Only check the string length outside the loop.
+ lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1`
+ teststring=
+ # Add a significant safety factor because C++ compilers can tack on
+ # massive amounts of additional arguments before passing them to the
+ # linker. It appears as though 1/2 is a usable value.
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2`
+ fi
+ ;;
+ esac
+])
+if test -n $lt_cv_sys_max_cmd_len ; then
+ AC_MSG_RESULT($lt_cv_sys_max_cmd_len)
+else
+ AC_MSG_RESULT(none)
+fi
+max_cmd_len=$lt_cv_sys_max_cmd_len
+_LT_DECL([], [max_cmd_len], [0],
+ [What is the maximum length of a command?])
+])# LT_CMD_MAX_LEN
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], [])
+
+
+# _LT_HEADER_DLFCN
+# ----------------
+m4_defun([_LT_HEADER_DLFCN],
+[AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl
+])# _LT_HEADER_DLFCN
+
+
+# _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE,
+# ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING)
+# ----------------------------------------------------------------
+m4_defun([_LT_TRY_DLOPEN_SELF],
+[m4_require([_LT_HEADER_DLFCN])dnl
+if test "$cross_compiling" = yes; then :
+ [$4]
+else
+ lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+ lt_status=$lt_dlunknown
+ cat > conftest.$ac_ext <<_LT_EOF
+[#line __oline__ "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+# define LT_DLGLOBAL RTLD_GLOBAL
+#else
+# ifdef DL_GLOBAL
+# define LT_DLGLOBAL DL_GLOBAL
+# else
+# define LT_DLGLOBAL 0
+# endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+ find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+# ifdef RTLD_LAZY
+# define LT_DLLAZY_OR_NOW RTLD_LAZY
+# else
+# ifdef DL_LAZY
+# define LT_DLLAZY_OR_NOW DL_LAZY
+# else
+# ifdef RTLD_NOW
+# define LT_DLLAZY_OR_NOW RTLD_NOW
+# else
+# ifdef DL_NOW
+# define LT_DLLAZY_OR_NOW DL_NOW
+# else
+# define LT_DLLAZY_OR_NOW 0
+# endif
+# endif
+# endif
+# endif
+#endif
+
+void fnord() { int i=42;}
+int main ()
+{
+ void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+ int status = $lt_dlunknown;
+
+ if (self)
+ {
+ if (dlsym (self,"fnord")) status = $lt_dlno_uscore;
+ else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
+ /* dlclose (self); */
+ }
+ else
+ puts (dlerror ());
+
+ return status;
+}]
+_LT_EOF
+ if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext} 2>/dev/null; then
+ (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null
+ lt_status=$?
+ case x$lt_status in
+ x$lt_dlno_uscore) $1 ;;
+ x$lt_dlneed_uscore) $2 ;;
+ x$lt_dlunknown|x*) $3 ;;
+ esac
+ else :
+ # compilation failed
+ $3
+ fi
+fi
+rm -fr conftest*
+])# _LT_TRY_DLOPEN_SELF
+
+
+# LT_SYS_DLOPEN_SELF
+# ------------------
+AC_DEFUN([LT_SYS_DLOPEN_SELF],
+[m4_require([_LT_HEADER_DLFCN])dnl
+if test "x$enable_dlopen" != xyes; then
+ enable_dlopen=unknown
+ enable_dlopen_self=unknown
+ enable_dlopen_self_static=unknown
+else
+ lt_cv_dlopen=no
+ lt_cv_dlopen_libs=
+
+ case $host_os in
+ beos*)
+ lt_cv_dlopen="load_add_on"
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=yes
+ ;;
+
+ mingw* | pw32* | cegcc*)
+ lt_cv_dlopen="LoadLibrary"
+ lt_cv_dlopen_libs=
+ ;;
+
+ cygwin*)
+ lt_cv_dlopen="dlopen"
+ lt_cv_dlopen_libs=
+ ;;
+
+ darwin*)
+ # if libdl is installed we need to link against it
+ AC_CHECK_LIB([dl], [dlopen],
+ [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],[
+ lt_cv_dlopen="dyld"
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=yes
+ ])
+ ;;
+
+ *)
+ AC_CHECK_FUNC([shl_load],
+ [lt_cv_dlopen="shl_load"],
+ [AC_CHECK_LIB([dld], [shl_load],
+ [lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"],
+ [AC_CHECK_FUNC([dlopen],
+ [lt_cv_dlopen="dlopen"],
+ [AC_CHECK_LIB([dl], [dlopen],
+ [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],
+ [AC_CHECK_LIB([svld], [dlopen],
+ [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"],
+ [AC_CHECK_LIB([dld], [dld_link],
+ [lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"])
+ ])
+ ])
+ ])
+ ])
+ ])
+ ;;
+ esac
+
+ if test "x$lt_cv_dlopen" != xno; then
+ enable_dlopen=yes
+ else
+ enable_dlopen=no
+ fi
+
+ case $lt_cv_dlopen in
+ dlopen)
+ save_CPPFLAGS="$CPPFLAGS"
+ test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+
+ save_LDFLAGS="$LDFLAGS"
+ wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
+
+ save_LIBS="$LIBS"
+ LIBS="$lt_cv_dlopen_libs $LIBS"
+
+ AC_CACHE_CHECK([whether a program can dlopen itself],
+ lt_cv_dlopen_self, [dnl
+ _LT_TRY_DLOPEN_SELF(
+ lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes,
+ lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross)
+ ])
+
+ if test "x$lt_cv_dlopen_self" = xyes; then
+ wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\"
+ AC_CACHE_CHECK([whether a statically linked program can dlopen itself],
+ lt_cv_dlopen_self_static, [dnl
+ _LT_TRY_DLOPEN_SELF(
+ lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes,
+ lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross)
+ ])
+ fi
+
+ CPPFLAGS="$save_CPPFLAGS"
+ LDFLAGS="$save_LDFLAGS"
+ LIBS="$save_LIBS"
+ ;;
+ esac
+
+ case $lt_cv_dlopen_self in
+ yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
+ *) enable_dlopen_self=unknown ;;
+ esac
+
+ case $lt_cv_dlopen_self_static in
+ yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
+ *) enable_dlopen_self_static=unknown ;;
+ esac
+fi
+_LT_DECL([dlopen_support], [enable_dlopen], [0],
+ [Whether dlopen is supported])
+_LT_DECL([dlopen_self], [enable_dlopen_self], [0],
+ [Whether dlopen of programs is supported])
+_LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0],
+ [Whether dlopen of statically linked programs is supported])
+])# LT_SYS_DLOPEN_SELF
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], [])
+
+
+# _LT_COMPILER_C_O([TAGNAME])
+# ---------------------------
+# Check to see if options -c and -o are simultaneously supported by compiler.
+# This macro does not hard code the compiler like AC_PROG_CC_C_O.
+m4_defun([_LT_COMPILER_C_O],
+[m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_TAG_COMPILER])dnl
+AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext],
+ [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)],
+ [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no
+ $RM -r conftest 2>/dev/null
+ mkdir conftest
+ cd conftest
+ mkdir out
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ lt_compiler_flag="-o out/conftest2.$ac_objext"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD)
+ (eval "$lt_compile" 2>out/conftest.err)
+ ac_status=$?
+ cat out/conftest.err >&AS_MESSAGE_LOG_FD
+ echo "$as_me:__oline__: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
+ if (exit $ac_status) && test -s out/conftest2.$ac_objext
+ then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp
+ $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+ if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+ _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes
+ fi
+ fi
+ chmod u+w . 2>&AS_MESSAGE_LOG_FD
+ $RM conftest*
+ # SGI C++ compiler will create directory out/ii_files/ for
+ # template instantiation
+ test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+ $RM out/* && rmdir out
+ cd ..
+ $RM -r conftest
+ $RM conftest*
+])
+_LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1],
+ [Does compiler simultaneously support -c and -o options?])
+])# _LT_COMPILER_C_O
+
+
+# _LT_COMPILER_FILE_LOCKS([TAGNAME])
+# ----------------------------------
+# Check to see if we can do hard links to lock some files if needed
+m4_defun([_LT_COMPILER_FILE_LOCKS],
+[m4_require([_LT_ENABLE_LOCK])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+_LT_COMPILER_C_O([$1])
+
+hard_links="nottested"
+if test "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" = no && test "$need_locks" != no; then
+ # do not overwrite the value of need_locks provided by the user
+ AC_MSG_CHECKING([if we can lock with hard links])
+ hard_links=yes
+ $RM conftest*
+ ln conftest.a conftest.b 2>/dev/null && hard_links=no
+ touch conftest.a
+ ln conftest.a conftest.b 2>&5 || hard_links=no
+ ln conftest.a conftest.b 2>/dev/null && hard_links=no
+ AC_MSG_RESULT([$hard_links])
+ if test "$hard_links" = no; then
+ AC_MSG_WARN([`$CC' does not support `-c -o', so `make -j' may be unsafe])
+ need_locks=warn
+ fi
+else
+ need_locks=no
+fi
+_LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?])
+])# _LT_COMPILER_FILE_LOCKS
+
+
+# _LT_CHECK_OBJDIR
+# ----------------
+m4_defun([_LT_CHECK_OBJDIR],
+[AC_CACHE_CHECK([for objdir], [lt_cv_objdir],
+[rm -f .libs 2>/dev/null
+mkdir .libs 2>/dev/null
+if test -d .libs; then
+ lt_cv_objdir=.libs
+else
+ # MS-DOS does not allow filenames that begin with a dot.
+ lt_cv_objdir=_libs
+fi
+rmdir .libs 2>/dev/null])
+objdir=$lt_cv_objdir
+_LT_DECL([], [objdir], [0],
+ [The name of the directory that contains temporary libtool files])dnl
+m4_pattern_allow([LT_OBJDIR])dnl
+AC_DEFINE_UNQUOTED(LT_OBJDIR, "$lt_cv_objdir/",
+ [Define to the sub-directory in which libtool stores uninstalled libraries.])
+])# _LT_CHECK_OBJDIR
+
+
+# _LT_LINKER_HARDCODE_LIBPATH([TAGNAME])
+# --------------------------------------
+# Check hardcoding attributes.
+m4_defun([_LT_LINKER_HARDCODE_LIBPATH],
+[AC_MSG_CHECKING([how to hardcode library paths into programs])
+_LT_TAGVAR(hardcode_action, $1)=
+if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" ||
+ test -n "$_LT_TAGVAR(runpath_var, $1)" ||
+ test "X$_LT_TAGVAR(hardcode_automatic, $1)" = "Xyes" ; then
+
+ # We can hardcode non-existent directories.
+ if test "$_LT_TAGVAR(hardcode_direct, $1)" != no &&
+ # If the only mechanism to avoid hardcoding is shlibpath_var, we
+ # have to relink, otherwise we might link with an installed library
+ # when we should be linking with a yet-to-be-installed one
+ ## test "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" != no &&
+ test "$_LT_TAGVAR(hardcode_minus_L, $1)" != no; then
+ # Linking always hardcodes the temporary library directory.
+ _LT_TAGVAR(hardcode_action, $1)=relink
+ else
+ # We can link without hardcoding, and we can hardcode nonexisting dirs.
+ _LT_TAGVAR(hardcode_action, $1)=immediate
+ fi
+else
+ # We cannot hardcode anything, or else we can only hardcode existing
+ # directories.
+ _LT_TAGVAR(hardcode_action, $1)=unsupported
+fi
+AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)])
+
+if test "$_LT_TAGVAR(hardcode_action, $1)" = relink ||
+ test "$_LT_TAGVAR(inherit_rpath, $1)" = yes; then
+ # Fast installation is not supported
+ enable_fast_install=no
+elif test "$shlibpath_overrides_runpath" = yes ||
+ test "$enable_shared" = no; then
+ # Fast installation is not necessary
+ enable_fast_install=needless
+fi
+_LT_TAGDECL([], [hardcode_action], [0],
+ [How to hardcode a shared library path into an executable])
+])# _LT_LINKER_HARDCODE_LIBPATH
+
+
+# _LT_CMD_STRIPLIB
+# ----------------
+m4_defun([_LT_CMD_STRIPLIB],
+[m4_require([_LT_DECL_EGREP])
+striplib=
+old_striplib=
+AC_MSG_CHECKING([whether stripping libraries is possible])
+if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then
+ test -z "$old_striplib" && old_striplib="$STRIP --strip-debug"
+ test -z "$striplib" && striplib="$STRIP --strip-unneeded"
+ AC_MSG_RESULT([yes])
+else
+# FIXME - insert some real tests, host_os isn't really good enough
+ case $host_os in
+ darwin*)
+ if test -n "$STRIP" ; then
+ striplib="$STRIP -x"
+ old_striplib="$STRIP -S"
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ fi
+ ;;
+ *)
+ AC_MSG_RESULT([no])
+ ;;
+ esac
+fi
+_LT_DECL([], [old_striplib], [1], [Commands to strip libraries])
+_LT_DECL([], [striplib], [1])
+])# _LT_CMD_STRIPLIB
+
+
+# _LT_SYS_DYNAMIC_LINKER([TAG])
+# -----------------------------
+# PORTME Fill in your ld.so characteristics
+m4_defun([_LT_SYS_DYNAMIC_LINKER],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_OBJDUMP])dnl
+m4_require([_LT_DECL_SED])dnl
+AC_MSG_CHECKING([dynamic linker characteristics])
+m4_if([$1],
+ [], [
+if test "$GCC" = yes; then
+ case $host_os in
+ darwin*) lt_awk_arg="/^libraries:/,/LR/" ;;
+ *) lt_awk_arg="/^libraries:/" ;;
+ esac
+ lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e "s,=/,/,g"`
+ if $ECHO "$lt_search_path_spec" | $GREP ';' >/dev/null ; then
+ # if the path contains ";" then we assume it to be the separator
+ # otherwise default to the standard path separator (i.e. ":") - it is
+ # assumed that no part of a normal pathname contains ";" but that should
+ # okay in the real world where ";" in dirpaths is itself problematic.
+ lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e 's/;/ /g'`
+ else
+ lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+ fi
+ # Ok, now we have the path, separated by spaces, we can step through it
+ # and add multilib dir if necessary.
+ lt_tmp_lt_search_path_spec=
+ lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null`
+ for lt_sys_path in $lt_search_path_spec; do
+ if test -d "$lt_sys_path/$lt_multi_os_dir"; then
+ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir"
+ else
+ test -d "$lt_sys_path" && \
+ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path"
+ fi
+ done
+ lt_search_path_spec=`$ECHO $lt_tmp_lt_search_path_spec | awk '
+BEGIN {RS=" "; FS="/|\n";} {
+ lt_foo="";
+ lt_count=0;
+ for (lt_i = NF; lt_i > 0; lt_i--) {
+ if ($lt_i != "" && $lt_i != ".") {
+ if ($lt_i == "..") {
+ lt_count++;
+ } else {
+ if (lt_count == 0) {
+ lt_foo="/" $lt_i lt_foo;
+ } else {
+ lt_count--;
+ }
+ }
+ }
+ }
+ if (lt_foo != "") { lt_freq[[lt_foo]]++; }
+ if (lt_freq[[lt_foo]] == 1) { print lt_foo; }
+}'`
+ sys_lib_search_path_spec=`$ECHO $lt_search_path_spec`
+else
+ sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+fi])
+library_names_spec=
+libname_spec='lib$name'
+soname_spec=
+shrext_cmds=".so"
+postinstall_cmds=
+postuninstall_cmds=
+finish_cmds=
+finish_eval=
+shlibpath_var=
+shlibpath_overrides_runpath=unknown
+version_type=none
+dynamic_linker="$host_os ld.so"
+sys_lib_dlsearch_path_spec="/lib /usr/lib"
+need_lib_prefix=unknown
+hardcode_into_libs=no
+
+# when you set need_version to no, make sure it does not cause -set_version
+# flags to be left without arguments
+need_version=unknown
+
+case $host_os in
+aix3*)
+ version_type=linux
+ library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a'
+ shlibpath_var=LIBPATH
+
+ # AIX 3 has no versioning support, so we append a major version to the name.
+ soname_spec='${libname}${release}${shared_ext}$major'
+ ;;
+
+aix[[4-9]]*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ hardcode_into_libs=yes
+ if test "$host_cpu" = ia64; then
+ # AIX 5 supports IA64
+ library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}'
+ shlibpath_var=LD_LIBRARY_PATH
+ else
+ # With GCC up to 2.95.x, collect2 would create an import file
+ # for dependence libraries. The import file would start with
+ # the line `#! .'. This would cause the generated library to
+ # depend on `.', always an invalid library. This was fixed in
+ # development snapshots of GCC prior to 3.0.
+ case $host_os in
+ aix4 | aix4.[[01]] | aix4.[[01]].*)
+ if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
+ echo ' yes '
+ echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then
+ :
+ else
+ can_build_shared=no
+ fi
+ ;;
+ esac
+ # AIX (on Power*) has no versioning support, so currently we can not hardcode correct
+ # soname into executable. Probably we can add versioning support to
+ # collect2, so additional links can be useful in future.
+ if test "$aix_use_runtimelinking" = yes; then
+ # If using run time linking (on AIX 4.2 or later) use lib<name>.so
+ # instead of lib<name>.a to let people know that these are not
+ # typical AIX shared libraries.
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ else
+ # We preserve .a as extension for shared libraries through AIX4.2
+ # and later when we are not doing run time linking.
+ library_names_spec='${libname}${release}.a $libname.a'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ fi
+ shlibpath_var=LIBPATH
+ fi
+ ;;
+
+amigaos*)
+ case $host_cpu in
+ powerpc)
+ # Since July 2007 AmigaOS4 officially supports .so libraries.
+ # When compiling the executable, add -use-dynld -Lsobjs: to the compileline.
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ ;;
+ m68k)
+ library_names_spec='$libname.ixlibrary $libname.a'
+ # Create ${libname}_ixlibrary.a entries in /sys/libs.
+ finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$ECHO "X$lib" | $Xsed -e '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
+ ;;
+ esac
+ ;;
+
+beos*)
+ library_names_spec='${libname}${shared_ext}'
+ dynamic_linker="$host_os ld.so"
+ shlibpath_var=LIBRARY_PATH
+ ;;
+
+bsdi[[45]]*)
+ version_type=linux
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
+ sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
+ # the default ld.so.conf also contains /usr/contrib/lib and
+ # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
+ # libtool to hard-code these into programs
+ ;;
+
+cygwin* | mingw* | pw32* | cegcc*)
+ version_type=windows
+ shrext_cmds=".dll"
+ need_version=no
+ need_lib_prefix=no
+
+ case $GCC,$host_os in
+ yes,cygwin* | yes,mingw* | yes,pw32* | yes,cegcc*)
+ library_names_spec='$libname.dll.a'
+ # DLL is installed to $(libdir)/../bin by postinstall_cmds
+ postinstall_cmds='base_file=`basename \${file}`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~
+ dldir=$destdir/`dirname \$dlpath`~
+ test -d \$dldir || mkdir -p \$dldir~
+ $install_prog $dir/$dlname \$dldir/$dlname~
+ chmod a+x \$dldir/$dlname~
+ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+ eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+ fi'
+ postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+ dlpath=$dir/\$dldll~
+ $RM \$dlpath'
+ shlibpath_overrides_runpath=yes
+
+ case $host_os in
+ cygwin*)
+ # Cygwin DLLs use 'cyg' prefix rather than 'lib'
+ soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}'
+ sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib"
+ ;;
+ mingw* | cegcc*)
+ # MinGW DLLs use traditional 'lib' prefix
+ soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}'
+ sys_lib_search_path_spec=`$CC -print-search-dirs | $GREP "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"`
+ if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then
+ # It is most probably a Windows format PATH printed by
+ # mingw gcc, but we are running on Cygwin. Gcc prints its search
+ # path with ; separators, and with drive letters. We can handle the
+ # drive letters (cygwin fileutils understands them), so leave them,
+ # especially as we might pass files found there to a mingw objdump,
+ # which wouldn't understand a cygwinified path. Ahh.
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
+ else
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+ fi
+ ;;
+ pw32*)
+ # pw32 DLLs use 'pw' prefix rather than 'lib'
+ library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}'
+ ;;
+ esac
+ ;;
+
+ *)
+ library_names_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext} $libname.lib'
+ ;;
+ esac
+ dynamic_linker='Win32 ld.exe'
+ # FIXME: first we should search . and the directory the executable is in
+ shlibpath_var=PATH
+ ;;
+
+darwin* | rhapsody*)
+ dynamic_linker="$host_os dyld"
+ version_type=darwin
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext'
+ soname_spec='${libname}${release}${major}$shared_ext'
+ shlibpath_overrides_runpath=yes
+ shlibpath_var=DYLD_LIBRARY_PATH
+ shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`'
+m4_if([$1], [],[
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"])
+ sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'
+ ;;
+
+dgux*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+freebsd1*)
+ dynamic_linker=no
+ ;;
+
+freebsd* | dragonfly*)
+ # DragonFly does not have aout. When/if they implement a new
+ # versioning mechanism, adjust this.
+ if test -x /usr/bin/objformat; then
+ objformat=`/usr/bin/objformat`
+ else
+ case $host_os in
+ freebsd[[123]]*) objformat=aout ;;
+ *) objformat=elf ;;
+ esac
+ fi
+ version_type=freebsd-$objformat
+ case $version_type in
+ freebsd-elf*)
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+ need_version=no
+ need_lib_prefix=no
+ ;;
+ freebsd-*)
+ library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix'
+ need_version=yes
+ ;;
+ esac
+ shlibpath_var=LD_LIBRARY_PATH
+ case $host_os in
+ freebsd2*)
+ shlibpath_overrides_runpath=yes
+ ;;
+ freebsd3.[[01]]* | freebsdelf3.[[01]]*)
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+ freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \
+ freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1)
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+ *) # from 4.6 on, and DragonFly
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+ esac
+ ;;
+
+gnu*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ hardcode_into_libs=yes
+ ;;
+
+hpux9* | hpux10* | hpux11*)
+ # Give a soname corresponding to the major version so that dld.sl refuses to
+ # link against other versions.
+ version_type=sunos
+ need_lib_prefix=no
+ need_version=no
+ case $host_cpu in
+ ia64*)
+ shrext_cmds='.so'
+ hardcode_into_libs=yes
+ dynamic_linker="$host_os dld.so"
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ if test "X$HPUX_IA64_MODE" = X32; then
+ sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
+ else
+ sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
+ fi
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ ;;
+ hppa*64*)
+ shrext_cmds='.sl'
+ hardcode_into_libs=yes
+ dynamic_linker="$host_os dld.sl"
+ shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
+ shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ ;;
+ *)
+ shrext_cmds='.sl'
+ dynamic_linker="$host_os dld.sl"
+ shlibpath_var=SHLIB_PATH
+ shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ ;;
+ esac
+ # HP-UX runs *really* slowly unless shared libraries are mode 555.
+ postinstall_cmds='chmod 555 $lib'
+ ;;
+
+interix[[3-9]]*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+
+irix5* | irix6* | nonstopux*)
+ case $host_os in
+ nonstopux*) version_type=nonstopux ;;
+ *)
+ if test "$lt_cv_prog_gnu_ld" = yes; then
+ version_type=linux
+ else
+ version_type=irix
+ fi ;;
+ esac
+ need_lib_prefix=no
+ need_version=no
+ soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}'
+ case $host_os in
+ irix5* | nonstopux*)
+ libsuff= shlibsuff=
+ ;;
+ *)
+ case $LD in # libtool.m4 will add one of these switches to LD
+ *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ")
+ libsuff= shlibsuff= libmagic=32-bit;;
+ *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ")
+ libsuff=32 shlibsuff=N32 libmagic=N32;;
+ *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ")
+ libsuff=64 shlibsuff=64 libmagic=64-bit;;
+ *) libsuff= shlibsuff= libmagic=never-match;;
+ esac
+ ;;
+ esac
+ shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
+ shlibpath_overrides_runpath=no
+ sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}"
+ sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}"
+ hardcode_into_libs=yes
+ ;;
+
+# No shared lib support for Linux oldld, aout, or coff.
+linux*oldld* | linux*aout* | linux*coff*)
+ dynamic_linker=no
+ ;;
+
+# This must be Linux ELF.
+linux* | k*bsd*-gnu)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ # Some binutils ld are patched to set DT_RUNPATH
+ save_LDFLAGS=$LDFLAGS
+ save_libdir=$libdir
+ eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \
+ LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\""
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])],
+ [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null],
+ [shlibpath_overrides_runpath=yes])])
+ LDFLAGS=$save_LDFLAGS
+ libdir=$save_libdir
+
+ # This implies no fast_install, which is unacceptable.
+ # Some rework will be needed to allow for fast_install
+ # before this can be enabled.
+ hardcode_into_libs=yes
+
+ # Add ABI-specific directories to the system library path.
+ sys_lib_dlsearch_path_spec="/lib64 /usr/lib64 /lib /usr/lib"
+
+ # Append ld.so.conf contents to the search path
+ if test -f /etc/ld.so.conf; then
+ lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '`
+ sys_lib_dlsearch_path_spec="$sys_lib_dlsearch_path_spec $lt_ld_extra"
+ fi
+
+ # We used to test for /lib/ld.so.1 and disable shared libraries on
+ # powerpc, because MkLinux only supported shared libraries with the
+ # GNU dynamic linker. Since this was broken with cross compilers,
+ # most powerpc-linux boxes support dynamic linking these days and
+ # people can always --disable-shared, the test was removed, and we
+ # assume the GNU/Linux dynamic linker is in use.
+ dynamic_linker='GNU/Linux ld.so'
+ ;;
+
+netbsd*)
+ version_type=sunos
+ need_lib_prefix=no
+ need_version=no
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ dynamic_linker='NetBSD (a.out) ld.so'
+ else
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ dynamic_linker='NetBSD ld.elf_so'
+ fi
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+
+newsos6)
+ version_type=linux
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ ;;
+
+*nto* | *qnx*)
+ version_type=qnx
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ dynamic_linker='ldqnx.so'
+ ;;
+
+openbsd*)
+ version_type=sunos
+ sys_lib_dlsearch_path_spec="/usr/lib"
+ need_lib_prefix=no
+ # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs.
+ case $host_os in
+ openbsd3.3 | openbsd3.3.*) need_version=yes ;;
+ *) need_version=no ;;
+ esac
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+ case $host_os in
+ openbsd2.[[89]] | openbsd2.[[89]].*)
+ shlibpath_overrides_runpath=no
+ ;;
+ *)
+ shlibpath_overrides_runpath=yes
+ ;;
+ esac
+ else
+ shlibpath_overrides_runpath=yes
+ fi
+ ;;
+
+os2*)
+ libname_spec='$name'
+ shrext_cmds=".dll"
+ need_lib_prefix=no
+ library_names_spec='$libname${shared_ext} $libname.a'
+ dynamic_linker='OS/2 ld.exe'
+ shlibpath_var=LIBPATH
+ ;;
+
+osf3* | osf4* | osf5*)
+ version_type=osf
+ need_lib_prefix=no
+ need_version=no
+ soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ shlibpath_var=LD_LIBRARY_PATH
+ sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
+ sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec"
+ ;;
+
+rdos*)
+ dynamic_linker=no
+ ;;
+
+solaris*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ # ldd complains unless libraries are executable
+ postinstall_cmds='chmod +x $lib'
+ ;;
+
+sunos4*)
+ version_type=sunos
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+ finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ if test "$with_gnu_ld" = yes; then
+ need_lib_prefix=no
+ fi
+ need_version=yes
+ ;;
+
+sysv4 | sysv4.3*)
+ version_type=linux
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ case $host_vendor in
+ sni)
+ shlibpath_overrides_runpath=no
+ need_lib_prefix=no
+ runpath_var=LD_RUN_PATH
+ ;;
+ siemens)
+ need_lib_prefix=no
+ ;;
+ motorola)
+ need_lib_prefix=no
+ need_version=no
+ shlibpath_overrides_runpath=no
+ sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
+ ;;
+ esac
+ ;;
+
+sysv4*MP*)
+ if test -d /usr/nec ;then
+ version_type=linux
+ library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}'
+ soname_spec='$libname${shared_ext}.$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ fi
+ ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+ version_type=freebsd-elf
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ if test "$with_gnu_ld" = yes; then
+ sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib'
+ else
+ sys_lib_search_path_spec='/usr/ccs/lib /usr/lib'
+ case $host_os in
+ sco3.2v5*)
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /lib"
+ ;;
+ esac
+ fi
+ sys_lib_dlsearch_path_spec='/usr/lib'
+ ;;
+
+tpf*)
+ # TPF is a cross-target only. Preferred cross-host = GNU/Linux.
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+
+uts4*)
+ version_type=linux
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+*)
+ dynamic_linker=no
+ ;;
+esac
+AC_MSG_RESULT([$dynamic_linker])
+test "$dynamic_linker" = no && can_build_shared=no
+
+variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
+if test "$GCC" = yes; then
+ variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
+fi
+
+if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then
+ sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec"
+fi
+if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then
+ sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec"
+fi
+
+_LT_DECL([], [variables_saved_for_relink], [1],
+ [Variables whose values should be saved in libtool wrapper scripts and
+ restored at link time])
+_LT_DECL([], [need_lib_prefix], [0],
+ [Do we need the "lib" prefix for modules?])
+_LT_DECL([], [need_version], [0], [Do we need a version for libraries?])
+_LT_DECL([], [version_type], [0], [Library versioning type])
+_LT_DECL([], [runpath_var], [0], [Shared library runtime path variable])
+_LT_DECL([], [shlibpath_var], [0],[Shared library path variable])
+_LT_DECL([], [shlibpath_overrides_runpath], [0],
+ [Is shlibpath searched before the hard-coded library search path?])
+_LT_DECL([], [libname_spec], [1], [Format of library name prefix])
+_LT_DECL([], [library_names_spec], [1],
+ [[List of archive names. First name is the real one, the rest are links.
+ The last name is the one that the linker finds with -lNAME]])
+_LT_DECL([], [soname_spec], [1],
+ [[The coded name of the library, if different from the real name]])
+_LT_DECL([], [postinstall_cmds], [2],
+ [Command to use after installation of a shared archive])
+_LT_DECL([], [postuninstall_cmds], [2],
+ [Command to use after uninstallation of a shared archive])
+_LT_DECL([], [finish_cmds], [2],
+ [Commands used to finish a libtool library installation in a directory])
+_LT_DECL([], [finish_eval], [1],
+ [[As "finish_cmds", except a single script fragment to be evaled but
+ not shown]])
+_LT_DECL([], [hardcode_into_libs], [0],
+ [Whether we should hardcode library paths into libraries])
+_LT_DECL([], [sys_lib_search_path_spec], [2],
+ [Compile-time system search path for libraries])
+_LT_DECL([], [sys_lib_dlsearch_path_spec], [2],
+ [Run-time system search path for libraries])
+])# _LT_SYS_DYNAMIC_LINKER
+
+
+# _LT_PATH_TOOL_PREFIX(TOOL)
+# --------------------------
+# find a file program which can recognize shared library
+AC_DEFUN([_LT_PATH_TOOL_PREFIX],
+[m4_require([_LT_DECL_EGREP])dnl
+AC_MSG_CHECKING([for $1])
+AC_CACHE_VAL(lt_cv_path_MAGIC_CMD,
+[case $MAGIC_CMD in
+[[\\/*] | ?:[\\/]*])
+ lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+ ;;
+*)
+ lt_save_MAGIC_CMD="$MAGIC_CMD"
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+dnl $ac_dummy forces splitting on constant user-supplied paths.
+dnl POSIX.2 word splitting is done only on the output of word expansions,
+dnl not every word. This closes a longstanding sh security hole.
+ ac_dummy="m4_if([$2], , $PATH, [$2])"
+ for ac_dir in $ac_dummy; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$1; then
+ lt_cv_path_MAGIC_CMD="$ac_dir/$1"
+ if test -n "$file_magic_test_file"; then
+ case $deplibs_check_method in
+ "file_magic "*)
+ file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+ MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+ if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+ $EGREP "$file_magic_regex" > /dev/null; then
+ :
+ else
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such. This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem. Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+ fi ;;
+ esac
+ fi
+ break
+ fi
+ done
+ IFS="$lt_save_ifs"
+ MAGIC_CMD="$lt_save_MAGIC_CMD"
+ ;;
+esac])
+MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+if test -n "$MAGIC_CMD"; then
+ AC_MSG_RESULT($MAGIC_CMD)
+else
+ AC_MSG_RESULT(no)
+fi
+_LT_DECL([], [MAGIC_CMD], [0],
+ [Used to examine libraries when file_magic_cmd begins with "file"])dnl
+])# _LT_PATH_TOOL_PREFIX
+
+# Old name:
+AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], [])
+
+
+# _LT_PATH_MAGIC
+# --------------
+# find a file program which can recognize a shared library
+m4_defun([_LT_PATH_MAGIC],
+[_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH)
+if test -z "$lt_cv_path_MAGIC_CMD"; then
+ if test -n "$ac_tool_prefix"; then
+ _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH)
+ else
+ MAGIC_CMD=:
+ fi
+fi
+])# _LT_PATH_MAGIC
+
+
+# LT_PATH_LD
+# ----------
+# find the pathname to the GNU or non-GNU linker
+AC_DEFUN([LT_PATH_LD],
+[AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_DECL_EGREP])dnl
+
+AC_ARG_WITH([gnu-ld],
+ [AS_HELP_STRING([--with-gnu-ld],
+ [assume the C compiler uses GNU ld @<:@default=no@:>@])],
+ [test "$withval" = no || with_gnu_ld=yes],
+ [with_gnu_ld=no])dnl
+
+ac_prog=ld
+if test "$GCC" = yes; then
+ # Check if gcc -print-prog-name=ld gives a path.
+ AC_MSG_CHECKING([for ld used by $CC])
+ case $host in
+ *-*-mingw*)
+ # gcc leaves a trailing carriage return which upsets mingw
+ ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+ *)
+ ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+ esac
+ case $ac_prog in
+ # Accept absolute paths.
+ [[\\/]]* | ?:[[\\/]]*)
+ re_direlt='/[[^/]][[^/]]*/\.\./'
+ # Canonicalize the pathname of ld
+ ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'`
+ while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do
+ ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"`
+ done
+ test -z "$LD" && LD="$ac_prog"
+ ;;
+ "")
+ # If it fails, then pretend we aren't using GCC.
+ ac_prog=ld
+ ;;
+ *)
+ # If it is relative, then search for the first ld in PATH.
+ with_gnu_ld=unknown
+ ;;
+ esac
+elif test "$with_gnu_ld" = yes; then
+ AC_MSG_CHECKING([for GNU ld])
+else
+ AC_MSG_CHECKING([for non-GNU ld])
+fi
+AC_CACHE_VAL(lt_cv_path_LD,
+[if test -z "$LD"; then
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+ lt_cv_path_LD="$ac_dir/$ac_prog"
+ # Check to see if the program is GNU ld. I'd rather use --version,
+ # but apparently some variants of GNU ld only accept -v.
+ # Break only if it was the GNU/non-GNU ld that we prefer.
+ case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
+ *GNU* | *'with BFD'*)
+ test "$with_gnu_ld" != no && break
+ ;;
+ *)
+ test "$with_gnu_ld" != yes && break
+ ;;
+ esac
+ fi
+ done
+ IFS="$lt_save_ifs"
+else
+ lt_cv_path_LD="$LD" # Let the user override the test with a path.
+fi])
+LD="$lt_cv_path_LD"
+if test -n "$LD"; then
+ AC_MSG_RESULT($LD)
+else
+ AC_MSG_RESULT(no)
+fi
+test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH])
+_LT_PATH_LD_GNU
+AC_SUBST([LD])
+
+_LT_TAGDECL([], [LD], [1], [The linker used to build libraries])
+])# LT_PATH_LD
+
+# Old names:
+AU_ALIAS([AM_PROG_LD], [LT_PATH_LD])
+AU_ALIAS([AC_PROG_LD], [LT_PATH_LD])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_PROG_LD], [])
+dnl AC_DEFUN([AC_PROG_LD], [])
+
+
+# _LT_PATH_LD_GNU
+#- --------------
+m4_defun([_LT_PATH_LD_GNU],
+[AC_CACHE_CHECK([if the linker ($LD) is GNU ld], lt_cv_prog_gnu_ld,
+[# I'd rather use --version here, but apparently some GNU lds only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+ lt_cv_prog_gnu_ld=yes
+ ;;
+*)
+ lt_cv_prog_gnu_ld=no
+ ;;
+esac])
+with_gnu_ld=$lt_cv_prog_gnu_ld
+])# _LT_PATH_LD_GNU
+
+
+# _LT_CMD_RELOAD
+# --------------
+# find reload flag for linker
+# -- PORTME Some linkers may need a different reload flag.
+m4_defun([_LT_CMD_RELOAD],
+[AC_CACHE_CHECK([for $LD option to reload object files],
+ lt_cv_ld_reload_flag,
+ [lt_cv_ld_reload_flag='-r'])
+reload_flag=$lt_cv_ld_reload_flag
+case $reload_flag in
+"" | " "*) ;;
+*) reload_flag=" $reload_flag" ;;
+esac
+reload_cmds='$LD$reload_flag -o $output$reload_objs'
+case $host_os in
+ darwin*)
+ if test "$GCC" = yes; then
+ reload_cmds='$LTCC $LTCFLAGS -nostdlib ${wl}-r -o $output$reload_objs'
+ else
+ reload_cmds='$LD$reload_flag -o $output$reload_objs'
+ fi
+ ;;
+esac
+_LT_DECL([], [reload_flag], [1], [How to create reloadable object files])dnl
+_LT_DECL([], [reload_cmds], [2])dnl
+])# _LT_CMD_RELOAD
+
+
+# _LT_CHECK_MAGIC_METHOD
+# ----------------------
+# how to check for library dependencies
+# -- PORTME fill in with the dynamic library characteristics
+m4_defun([_LT_CHECK_MAGIC_METHOD],
+[m4_require([_LT_DECL_EGREP])
+m4_require([_LT_DECL_OBJDUMP])
+AC_CACHE_CHECK([how to recognize dependent libraries],
+lt_cv_deplibs_check_method,
+[lt_cv_file_magic_cmd='$MAGIC_CMD'
+lt_cv_file_magic_test_file=
+lt_cv_deplibs_check_method='unknown'
+# Need to set the preceding variable on all platforms that support
+# interlibrary dependencies.
+# 'none' -- dependencies not supported.
+# `unknown' -- same as none, but documents that we really don't know.
+# 'pass_all' -- all dependencies passed with no checks.
+# 'test_compile' -- check by making test program.
+# 'file_magic [[regex]]' -- check by looking for files in library path
+# which responds to the $file_magic_cmd with a given extended regex.
+# If you have `file' or equivalent on your system and you're not sure
+# whether `pass_all' will *always* work, you probably want this one.
+
+case $host_os in
+aix[[4-9]]*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+beos*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+bsdi[[45]]*)
+ lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib)'
+ lt_cv_file_magic_cmd='/usr/bin/file -L'
+ lt_cv_file_magic_test_file=/shlib/libc.so
+ ;;
+
+cygwin*)
+ # func_win32_libid is a shell function defined in ltmain.sh
+ lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+ lt_cv_file_magic_cmd='func_win32_libid'
+ ;;
+
+mingw* | pw32*)
+ # Base MSYS/MinGW do not provide the 'file' command needed by
+ # func_win32_libid shell function, so use a weaker test based on 'objdump',
+ # unless we find 'file', for example because we are cross-compiling.
+ if ( file / ) >/dev/null 2>&1; then
+ lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+ lt_cv_file_magic_cmd='func_win32_libid'
+ else
+ lt_cv_deplibs_check_method='file_magic file format pei*-i386(.*architecture: i386)?'
+ lt_cv_file_magic_cmd='$OBJDUMP -f'
+ fi
+ ;;
+
+cegcc)
+ # use the weaker test based on 'objdump'. See mingw*.
+ lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?'
+ lt_cv_file_magic_cmd='$OBJDUMP -f'
+ ;;
+
+darwin* | rhapsody*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+freebsd* | dragonfly*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+ case $host_cpu in
+ i*86 )
+ # Not sure whether the presence of OpenBSD here was a mistake.
+ # Let's accept both of them until this is cleared up.
+ lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library'
+ lt_cv_file_magic_cmd=/usr/bin/file
+ lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
+ ;;
+ esac
+ else
+ lt_cv_deplibs_check_method=pass_all
+ fi
+ ;;
+
+gnu*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+hpux10.20* | hpux11*)
+ lt_cv_file_magic_cmd=/usr/bin/file
+ case $host_cpu in
+ ia64*)
+ lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64'
+ lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so
+ ;;
+ hppa*64*)
+ [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - PA-RISC [0-9].[0-9]']
+ lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl
+ ;;
+ *)
+ lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]].[[0-9]]) shared library'
+ lt_cv_file_magic_test_file=/usr/lib/libc.sl
+ ;;
+ esac
+ ;;
+
+interix[[3-9]]*)
+ # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here
+ lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$'
+ ;;
+
+irix5* | irix6* | nonstopux*)
+ case $LD in
+ *-32|*"-32 ") libmagic=32-bit;;
+ *-n32|*"-n32 ") libmagic=N32;;
+ *-64|*"-64 ") libmagic=64-bit;;
+ *) libmagic=never-match;;
+ esac
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+# This must be Linux ELF.
+linux* | k*bsd*-gnu)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+ lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$'
+ else
+ lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$'
+ fi
+ ;;
+
+newos6*)
+ lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)'
+ lt_cv_file_magic_cmd=/usr/bin/file
+ lt_cv_file_magic_test_file=/usr/lib/libnls.so
+ ;;
+
+*nto* | *qnx*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+openbsd*)
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+ lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$'
+ else
+ lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$'
+ fi
+ ;;
+
+osf3* | osf4* | osf5*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+rdos*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+solaris*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+sysv4 | sysv4.3*)
+ case $host_vendor in
+ motorola)
+ lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]'
+ lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*`
+ ;;
+ ncr)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ sequent)
+ lt_cv_file_magic_cmd='/bin/file'
+ lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )'
+ ;;
+ sni)
+ lt_cv_file_magic_cmd='/bin/file'
+ lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib"
+ lt_cv_file_magic_test_file=/lib/libc.so
+ ;;
+ siemens)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ pc)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ esac
+ ;;
+
+tpf*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+esac
+])
+file_magic_cmd=$lt_cv_file_magic_cmd
+deplibs_check_method=$lt_cv_deplibs_check_method
+test -z "$deplibs_check_method" && deplibs_check_method=unknown
+
+_LT_DECL([], [deplibs_check_method], [1],
+ [Method to check whether dependent libraries are shared objects])
+_LT_DECL([], [file_magic_cmd], [1],
+ [Command to use when deplibs_check_method == "file_magic"])
+])# _LT_CHECK_MAGIC_METHOD
+
+
+# LT_PATH_NM
+# ----------
+# find the pathname to a BSD- or MS-compatible name lister
+AC_DEFUN([LT_PATH_NM],
+[AC_REQUIRE([AC_PROG_CC])dnl
+AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM,
+[if test -n "$NM"; then
+ # Let the user override the test.
+ lt_cv_path_NM="$NM"
+else
+ lt_nm_to_check="${ac_tool_prefix}nm"
+ if test -n "$ac_tool_prefix" && test "$build" = "$host"; then
+ lt_nm_to_check="$lt_nm_to_check nm"
+ fi
+ for lt_tmp_nm in $lt_nm_to_check; do
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ tmp_nm="$ac_dir/$lt_tmp_nm"
+ if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then
+ # Check to see if the nm accepts a BSD-compat flag.
+ # Adding the `sed 1q' prevents false positives on HP-UX, which says:
+ # nm: unknown option "B" ignored
+ # Tru64's nm complains that /dev/null is an invalid object file
+ case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in
+ */dev/null* | *'Invalid file or object type'*)
+ lt_cv_path_NM="$tmp_nm -B"
+ break
+ ;;
+ *)
+ case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in
+ */dev/null*)
+ lt_cv_path_NM="$tmp_nm -p"
+ break
+ ;;
+ *)
+ lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but
+ continue # so that we can try to find one that supports BSD flags
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ done
+ IFS="$lt_save_ifs"
+ done
+ : ${lt_cv_path_NM=no}
+fi])
+if test "$lt_cv_path_NM" != "no"; then
+ NM="$lt_cv_path_NM"
+else
+ # Didn't find any BSD compatible name lister, look for dumpbin.
+ AC_CHECK_TOOLS(DUMPBIN, ["dumpbin -symbols" "link -dump -symbols"], :)
+ AC_SUBST([DUMPBIN])
+ if test "$DUMPBIN" != ":"; then
+ NM="$DUMPBIN"
+ fi
+fi
+test -z "$NM" && NM=nm
+AC_SUBST([NM])
+_LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl
+
+AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface],
+ [lt_cv_nm_interface="BSD nm"
+ echo "int some_variable = 0;" > conftest.$ac_ext
+ (eval echo "\"\$as_me:__oline__: $ac_compile\"" >&AS_MESSAGE_LOG_FD)
+ (eval "$ac_compile" 2>conftest.err)
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ (eval echo "\"\$as_me:__oline__: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD)
+ (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ (eval echo "\"\$as_me:__oline__: output\"" >&AS_MESSAGE_LOG_FD)
+ cat conftest.out >&AS_MESSAGE_LOG_FD
+ if $GREP 'External.*some_variable' conftest.out > /dev/null; then
+ lt_cv_nm_interface="MS dumpbin"
+ fi
+ rm -f conftest*])
+])# LT_PATH_NM
+
+# Old names:
+AU_ALIAS([AM_PROG_NM], [LT_PATH_NM])
+AU_ALIAS([AC_PROG_NM], [LT_PATH_NM])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_PROG_NM], [])
+dnl AC_DEFUN([AC_PROG_NM], [])
+
+
+# LT_LIB_M
+# --------
+# check for math library
+AC_DEFUN([LT_LIB_M],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+LIBM=
+case $host in
+*-*-beos* | *-*-cygwin* | *-*-pw32* | *-*-darwin*)
+ # These system don't have libm, or don't need it
+ ;;
+*-ncr-sysv4.3*)
+ AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM="-lmw")
+ AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm")
+ ;;
+*)
+ AC_CHECK_LIB(m, cos, LIBM="-lm")
+ ;;
+esac
+AC_SUBST([LIBM])
+])# LT_LIB_M
+
+# Old name:
+AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_CHECK_LIBM], [])
+
+
+# _LT_COMPILER_NO_RTTI([TAGNAME])
+# -------------------------------
+m4_defun([_LT_COMPILER_NO_RTTI],
+[m4_require([_LT_TAG_COMPILER])dnl
+
+_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=
+
+if test "$GCC" = yes; then
+ _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin'
+
+ _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions],
+ lt_cv_prog_compiler_rtti_exceptions,
+ [-fno-rtti -fno-exceptions], [],
+ [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"])
+fi
+_LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1],
+ [Compiler flag to turn off builtin functions])
+])# _LT_COMPILER_NO_RTTI
+
+
+# _LT_CMD_GLOBAL_SYMBOLS
+# ----------------------
+m4_defun([_LT_CMD_GLOBAL_SYMBOLS],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([LT_PATH_NM])dnl
+AC_REQUIRE([LT_PATH_LD])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_TAG_COMPILER])dnl
+
+# Check for command to grab the raw symbol name followed by C symbol from nm.
+AC_MSG_CHECKING([command to parse $NM output from $compiler object])
+AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe],
+[
+# These are sane defaults that work on at least a few old systems.
+# [They come from Ultrix. What could be older than Ultrix?!! ;)]
+
+# Character class describing NM global symbol codes.
+symcode='[[BCDEGRST]]'
+
+# Regexp to match symbols that can be accessed directly from C.
+sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)'
+
+# Define system-specific variables.
+case $host_os in
+aix*)
+ symcode='[[BCDT]]'
+ ;;
+cygwin* | mingw* | pw32* | cegcc*)
+ symcode='[[ABCDGISTW]]'
+ ;;
+hpux*)
+ if test "$host_cpu" = ia64; then
+ symcode='[[ABCDEGRST]]'
+ fi
+ ;;
+irix* | nonstopux*)
+ symcode='[[BCDEGRST]]'
+ ;;
+osf*)
+ symcode='[[BCDEGQRST]]'
+ ;;
+solaris*)
+ symcode='[[BDRT]]'
+ ;;
+sco3.2v5*)
+ symcode='[[DT]]'
+ ;;
+sysv4.2uw2*)
+ symcode='[[DT]]'
+ ;;
+sysv5* | sco5v6* | unixware* | OpenUNIX*)
+ symcode='[[ABDT]]'
+ ;;
+sysv4)
+ symcode='[[DFNSTU]]'
+ ;;
+esac
+
+# If we're using GNU nm, then use its standard symbol codes.
+case `$NM -V 2>&1` in
+*GNU* | *'with BFD'*)
+ symcode='[[ABCDGIRSTW]]' ;;
+esac
+
+# Transform an extracted symbol line into a proper C declaration.
+# Some systems (esp. on ia64) link data and code symbols differently,
+# so use this general approach.
+lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'"
+
+# Transform an extracted symbol line into symbol name and symbol address
+lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p'"
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([[^ ]]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \(lib[[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"lib\2\", (void *) \&\2},/p'"
+
+# Handle CRLF in mingw tool chain
+opt_cr=
+case $build_os in
+mingw*)
+ opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp
+ ;;
+esac
+
+# Try without a prefix underscore, then with it.
+for ac_symprfx in "" "_"; do
+
+ # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol.
+ symxfrm="\\1 $ac_symprfx\\2 \\2"
+
+ # Write the raw and C identifiers.
+ if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+ # Fake it for dumpbin and say T for any non-static function
+ # and D for any global variable.
+ # Also find C++ and __fastcall symbols from MSVC++,
+ # which start with @ or ?.
+ lt_cv_sys_global_symbol_pipe="$AWK ['"\
+" {last_section=section; section=\$ 3};"\
+" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\
+" \$ 0!~/External *\|/{next};"\
+" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\
+" {if(hide[section]) next};"\
+" {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\
+" {split(\$ 0, a, /\||\r/); split(a[2], s)};"\
+" s[1]~/^[@?]/{print s[1], s[1]; next};"\
+" s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\
+" ' prfx=^$ac_symprfx]"
+ else
+ lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'"
+ fi
+
+ # Check to see that the pipe works correctly.
+ pipe_works=no
+
+ rm -f conftest*
+ cat > conftest.$ac_ext <<_LT_EOF
+#ifdef __cplusplus
+extern "C" {
+#endif
+char nm_test_var;
+void nm_test_func(void);
+void nm_test_func(void){}
+#ifdef __cplusplus
+}
+#endif
+int main(){nm_test_var='a';nm_test_func();return(0);}
+_LT_EOF
+
+ if AC_TRY_EVAL(ac_compile); then
+ # Now try to grab the symbols.
+ nlist=conftest.nm
+ if AC_TRY_EVAL(NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist) && test -s "$nlist"; then
+ # Try sorting and uniquifying the output.
+ if sort "$nlist" | uniq > "$nlist"T; then
+ mv -f "$nlist"T "$nlist"
+ else
+ rm -f "$nlist"T
+ fi
+
+ # Make sure that we snagged all the symbols we need.
+ if $GREP ' nm_test_var$' "$nlist" >/dev/null; then
+ if $GREP ' nm_test_func$' "$nlist" >/dev/null; then
+ cat <<_LT_EOF > conftest.$ac_ext
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+_LT_EOF
+ # Now generate the symbol file.
+ eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext'
+
+ cat <<_LT_EOF >> conftest.$ac_ext
+
+/* The mapping between symbol names and symbols. */
+const struct {
+ const char *name;
+ void *address;
+}
+lt__PROGRAM__LTX_preloaded_symbols[[]] =
+{
+ { "@PROGRAM@", (void *) 0 },
+_LT_EOF
+ $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext
+ cat <<\_LT_EOF >> conftest.$ac_ext
+ {0, (void *) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+ return lt__PROGRAM__LTX_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+_LT_EOF
+ # Now try linking the two files.
+ mv conftest.$ac_objext conftstm.$ac_objext
+ lt_save_LIBS="$LIBS"
+ lt_save_CFLAGS="$CFLAGS"
+ LIBS="conftstm.$ac_objext"
+ CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)"
+ if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext}; then
+ pipe_works=yes
+ fi
+ LIBS="$lt_save_LIBS"
+ CFLAGS="$lt_save_CFLAGS"
+ else
+ echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD
+ fi
+ else
+ echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD
+ fi
+ else
+ echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD
+ fi
+ else
+ echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD
+ cat conftest.$ac_ext >&5
+ fi
+ rm -rf conftest* conftst*
+
+ # Do not use the global_symbol_pipe unless it works.
+ if test "$pipe_works" = yes; then
+ break
+ else
+ lt_cv_sys_global_symbol_pipe=
+ fi
+done
+])
+if test -z "$lt_cv_sys_global_symbol_pipe"; then
+ lt_cv_sys_global_symbol_to_cdecl=
+fi
+if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then
+ AC_MSG_RESULT(failed)
+else
+ AC_MSG_RESULT(ok)
+fi
+
+_LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1],
+ [Take the output of nm and produce a listing of raw symbols and C names])
+_LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1],
+ [Transform the output of nm in a proper C declaration])
+_LT_DECL([global_symbol_to_c_name_address],
+ [lt_cv_sys_global_symbol_to_c_name_address], [1],
+ [Transform the output of nm in a C name address pair])
+_LT_DECL([global_symbol_to_c_name_address_lib_prefix],
+ [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1],
+ [Transform the output of nm in a C name address pair when lib prefix is needed])
+]) # _LT_CMD_GLOBAL_SYMBOLS
+
+
+# _LT_COMPILER_PIC([TAGNAME])
+# ---------------------------
+m4_defun([_LT_COMPILER_PIC],
+[m4_require([_LT_TAG_COMPILER])dnl
+_LT_TAGVAR(lt_prog_compiler_wl, $1)=
+_LT_TAGVAR(lt_prog_compiler_pic, $1)=
+_LT_TAGVAR(lt_prog_compiler_static, $1)=
+
+AC_MSG_CHECKING([for $compiler option to produce PIC])
+m4_if([$1], [CXX], [
+ # C++ specific cases for pic, static, wl, etc.
+ if test "$GXX" = yes; then
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+
+ case $host_os in
+ aix*)
+ # All AIX code is PIC.
+ if test "$host_cpu" = ia64; then
+ # AIX 5 now supports IA64 processor
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ m68k)
+ # FIXME: we need at least 68020 code to build shared libraries, but
+ # adding the `-m68020' flag to GCC prevents building anything better,
+ # like `-m68040'.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4'
+ ;;
+ esac
+ ;;
+
+ beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+ # PIC is the default for these OSes.
+ ;;
+ mingw* | cygwin* | os2* | pw32* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ # Although the cygwin gcc ignores -fPIC, still need this for old-style
+ # (--disable-auto-import) libraries
+ m4_if([$1], [GCJ], [],
+ [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+ ;;
+ darwin* | rhapsody*)
+ # PIC is the default on this platform
+ # Common symbols not allowed in MH_DYLIB files
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common'
+ ;;
+ *djgpp*)
+ # DJGPP does not support shared libraries at all
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+ ;;
+ interix[[3-9]]*)
+ # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+ # Instead, we relocate shared libraries at runtime.
+ ;;
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic
+ fi
+ ;;
+ hpux*)
+ # PIC is the default for 64-bit PA HP-UX, but not for 32-bit
+ # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag
+ # sets the default TLS model and affects inlining.
+ case $host_cpu in
+ hppa*64*)
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ esac
+ ;;
+ *qnx* | *nto*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ esac
+ else
+ case $host_os in
+ aix[[4-9]]*)
+ # All AIX code is PIC.
+ if test "$host_cpu" = ia64; then
+ # AIX 5 now supports IA64 processor
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ else
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp'
+ fi
+ ;;
+ chorus*)
+ case $cc_basename in
+ cxch68*)
+ # Green Hills C++ Compiler
+ # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a"
+ ;;
+ esac
+ ;;
+ dgux*)
+ case $cc_basename in
+ ec++*)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ ;;
+ ghcx*)
+ # Green Hills C++ Compiler
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ freebsd* | dragonfly*)
+ # FreeBSD uses GNU C++
+ ;;
+ hpux9* | hpux10* | hpux11*)
+ case $cc_basename in
+ CC*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive'
+ if test "$host_cpu" != ia64; then
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
+ fi
+ ;;
+ aCC*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive'
+ case $host_cpu in
+ hppa*64*|ia64*)
+ # +Z the default
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
+ ;;
+ esac
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ interix*)
+ # This is c89, which is MS Visual C++ (no shared libs)
+ # Anyone wants to do a port?
+ ;;
+ irix5* | irix6* | nonstopux*)
+ case $cc_basename in
+ CC*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ # CC pic flag -KPIC is the default.
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ linux* | k*bsd*-gnu)
+ case $cc_basename in
+ KCC*)
+ # KAI C++ Compiler
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ ecpc* )
+ # old Intel C++ for x86_64 which still supported -KPIC.
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+ ;;
+ icpc* )
+ # Intel C++, used to be incompatible with GCC.
+ # ICC 10 doesn't accept -KPIC any more.
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+ ;;
+ pgCC* | pgcpp*)
+ # Portland Group C++ compiler
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+ cxx*)
+ # Compaq C++
+ # Make sure the PIC flag is empty. It appears that all Alpha
+ # Linux and Compaq Tru64 Unix objects are PIC.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ ;;
+ xlc* | xlC*)
+ # IBM XL 8.0 on PPC
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink'
+ ;;
+ *)
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*)
+ # Sun C++ 5.9
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ lynxos*)
+ ;;
+ m88k*)
+ ;;
+ mvs*)
+ case $cc_basename in
+ cxx*)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ netbsd*)
+ ;;
+ *qnx* | *nto*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+ ;;
+ osf3* | osf4* | osf5*)
+ case $cc_basename in
+ KCC*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,'
+ ;;
+ RCC*)
+ # Rational C++ 2.4.1
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+ ;;
+ cxx*)
+ # Digital/Compaq C++
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ # Make sure the PIC flag is empty. It appears that all Alpha
+ # Linux and Compaq Tru64 Unix objects are PIC.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ psos*)
+ ;;
+ solaris*)
+ case $cc_basename in
+ CC*)
+ # Sun C++ 4.2, 5.x and Centerline C++
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+ ;;
+ gcx*)
+ # Green Hills C++ Compiler
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ sunos4*)
+ case $cc_basename in
+ CC*)
+ # Sun C++ 4.x
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+ lcc*)
+ # Lucid
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+ case $cc_basename in
+ CC*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+ esac
+ ;;
+ tandem*)
+ case $cc_basename in
+ NCC*)
+ # NonStop-UX NCC 3.20
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ vxworks*)
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+ ;;
+ esac
+ fi
+],
+[
+ if test "$GCC" = yes; then
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+
+ case $host_os in
+ aix*)
+ # All AIX code is PIC.
+ if test "$host_cpu" = ia64; then
+ # AIX 5 now supports IA64 processor
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ m68k)
+ # FIXME: we need at least 68020 code to build shared libraries, but
+ # adding the `-m68020' flag to GCC prevents building anything better,
+ # like `-m68040'.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4'
+ ;;
+ esac
+ ;;
+
+ beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+ # PIC is the default for these OSes.
+ ;;
+
+ mingw* | cygwin* | pw32* | os2* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ # Although the cygwin gcc ignores -fPIC, still need this for old-style
+ # (--disable-auto-import) libraries
+ m4_if([$1], [GCJ], [],
+ [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+ ;;
+
+ darwin* | rhapsody*)
+ # PIC is the default on this platform
+ # Common symbols not allowed in MH_DYLIB files
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common'
+ ;;
+
+ hpux*)
+ # PIC is the default for 64-bit PA HP-UX, but not for 32-bit
+ # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag
+ # sets the default TLS model and affects inlining.
+ case $host_cpu in
+ hppa*64*)
+ # +Z the default
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ esac
+ ;;
+
+ interix[[3-9]]*)
+ # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+ # Instead, we relocate shared libraries at runtime.
+ ;;
+
+ msdosdjgpp*)
+ # Just because we use GCC doesn't mean we suddenly get shared libraries
+ # on systems that don't support them.
+ _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+ enable_shared=no
+ ;;
+
+ *nto* | *qnx*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic
+ fi
+ ;;
+
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ esac
+ else
+ # PORTME Check for flag to pass linker flags through the system compiler.
+ case $host_os in
+ aix*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ if test "$host_cpu" = ia64; then
+ # AIX 5 now supports IA64 processor
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ else
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp'
+ fi
+ ;;
+
+ mingw* | cygwin* | pw32* | os2* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ m4_if([$1], [GCJ], [],
+ [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+ ;;
+
+ hpux9* | hpux10* | hpux11*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+ # not for PA HP-UX.
+ case $host_cpu in
+ hppa*64*|ia64*)
+ # +Z the default
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
+ ;;
+ esac
+ # Is there a better lt_prog_compiler_static that works with the bundled CC?
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive'
+ ;;
+
+ irix5* | irix6* | nonstopux*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ # PIC (with -KPIC) is the default.
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ ;;
+
+ linux* | k*bsd*-gnu)
+ case $cc_basename in
+ # old Intel for x86_64 which still supported -KPIC.
+ ecc*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+ ;;
+ # icc used to be incompatible with GCC.
+ # ICC 10 doesn't accept -KPIC any more.
+ icc* | ifort*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+ ;;
+ # Lahey Fortran 8.1.
+ lf95*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='--static'
+ ;;
+ pgcc* | pgf77* | pgf90* | pgf95*)
+ # Portland Group compilers (*not* the Pentium gcc compiler,
+ # which looks to be a dead project)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+ ccc*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ # All Alpha code is PIC.
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ ;;
+ xl*)
+ # IBM XL C 8.0/Fortran 10.1 on PPC
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink'
+ ;;
+ *)
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*)
+ # Sun C 5.9
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ ;;
+ *Sun\ F*)
+ # Sun Fortran 8.3 passes all unrecognized flags to the linker
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)=''
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+
+ newsos6)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+
+ *nto* | *qnx*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+ ;;
+
+ osf3* | osf4* | osf5*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ # All OSF/1 code is PIC.
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ ;;
+
+ rdos*)
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ ;;
+
+ solaris*)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ case $cc_basename in
+ f77* | f90* | f95*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';;
+ esac
+ ;;
+
+ sunos4*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+
+ sysv4 | sysv4.2uw2* | sysv4.3*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec ;then
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ fi
+ ;;
+
+ sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+
+ unicos*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+ ;;
+
+ uts4*)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+
+ *)
+ _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+ ;;
+ esac
+ fi
+])
+case $host_os in
+ # For platforms which do not support PIC, -DPIC is meaningless:
+ *djgpp*)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])"
+ ;;
+esac
+AC_MSG_RESULT([$_LT_TAGVAR(lt_prog_compiler_pic, $1)])
+_LT_TAGDECL([wl], [lt_prog_compiler_wl], [1],
+ [How to pass a linker flag through the compiler])
+
+#
+# Check to make sure the PIC flag actually works.
+#
+if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then
+ _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works],
+ [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)],
+ [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [],
+ [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in
+ "" | " "*) ;;
+ *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;;
+ esac],
+ [_LT_TAGVAR(lt_prog_compiler_pic, $1)=
+ _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no])
+fi
+_LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1],
+ [Additional compiler flags for building library objects])
+
+#
+# Check to make sure the static flag actually works.
+#
+wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\"
+_LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works],
+ _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1),
+ $lt_tmp_static_flag,
+ [],
+ [_LT_TAGVAR(lt_prog_compiler_static, $1)=])
+_LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1],
+ [Compiler flag to prevent dynamic linking])
+])# _LT_COMPILER_PIC
+
+
+# _LT_LINKER_SHLIBS([TAGNAME])
+# ----------------------------
+# See if the linker supports building shared libraries.
+m4_defun([_LT_LINKER_SHLIBS],
+[AC_REQUIRE([LT_PATH_LD])dnl
+AC_REQUIRE([LT_PATH_NM])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl
+m4_require([_LT_TAG_COMPILER])dnl
+AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries])
+m4_if([$1], [CXX], [
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+ case $host_os in
+ aix[[4-9]]*)
+ # If we're using GNU nm, then we don't want the "-C" option.
+ # -C means demangle to AIX nm, but means don't demangle with GNU nm
+ if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+ else
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+ fi
+ ;;
+ pw32*)
+ _LT_TAGVAR(export_symbols_cmds, $1)="$ltdll_cmds"
+ ;;
+ cygwin* | mingw* | cegcc*)
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;/^.*[[ ]]__nm__/s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols'
+ ;;
+ *)
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+ ;;
+ esac
+ _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*']
+], [
+ runpath_var=
+ _LT_TAGVAR(allow_undefined_flag, $1)=
+ _LT_TAGVAR(always_export_symbols, $1)=no
+ _LT_TAGVAR(archive_cmds, $1)=
+ _LT_TAGVAR(archive_expsym_cmds, $1)=
+ _LT_TAGVAR(compiler_needs_object, $1)=no
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)=
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+ _LT_TAGVAR(hardcode_automatic, $1)=no
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=no
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+ _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)=
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=
+ _LT_TAGVAR(hardcode_minus_L, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
+ _LT_TAGVAR(inherit_rpath, $1)=no
+ _LT_TAGVAR(link_all_deplibs, $1)=unknown
+ _LT_TAGVAR(module_cmds, $1)=
+ _LT_TAGVAR(module_expsym_cmds, $1)=
+ _LT_TAGVAR(old_archive_from_new_cmds, $1)=
+ _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)=
+ _LT_TAGVAR(thread_safe_flag_spec, $1)=
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=
+ # include_expsyms should be a list of space-separated symbols to be *always*
+ # included in the symbol list
+ _LT_TAGVAR(include_expsyms, $1)=
+ # exclude_expsyms can be an extended regexp of symbols to exclude
+ # it will be wrapped by ` (' and `)$', so one must not match beginning or
+ # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc',
+ # as well as any symbol that contains `d'.
+ _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*']
+ # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
+ # platforms (ab)use it in PIC code, but their linkers get confused if
+ # the symbol is explicitly referenced. Since portable code cannot
+ # rely on this symbol name, it's probably fine to never include it in
+ # preloaded symbol tables.
+ # Exclude shared library initialization/finalization symbols.
+dnl Note also adjust exclude_expsyms for C++ above.
+ extract_expsyms_cmds=
+
+ case $host_os in
+ cygwin* | mingw* | pw32* | cegcc*)
+ # FIXME: the MSVC++ port hasn't been tested in a loooong time
+ # When not using gcc, we currently assume that we are using
+ # Microsoft Visual C++.
+ if test "$GCC" != yes; then
+ with_gnu_ld=no
+ fi
+ ;;
+ interix*)
+ # we just hope/assume this is gcc and not c89 (= MSVC++)
+ with_gnu_ld=yes
+ ;;
+ openbsd*)
+ with_gnu_ld=no
+ ;;
+ esac
+
+ _LT_TAGVAR(ld_shlibs, $1)=yes
+ if test "$with_gnu_ld" = yes; then
+ # If archive_cmds runs LD, not CC, wlarc should be empty
+ wlarc='${wl}'
+
+ # Set some defaults for GNU ld with shared library support. These
+ # are reset later if shared libraries are not supported. Putting them
+ # here allows them to be overridden if necessary.
+ runpath_var=LD_RUN_PATH
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+ # ancient GNU ld didn't support --whole-archive et. al.
+ if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then
+ _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+ else
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=
+ fi
+ supports_anon_versioning=no
+ case `$LD -v 2>&1` in
+ *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11
+ *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ...
+ *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ...
+ *\ 2.11.*) ;; # other 2.11 versions
+ *) supports_anon_versioning=yes ;;
+ esac
+
+ # See if GNU ld supports shared libraries.
+ case $host_os in
+ aix[[3-9]]*)
+ # On AIX/PPC, the GNU linker is very broken
+ if test "$host_cpu" != ia64; then
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the GNU linker, at least up to release 2.9.1, is reported
+*** to be unable to reliably create shared libraries on AIX.
+*** Therefore, libtool is disabling shared libraries support. If you
+*** really care for shared libraries, you may want to modify your PATH
+*** so that a non-GNU linker is found, and then restart.
+
+_LT_EOF
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)=''
+ ;;
+ m68k)
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ ;;
+ esac
+ ;;
+
+ beos*)
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ # Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+ # support --undefined. This deserves some investigation. FIXME
+ _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ cygwin* | mingw* | pw32* | cegcc*)
+ # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless,
+ # as there is no search path for DLLs.
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ _LT_TAGVAR(always_export_symbols, $1)=no
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols'
+
+ if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ # If the export-symbols file already is a .def file (1st line
+ # is EXPORTS), use it as is; otherwise, prepend...
+ _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+ cp $export_symbols $output_objdir/$soname.def;
+ else
+ echo EXPORTS > $output_objdir/$soname.def;
+ cat $export_symbols >> $output_objdir/$soname.def;
+ fi~
+ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ interix[[3-9]]*)
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+ # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+ # Instead, shared libraries are loaded at an image base (0x10000000 by
+ # default) and relocated if they conflict, which is a slow very memory
+ # consuming and fragmenting process. To avoid this, we pick a random,
+ # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+ # time. Moving up from 0x10000000 also allows more sbrk(2) space.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ ;;
+
+ gnu* | linux* | tpf* | k*bsd*-gnu)
+ tmp_diet=no
+ if test "$host_os" = linux-dietlibc; then
+ case $cc_basename in
+ diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn)
+ esac
+ fi
+ if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \
+ && test "$tmp_diet" = no
+ then
+ tmp_addflag=
+ tmp_sharedflag='-shared'
+ case $cc_basename,$host_cpu in
+ pgcc*) # Portland Group C compiler
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+ tmp_addflag=' $pic_flag'
+ ;;
+ pgf77* | pgf90* | pgf95*) # Portland Group f77 and f90 compilers
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+ tmp_addflag=' $pic_flag -Mnomain' ;;
+ ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64
+ tmp_addflag=' -i_dynamic' ;;
+ efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64
+ tmp_addflag=' -i_dynamic -nofor_main' ;;
+ ifc* | ifort*) # Intel Fortran compiler
+ tmp_addflag=' -nofor_main' ;;
+ lf95*) # Lahey Fortran 8.1
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=
+ tmp_sharedflag='--shared' ;;
+ xl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below)
+ tmp_sharedflag='-qmkshrobj'
+ tmp_addflag= ;;
+ esac
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*) # Sun C 5.9
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+ _LT_TAGVAR(compiler_needs_object, $1)=yes
+ tmp_sharedflag='-G' ;;
+ *Sun\ F*) # Sun Fortran 8.3
+ tmp_sharedflag='-G' ;;
+ esac
+ _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+
+ if test "x$supports_anon_versioning" = xyes; then
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
+ fi
+
+ case $cc_basename in
+ xlf*)
+ # IBM XL Fortran 10.1 on PPC cannot create shared libs itself
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+ _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='-rpath $libdir'
+ _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $compiler_flags -soname $soname -o $lib'
+ if test "x$supports_anon_versioning" = xyes; then
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $LD -shared $libobjs $deplibs $compiler_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib'
+ fi
+ ;;
+ esac
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
+ wlarc=
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ fi
+ ;;
+
+ solaris*)
+ if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: The releases 2.8.* of the GNU linker cannot reliably
+*** create shared libraries on Solaris systems. Therefore, libtool
+*** is disabling shared libraries support. We urge you to upgrade GNU
+*** binutils to release 2.9.1 or newer. Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+ elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*)
+ case `$LD -v 2>&1` in
+ *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*)
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not
+*** reliably create shared libraries on SCO systems. Therefore, libtool
+*** is disabling shared libraries support. We urge you to upgrade GNU
+*** binutils to release 2.16.91.0.3 or newer. Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+ ;;
+ *)
+ # For security reasons, it is highly recommended that you always
+ # use absolute paths for naming shared libraries, and exclude the
+ # DT_RUNPATH tag from executables and libraries. But doing so
+ # requires that you compile everything twice, which is a pain.
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+ esac
+ ;;
+
+ sunos4*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ wlarc=
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ *)
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+ esac
+
+ if test "$_LT_TAGVAR(ld_shlibs, $1)" = no; then
+ runpath_var=
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)=
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=
+ fi
+ else
+ # PORTME fill in a description of your system's linker (not GNU ld)
+ case $host_os in
+ aix3*)
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ _LT_TAGVAR(always_export_symbols, $1)=yes
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname'
+ # Note: this linker hardcodes the directories in LIBPATH if there
+ # are no directories specified by -L.
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then
+ # Neither direct hardcoding nor static linking is supported with a
+ # broken collect2.
+ _LT_TAGVAR(hardcode_direct, $1)=unsupported
+ fi
+ ;;
+
+ aix[[4-9]]*)
+ if test "$host_cpu" = ia64; then
+ # On IA64, the linker does run time linking by default, so we don't
+ # have to do anything special.
+ aix_use_runtimelinking=no
+ exp_sym_flag='-Bexport'
+ no_entry_flag=""
+ else
+ # If we're using GNU nm, then we don't want the "-C" option.
+ # -C means demangle to AIX nm, but means don't demangle with GNU nm
+ if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+ else
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+ fi
+ aix_use_runtimelinking=no
+
+ # Test if we are trying to use run time linking or normal
+ # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+ # need to do runtime linking.
+ case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*)
+ for ld_flag in $LDFLAGS; do
+ if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then
+ aix_use_runtimelinking=yes
+ break
+ fi
+ done
+ ;;
+ esac
+
+ exp_sym_flag='-bexport'
+ no_entry_flag='-bnoentry'
+ fi
+
+ # When large executables or shared objects are built, AIX ld can
+ # have problems creating the table of contents. If linking a library
+ # or program results in "error TOC overflow" add -mminimal-toc to
+ # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not
+ # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+ _LT_TAGVAR(archive_cmds, $1)=''
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ _LT_TAGVAR(file_list_spec, $1)='${wl}-f,'
+
+ if test "$GCC" = yes; then
+ case $host_os in aix4.[[012]]|aix4.[[012]].*)
+ # We only want to do this on AIX 4.2 and lower, the check
+ # below for broken collect2 doesn't work under 4.3+
+ collect2name=`${CC} -print-prog-name=collect2`
+ if test -f "$collect2name" &&
+ strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+ then
+ # We have reworked collect2
+ :
+ else
+ # We have old collect2
+ _LT_TAGVAR(hardcode_direct, $1)=unsupported
+ # It fails to find uninstalled libraries when the uninstalled
+ # path is not listed in the libpath. Setting hardcode_minus_L
+ # to unsupported forces relinking
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=
+ fi
+ ;;
+ esac
+ shared_flag='-shared'
+ if test "$aix_use_runtimelinking" = yes; then
+ shared_flag="$shared_flag "'${wl}-G'
+ fi
+ else
+ # not using gcc
+ if test "$host_cpu" = ia64; then
+ # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+ # chokes on -Wl,-G. The following line is correct:
+ shared_flag='-G'
+ else
+ if test "$aix_use_runtimelinking" = yes; then
+ shared_flag='${wl}-G'
+ else
+ shared_flag='${wl}-bM:SRE'
+ fi
+ fi
+ fi
+
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall'
+ # It seems that -bexpall does not export symbols beginning with
+ # underscore (_), so it is better to generate a list of symbols to export.
+ _LT_TAGVAR(always_export_symbols, $1)=yes
+ if test "$aix_use_runtimelinking" = yes; then
+ # Warning - without using the other runtime loading flags (-brtl),
+ # -berok will link without error, but may produce a broken library.
+ _LT_TAGVAR(allow_undefined_flag, $1)='-berok'
+ # Determine the default libpath from the value encoded in an
+ # empty executable.
+ _LT_SYS_MODULE_PATH_AIX
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then $ECHO "X${wl}${allow_undefined_flag}" | $Xsed; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+ else
+ if test "$host_cpu" = ia64; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib'
+ _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs"
+ _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols"
+ else
+ # Determine the default libpath from the value encoded in an
+ # empty executable.
+ _LT_SYS_MODULE_PATH_AIX
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
+ # Warning - without using the other run time loading flags,
+ # -berok will link without error, but may produce a broken library.
+ _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok'
+ _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok'
+ # Exported symbols can be pulled into shared objects from archives
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience'
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+ # This is similar to how AIX traditionally builds its shared libraries.
+ _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
+ fi
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)=''
+ ;;
+ m68k)
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ ;;
+ esac
+ ;;
+
+ bsdi[[45]]*)
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic
+ ;;
+
+ cygwin* | mingw* | pw32* | cegcc*)
+ # When not using gcc, we currently assume that we are using
+ # Microsoft Visual C++.
+ # hardcode_libdir_flag_spec is actually meaningless, as there is
+ # no search path for DLLs.
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' '
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ # Tell ltmain to make .lib files, not .a files.
+ libext=lib
+ # Tell ltmain to make .dll files, not .so files.
+ shrext_cmds=".dll"
+ # FIXME: Setting linknames here is a bad hack.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `$ECHO "X$deplibs" | $Xsed -e '\''s/ -lc$//'\''` -link -dll~linknames='
+ # The linker will automatically build a .lib file if we build a DLL.
+ _LT_TAGVAR(old_archive_from_new_cmds, $1)='true'
+ # FIXME: Should let the user specify the lib program.
+ _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs'
+ _LT_TAGVAR(fix_srcfile_path, $1)='`cygpath -w "$srcfile"`'
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+ ;;
+
+ darwin* | rhapsody*)
+ _LT_DARWIN_LINKER_FEATURES($1)
+ ;;
+
+ dgux*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ freebsd1*)
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+
+ # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
+ # support. Future versions do this automatically, but an explicit c++rt0.o
+ # does not break anything, and helps significantly (at the cost of a little
+ # extra space).
+ freebsd2.2*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ # Unfortunately, older versions of FreeBSD 2 do not have this feature.
+ freebsd2*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ # FreeBSD 3 and greater uses gcc -shared to do shared libraries.
+ freebsd* | dragonfly*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ hpux9*)
+ if test "$GCC" = yes; then
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+ fi
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+ ;;
+
+ hpux10*)
+ if test "$GCC" = yes -a "$with_gnu_ld" = no; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
+ fi
+ if test "$with_gnu_ld" = no; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+ _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='+b $libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ fi
+ ;;
+
+ hpux11*)
+ if test "$GCC" = yes -a "$with_gnu_ld" = no; then
+ case $host_cpu in
+ hppa*64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ ia64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ else
+ case $host_cpu in
+ hppa*64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ ia64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ fi
+ if test "$with_gnu_ld" = no; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+ case $host_cpu in
+ hppa*64*|ia64*)
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+ *)
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ ;;
+ esac
+ fi
+ ;;
+
+ irix5* | irix6* | nonstopux*)
+ if test "$GCC" = yes; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ # Try to use the -exported_symbol ld option, if it does not
+ # work, assume that -exports_file does not work either and
+ # implicitly export all symbols.
+ save_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null"
+ AC_LINK_IFELSE(int foo(void) {},
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib'
+ )
+ LDFLAGS="$save_LDFLAGS"
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib'
+ fi
+ _LT_TAGVAR(archive_cmds_need_lc, $1)='no'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ _LT_TAGVAR(inherit_rpath, $1)=yes
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ ;;
+
+ netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF
+ fi
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ newsos6)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ *nto* | *qnx*)
+ ;;
+
+ openbsd*)
+ if test -f /usr/libexec/ld.so; then
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+ else
+ case $host_os in
+ openbsd[[01]].* | openbsd2.[[0-7]] | openbsd2.[[0-7]].*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+ ;;
+ esac
+ fi
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ os2*)
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$ECHO DATA >> $output_objdir/$libname.def~$ECHO " SINGLE NONSHARED" >> $output_objdir/$libname.def~$ECHO EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def'
+ _LT_TAGVAR(old_archive_from_new_cmds, $1)='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def'
+ ;;
+
+ osf3*)
+ if test "$GCC" = yes; then
+ _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ else
+ _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+ fi
+ _LT_TAGVAR(archive_cmds_need_lc, $1)='no'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ ;;
+
+ osf4* | osf5*) # as osf3* with the addition of -msym flag
+ if test "$GCC" = yes; then
+ _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ else
+ _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~
+ $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp'
+
+ # Both c and cxx compiler support -rpath directly
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
+ fi
+ _LT_TAGVAR(archive_cmds_need_lc, $1)='no'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ ;;
+
+ solaris*)
+ _LT_TAGVAR(no_undefined_flag, $1)=' -z defs'
+ if test "$GCC" = yes; then
+ wlarc='${wl}'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -shared ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+ else
+ case `$CC -V 2>&1` in
+ *"Compilers 5.0"*)
+ wlarc=''
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp'
+ ;;
+ *)
+ wlarc='${wl}'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+ ;;
+ esac
+ fi
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ case $host_os in
+ solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
+ *)
+ # The compiler driver will combine and reorder linker options,
+ # but understands `-z linker_flag'. GCC discards it without `$wl',
+ # but is careful enough not to reorder.
+ # Supported since Solaris 2.6 (maybe 2.5.1?)
+ if test "$GCC" = yes; then
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract'
+ else
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract'
+ fi
+ ;;
+ esac
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ ;;
+
+ sunos4*)
+ if test "x$host_vendor" = xsequent; then
+ # Use $CC to link under sequent, because it throws in some extra .o
+ # files that make .init and .fini sections work.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags'
+ fi
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ sysv4)
+ case $host_vendor in
+ sni)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true???
+ ;;
+ siemens)
+ ## LD is ld it makes a PLAMLIB
+ ## CC just makes a GrossModule.
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs'
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ ;;
+ motorola)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie
+ ;;
+ esac
+ runpath_var='LD_RUN_PATH'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ sysv4.3*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ runpath_var=LD_RUN_PATH
+ hardcode_runpath_var=yes
+ _LT_TAGVAR(ld_shlibs, $1)=yes
+ fi
+ ;;
+
+ sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*)
+ _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text'
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ runpath_var='LD_RUN_PATH'
+
+ if test "$GCC" = yes; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ fi
+ ;;
+
+ sysv5* | sco3.2v5* | sco5v6*)
+ # Note: We can NOT use -z defs as we might desire, because we do not
+ # link with -lc, and that would cause any symbols used from libc to
+ # always be unresolved, which means just about no library would
+ # ever link correctly. If we're not using GNU ld we use -z text
+ # though, which does catch some bad symbols but isn't as heavy-handed
+ # as -z defs.
+ _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text'
+ _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs'
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport'
+ runpath_var='LD_RUN_PATH'
+
+ if test "$GCC" = yes; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ fi
+ ;;
+
+ uts4*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ *)
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+
+ if test x$host_vendor = xsni; then
+ case $host in
+ sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Blargedynsym'
+ ;;
+ esac
+ fi
+ fi
+])
+AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)])
+test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no
+
+_LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld
+
+_LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl
+_LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl
+_LT_DECL([], [extract_expsyms_cmds], [2],
+ [The commands to extract the exported symbol list from a shared archive])
+
+#
+# Do we need to explicitly link libc?
+#
+case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in
+x|xyes)
+ # Assume -lc should be added
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+
+ if test "$enable_shared" = yes && test "$GCC" = yes; then
+ case $_LT_TAGVAR(archive_cmds, $1) in
+ *'~'*)
+ # FIXME: we may have to deal with multi-command sequences.
+ ;;
+ '$CC '*)
+ # Test whether the compiler implicitly links with -lc since on some
+ # systems, -lgcc has to come before -lc. If gcc already passes -lc
+ # to ld, don't add -lc before -lgcc.
+ AC_MSG_CHECKING([whether -lc should be explicitly linked in])
+ $RM conftest*
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ if AC_TRY_EVAL(ac_compile) 2>conftest.err; then
+ soname=conftest
+ lib=conftest
+ libobjs=conftest.$ac_objext
+ deplibs=
+ wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1)
+ pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1)
+ compiler_flags=-v
+ linker_flags=-v
+ verstring=
+ output_objdir=.
+ libname=conftest
+ lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1)
+ _LT_TAGVAR(allow_undefined_flag, $1)=
+ if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1)
+ then
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ else
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+ fi
+ _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag
+ else
+ cat conftest.err 1>&5
+ fi
+ $RM conftest*
+ AC_MSG_RESULT([$_LT_TAGVAR(archive_cmds_need_lc, $1)])
+ ;;
+ esac
+ fi
+ ;;
+esac
+
+_LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0],
+ [Whether or not to add -lc for building shared libraries])
+_LT_TAGDECL([allow_libtool_libs_with_static_runtimes],
+ [enable_shared_with_static_runtimes], [0],
+ [Whether or not to disallow shared libs when runtime libs are static])
+_LT_TAGDECL([], [export_dynamic_flag_spec], [1],
+ [Compiler flag to allow reflexive dlopens])
+_LT_TAGDECL([], [whole_archive_flag_spec], [1],
+ [Compiler flag to generate shared objects directly from archives])
+_LT_TAGDECL([], [compiler_needs_object], [1],
+ [Whether the compiler copes with passing no objects directly])
+_LT_TAGDECL([], [old_archive_from_new_cmds], [2],
+ [Create an old-style archive from a shared archive])
+_LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2],
+ [Create a temporary old-style archive to link instead of a shared archive])
+_LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive])
+_LT_TAGDECL([], [archive_expsym_cmds], [2])
+_LT_TAGDECL([], [module_cmds], [2],
+ [Commands used to build a loadable module if different from building
+ a shared archive.])
+_LT_TAGDECL([], [module_expsym_cmds], [2])
+_LT_TAGDECL([], [with_gnu_ld], [1],
+ [Whether we are building with GNU ld or not])
+_LT_TAGDECL([], [allow_undefined_flag], [1],
+ [Flag that allows shared libraries with undefined symbols to be built])
+_LT_TAGDECL([], [no_undefined_flag], [1],
+ [Flag that enforces no undefined symbols])
+_LT_TAGDECL([], [hardcode_libdir_flag_spec], [1],
+ [Flag to hardcode $libdir into a binary during linking.
+ This must work even if $libdir does not exist])
+_LT_TAGDECL([], [hardcode_libdir_flag_spec_ld], [1],
+ [[If ld is used when linking, flag to hardcode $libdir into a binary
+ during linking. This must work even if $libdir does not exist]])
+_LT_TAGDECL([], [hardcode_libdir_separator], [1],
+ [Whether we need a single "-rpath" flag with a separated argument])
+_LT_TAGDECL([], [hardcode_direct], [0],
+ [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes
+ DIR into the resulting binary])
+_LT_TAGDECL([], [hardcode_direct_absolute], [0],
+ [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes
+ DIR into the resulting binary and the resulting library dependency is
+ "absolute", i.e impossible to change by setting ${shlibpath_var} if the
+ library is relocated])
+_LT_TAGDECL([], [hardcode_minus_L], [0],
+ [Set to "yes" if using the -LDIR flag during linking hardcodes DIR
+ into the resulting binary])
+_LT_TAGDECL([], [hardcode_shlibpath_var], [0],
+ [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR
+ into the resulting binary])
+_LT_TAGDECL([], [hardcode_automatic], [0],
+ [Set to "yes" if building a shared library automatically hardcodes DIR
+ into the library and all subsequent libraries and executables linked
+ against it])
+_LT_TAGDECL([], [inherit_rpath], [0],
+ [Set to yes if linker adds runtime paths of dependent libraries
+ to runtime path list])
+_LT_TAGDECL([], [link_all_deplibs], [0],
+ [Whether libtool must link a program against all its dependency libraries])
+_LT_TAGDECL([], [fix_srcfile_path], [1],
+ [Fix the shell variable $srcfile for the compiler])
+_LT_TAGDECL([], [always_export_symbols], [0],
+ [Set to "yes" if exported symbols are required])
+_LT_TAGDECL([], [export_symbols_cmds], [2],
+ [The commands to list exported symbols])
+_LT_TAGDECL([], [exclude_expsyms], [1],
+ [Symbols that should not be listed in the preloaded symbols])
+_LT_TAGDECL([], [include_expsyms], [1],
+ [Symbols that must always be exported])
+_LT_TAGDECL([], [prelink_cmds], [2],
+ [Commands necessary for linking programs (against libraries) with templates])
+_LT_TAGDECL([], [file_list_spec], [1],
+ [Specify filename containing input files])
+dnl FIXME: Not yet implemented
+dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1],
+dnl [Compiler flag to generate thread safe objects])
+])# _LT_LINKER_SHLIBS
+
+
+# _LT_LANG_C_CONFIG([TAG])
+# ------------------------
+# Ensure that the configuration variables for a C compiler are suitably
+# defined. These variables are subsequently used by _LT_CONFIG to write
+# the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_C_CONFIG],
+[m4_require([_LT_DECL_EGREP])dnl
+lt_save_CC="$CC"
+AC_LANG_PUSH(C)
+
+# Source file extension for C test sources.
+ac_ext=c
+
+# Object file extension for compiled C test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="int some_variable = 0;"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='int main(){return(0);}'
+
+_LT_TAG_COMPILER
+# Save the default compiler, since it gets overwritten when the other
+# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP.
+compiler_DEFAULT=$CC
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+if test -n "$compiler"; then
+ _LT_COMPILER_NO_RTTI($1)
+ _LT_COMPILER_PIC($1)
+ _LT_COMPILER_C_O($1)
+ _LT_COMPILER_FILE_LOCKS($1)
+ _LT_LINKER_SHLIBS($1)
+ _LT_SYS_DYNAMIC_LINKER($1)
+ _LT_LINKER_HARDCODE_LIBPATH($1)
+ LT_SYS_DLOPEN_SELF
+ _LT_CMD_STRIPLIB
+
+ # Report which library types will actually be built
+ AC_MSG_CHECKING([if libtool supports shared libraries])
+ AC_MSG_RESULT([$can_build_shared])
+
+ AC_MSG_CHECKING([whether to build shared libraries])
+ test "$can_build_shared" = "no" && enable_shared=no
+
+ # On AIX, shared libraries and static libraries use the same namespace, and
+ # are all built from PIC.
+ case $host_os in
+ aix3*)
+ test "$enable_shared" = yes && enable_static=no
+ if test -n "$RANLIB"; then
+ archive_cmds="$archive_cmds~\$RANLIB \$lib"
+ postinstall_cmds='$RANLIB $lib'
+ fi
+ ;;
+
+ aix[[4-9]]*)
+ if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
+ test "$enable_shared" = yes && enable_static=no
+ fi
+ ;;
+ esac
+ AC_MSG_RESULT([$enable_shared])
+
+ AC_MSG_CHECKING([whether to build static libraries])
+ # Make sure either enable_shared or enable_static is yes.
+ test "$enable_shared" = yes || enable_static=yes
+ AC_MSG_RESULT([$enable_static])
+
+ _LT_CONFIG($1)
+fi
+AC_LANG_POP
+CC="$lt_save_CC"
+])# _LT_LANG_C_CONFIG
+
+
+# _LT_PROG_CXX
+# ------------
+# Since AC_PROG_CXX is broken, in that it returns g++ if there is no c++
+# compiler, we have our own version here.
+m4_defun([_LT_PROG_CXX],
+[
+pushdef([AC_MSG_ERROR], [_lt_caught_CXX_error=yes])
+AC_PROG_CXX
+if test -n "$CXX" && ( test "X$CXX" != "Xno" &&
+ ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) ||
+ (test "X$CXX" != "Xg++"))) ; then
+ AC_PROG_CXXCPP
+else
+ _lt_caught_CXX_error=yes
+fi
+popdef([AC_MSG_ERROR])
+])# _LT_PROG_CXX
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([_LT_PROG_CXX], [])
+
+
+# _LT_LANG_CXX_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for a C++ compiler are suitably
+# defined. These variables are subsequently used by _LT_CONFIG to write
+# the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_CXX_CONFIG],
+[AC_REQUIRE([_LT_PROG_CXX])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_EGREP])dnl
+
+AC_LANG_PUSH(C++)
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+_LT_TAGVAR(allow_undefined_flag, $1)=
+_LT_TAGVAR(always_export_symbols, $1)=no
+_LT_TAGVAR(archive_expsym_cmds, $1)=
+_LT_TAGVAR(compiler_needs_object, $1)=no
+_LT_TAGVAR(export_dynamic_flag_spec, $1)=
+_LT_TAGVAR(hardcode_direct, $1)=no
+_LT_TAGVAR(hardcode_direct_absolute, $1)=no
+_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+_LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)=
+_LT_TAGVAR(hardcode_libdir_separator, $1)=
+_LT_TAGVAR(hardcode_minus_L, $1)=no
+_LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
+_LT_TAGVAR(hardcode_automatic, $1)=no
+_LT_TAGVAR(inherit_rpath, $1)=no
+_LT_TAGVAR(module_cmds, $1)=
+_LT_TAGVAR(module_expsym_cmds, $1)=
+_LT_TAGVAR(link_all_deplibs, $1)=unknown
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(no_undefined_flag, $1)=
+_LT_TAGVAR(whole_archive_flag_spec, $1)=
+_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+
+# Source file extension for C++ test sources.
+ac_ext=cpp
+
+# Object file extension for compiled C++ test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# No sense in running all these tests if we already determined that
+# the CXX compiler isn't working. Some variables (like enable_shared)
+# are currently assumed to apply to all compilers on this platform,
+# and will be corrupted by setting them based on a non-working compiler.
+if test "$_lt_caught_CXX_error" != yes; then
+ # Code to be used in simple compile tests
+ lt_simple_compile_test_code="int some_variable = 0;"
+
+ # Code to be used in simple link tests
+ lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }'
+
+ # ltmain only uses $CC for tagged configurations so make sure $CC is set.
+ _LT_TAG_COMPILER
+
+ # save warnings/boilerplate of simple test code
+ _LT_COMPILER_BOILERPLATE
+ _LT_LINKER_BOILERPLATE
+
+ # Allow CC to be a program name with arguments.
+ lt_save_CC=$CC
+ lt_save_LD=$LD
+ lt_save_GCC=$GCC
+ GCC=$GXX
+ lt_save_with_gnu_ld=$with_gnu_ld
+ lt_save_path_LD=$lt_cv_path_LD
+ if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then
+ lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx
+ else
+ $as_unset lt_cv_prog_gnu_ld
+ fi
+ if test -n "${lt_cv_path_LDCXX+set}"; then
+ lt_cv_path_LD=$lt_cv_path_LDCXX
+ else
+ $as_unset lt_cv_path_LD
+ fi
+ test -z "${LDCXX+set}" || LD=$LDCXX
+ CC=${CXX-"c++"}
+ compiler=$CC
+ _LT_TAGVAR(compiler, $1)=$CC
+ _LT_CC_BASENAME([$compiler])
+
+ if test -n "$compiler"; then
+ # We don't want -fno-exception when compiling C++ code, so set the
+ # no_builtin_flag separately
+ if test "$GXX" = yes; then
+ _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin'
+ else
+ _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=
+ fi
+
+ if test "$GXX" = yes; then
+ # Set up default GNU C++ configuration
+
+ LT_PATH_LD
+
+ # Check if GNU C++ uses GNU ld as the underlying linker, since the
+ # archiving commands below assume that GNU ld is being used.
+ if test "$with_gnu_ld" = yes; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+
+ # If archive_cmds runs LD, not CC, wlarc should be empty
+ # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to
+ # investigate it a little bit more. (MM)
+ wlarc='${wl}'
+
+ # ancient GNU ld didn't support --whole-archive et. al.
+ if eval "`$CC -print-prog-name=ld` --help 2>&1" |
+ $GREP 'no-whole-archive' > /dev/null; then
+ _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+ else
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=
+ fi
+ else
+ with_gnu_ld=no
+ wlarc=
+
+ # A generic and very simple default shared library creation
+ # command for GNU C++ for the case where it uses the native
+ # linker, instead of GNU ld. If possible, this setting should
+ # overridden to take advantage of the native linker features on
+ # the platform it is being used on.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib'
+ fi
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"'
+
+ else
+ GXX=no
+ with_gnu_ld=no
+ wlarc=
+ fi
+
+ # PORTME: fill in a description of your system's C++ link characteristics
+ AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries])
+ _LT_TAGVAR(ld_shlibs, $1)=yes
+ case $host_os in
+ aix3*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ aix[[4-9]]*)
+ if test "$host_cpu" = ia64; then
+ # On IA64, the linker does run time linking by default, so we don't
+ # have to do anything special.
+ aix_use_runtimelinking=no
+ exp_sym_flag='-Bexport'
+ no_entry_flag=""
+ else
+ aix_use_runtimelinking=no
+
+ # Test if we are trying to use run time linking or normal
+ # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+ # need to do runtime linking.
+ case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*)
+ for ld_flag in $LDFLAGS; do
+ case $ld_flag in
+ *-brtl*)
+ aix_use_runtimelinking=yes
+ break
+ ;;
+ esac
+ done
+ ;;
+ esac
+
+ exp_sym_flag='-bexport'
+ no_entry_flag='-bnoentry'
+ fi
+
+ # When large executables or shared objects are built, AIX ld can
+ # have problems creating the table of contents. If linking a library
+ # or program results in "error TOC overflow" add -mminimal-toc to
+ # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not
+ # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+ _LT_TAGVAR(archive_cmds, $1)=''
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ _LT_TAGVAR(file_list_spec, $1)='${wl}-f,'
+
+ if test "$GXX" = yes; then
+ case $host_os in aix4.[[012]]|aix4.[[012]].*)
+ # We only want to do this on AIX 4.2 and lower, the check
+ # below for broken collect2 doesn't work under 4.3+
+ collect2name=`${CC} -print-prog-name=collect2`
+ if test -f "$collect2name" &&
+ strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+ then
+ # We have reworked collect2
+ :
+ else
+ # We have old collect2
+ _LT_TAGVAR(hardcode_direct, $1)=unsupported
+ # It fails to find uninstalled libraries when the uninstalled
+ # path is not listed in the libpath. Setting hardcode_minus_L
+ # to unsupported forces relinking
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=
+ fi
+ esac
+ shared_flag='-shared'
+ if test "$aix_use_runtimelinking" = yes; then
+ shared_flag="$shared_flag "'${wl}-G'
+ fi
+ else
+ # not using gcc
+ if test "$host_cpu" = ia64; then
+ # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+ # chokes on -Wl,-G. The following line is correct:
+ shared_flag='-G'
+ else
+ if test "$aix_use_runtimelinking" = yes; then
+ shared_flag='${wl}-G'
+ else
+ shared_flag='${wl}-bM:SRE'
+ fi
+ fi
+ fi
+
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall'
+ # It seems that -bexpall does not export symbols beginning with
+ # underscore (_), so it is better to generate a list of symbols to
+ # export.
+ _LT_TAGVAR(always_export_symbols, $1)=yes
+ if test "$aix_use_runtimelinking" = yes; then
+ # Warning - without using the other runtime loading flags (-brtl),
+ # -berok will link without error, but may produce a broken library.
+ _LT_TAGVAR(allow_undefined_flag, $1)='-berok'
+ # Determine the default libpath from the value encoded in an empty
+ # executable.
+ _LT_SYS_MODULE_PATH_AIX
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
+
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then $ECHO "X${wl}${allow_undefined_flag}" | $Xsed; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+ else
+ if test "$host_cpu" = ia64; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib'
+ _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs"
+ _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols"
+ else
+ # Determine the default libpath from the value encoded in an
+ # empty executable.
+ _LT_SYS_MODULE_PATH_AIX
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
+ # Warning - without using the other run time loading flags,
+ # -berok will link without error, but may produce a broken library.
+ _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok'
+ _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok'
+ # Exported symbols can be pulled into shared objects from archives
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience'
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+ # This is similar to how AIX traditionally builds its shared
+ # libraries.
+ _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
+ fi
+ fi
+ ;;
+
+ beos*)
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ # Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+ # support --undefined. This deserves some investigation. FIXME
+ _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ chorus*)
+ case $cc_basename in
+ *)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+ ;;
+
+ cygwin* | mingw* | pw32* | cegcc*)
+ # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless,
+ # as there is no search path for DLLs.
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ _LT_TAGVAR(always_export_symbols, $1)=no
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+
+ if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ # If the export-symbols file already is a .def file (1st line
+ # is EXPORTS), use it as is; otherwise, prepend...
+ _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+ cp $export_symbols $output_objdir/$soname.def;
+ else
+ echo EXPORTS > $output_objdir/$soname.def;
+ cat $export_symbols >> $output_objdir/$soname.def;
+ fi~
+ $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+ darwin* | rhapsody*)
+ _LT_DARWIN_LINKER_FEATURES($1)
+ ;;
+
+ dgux*)
+ case $cc_basename in
+ ec++*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ ghcx*)
+ # Green Hills C++ Compiler
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ *)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+ ;;
+
+ freebsd[[12]]*)
+ # C++ shared libraries reported to be fairly broken before
+ # switch to ELF
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+
+ freebsd-elf*)
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ ;;
+
+ freebsd* | dragonfly*)
+ # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF
+ # conventions
+ _LT_TAGVAR(ld_shlibs, $1)=yes
+ ;;
+
+ gnu*)
+ ;;
+
+ hpux9*)
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH,
+ # but as the default
+ # location of the library.
+
+ case $cc_basename in
+ CC*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ aCC*)
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ #
+ # There doesn't appear to be a way to prevent this compiler from
+ # explicitly linking system object files so we need to strip them
+ # from the output so that they don't get included in the library
+ # dependencies.
+ output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed'
+ ;;
+ *)
+ if test "$GXX" = yes; then
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+ else
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+ esac
+ ;;
+
+ hpux10*|hpux11*)
+ if test $with_gnu_ld = no; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+ case $host_cpu in
+ hppa*64*|ia64*)
+ ;;
+ *)
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+ ;;
+ esac
+ fi
+ case $host_cpu in
+ hppa*64*|ia64*)
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+ *)
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH,
+ # but as the default
+ # location of the library.
+ ;;
+ esac
+
+ case $cc_basename in
+ CC*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ aCC*)
+ case $host_cpu in
+ hppa*64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ ia64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ esac
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ #
+ # There doesn't appear to be a way to prevent this compiler from
+ # explicitly linking system object files so we need to strip them
+ # from the output so that they don't get included in the library
+ # dependencies.
+ output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed'
+ ;;
+ *)
+ if test "$GXX" = yes; then
+ if test $with_gnu_ld = no; then
+ case $host_cpu in
+ hppa*64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ ia64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ esac
+ fi
+ else
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+ esac
+ ;;
+
+ interix[[3-9]]*)
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+ # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+ # Instead, shared libraries are loaded at an image base (0x10000000 by
+ # default) and relocated if they conflict, which is a slow very memory
+ # consuming and fragmenting process. To avoid this, we pick a random,
+ # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+ # time. Moving up from 0x10000000 also allows more sbrk(2) space.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ ;;
+ irix5* | irix6*)
+ case $cc_basename in
+ CC*)
+ # SGI C++
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+
+ # Archives containing C++ object files must be created using
+ # "CC -ar", where "CC" is the IRIX C++ compiler. This is
+ # necessary to make sure instantiated templates are included
+ # in the archive.
+ _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs'
+ ;;
+ *)
+ if test "$GXX" = yes; then
+ if test "$with_gnu_ld" = no; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` -o $lib'
+ fi
+ fi
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ ;;
+ esac
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ _LT_TAGVAR(inherit_rpath, $1)=yes
+ ;;
+
+ linux* | k*bsd*-gnu)
+ case $cc_basename in
+ KCC*)
+ # Kuck and Associates, Inc. (KAI) C++ Compiler
+
+ # KCC will only create a shared library if the output file
+ # ends with ".so" (or ".sl" for HP-UX), so rename the library
+ # to its proper name (with version) after linking.
+ _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib'
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ #
+ # There doesn't appear to be a way to prevent this compiler from
+ # explicitly linking system object files so we need to strip them
+ # from the output so that they don't get included in the library
+ # dependencies.
+ output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed'
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+
+ # Archives containing C++ object files must be created using
+ # "CC -Bstatic", where "CC" is the KAI C++ compiler.
+ _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs'
+ ;;
+ icpc* | ecpc* )
+ # Intel C++
+ with_gnu_ld=yes
+ # version 8.0 and above of icpc choke on multiply defined symbols
+ # if we add $predep_objects and $postdep_objects, however 7.1 and
+ # earlier do not add the objects themselves.
+ case `$CC -V 2>&1` in
+ *"Version 7."*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ ;;
+ *) # Version 8.0 or newer
+ tmp_idyn=
+ case $host_cpu in
+ ia64*) tmp_idyn=' -i_dynamic';;
+ esac
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ ;;
+ esac
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive'
+ ;;
+ pgCC* | pgcpp*)
+ # Portland Group C++ compiler
+ case `$CC -V` in
+ *pgCC\ [[1-5]]* | *pgcpp\ [[1-5]]*)
+ _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~
+ compile_command="$compile_command `find $tpldir -name \*.o | $NL2SP`"'
+ _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~
+ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | $NL2SP`~
+ $RANLIB $oldlib'
+ _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
+ ;;
+ *) # Version 6 will use weak symbols
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
+ ;;
+ esac
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+ ;;
+ cxx*)
+ # Compaq C++
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib ${wl}-retain-symbols-file $wl$export_symbols'
+
+ runpath_var=LD_RUN_PATH
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ #
+ # There doesn't appear to be a way to prevent this compiler from
+ # explicitly linking system object files so we need to strip them
+ # from the output so that they don't get included in the library
+ # dependencies.
+ output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`$ECHO "X$templist" | $Xsed -e "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed'
+ ;;
+ xl*)
+ # IBM XL 8.0 on PPC, with GNU ld
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ if test "x$supports_anon_versioning" = xyes; then
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
+ fi
+ ;;
+ *)
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*)
+ # Sun C++ 5.9
+ _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file ${wl}$export_symbols'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+ _LT_TAGVAR(compiler_needs_object, $1)=yes
+
+ # Not sure whether something based on
+ # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1
+ # would be better.
+ output_verbose_link_cmd='echo'
+
+ # Archives containing C++ object files must be created using
+ # "CC -xar", where "CC" is the Sun C++ compiler. This is
+ # necessary to make sure instantiated templates are included
+ # in the archive.
+ _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs'
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+
+ lynxos*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+
+ m88k*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+
+ mvs*)
+ case $cc_basename in
+ cxx*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ *)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+ ;;
+
+ netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags'
+ wlarc=
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ fi
+ # Workaround some broken pre-1.5 toolchains
+ output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"'
+ ;;
+
+ *nto* | *qnx*)
+ _LT_TAGVAR(ld_shlibs, $1)=yes
+ ;;
+
+ openbsd2*)
+ # C++ shared libraries are fairly broken
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+
+ openbsd*)
+ if test -f /usr/libexec/ld.so; then
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+ if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+ _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+ fi
+ output_verbose_link_cmd=echo
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ osf3* | osf4* | osf5*)
+ case $cc_basename in
+ KCC*)
+ # Kuck and Associates, Inc. (KAI) C++ Compiler
+
+ # KCC will only create a shared library if the output file
+ # ends with ".so" (or ".sl" for HP-UX), so rename the library
+ # to its proper name (with version) after linking.
+ _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+ # Archives containing C++ object files must be created using
+ # the KAI C++ compiler.
+ case $host in
+ osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;;
+ *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;;
+ esac
+ ;;
+ RCC*)
+ # Rational C++ 2.4.1
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ cxx*)
+ case $host in
+ osf3*)
+ _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && $ECHO "X${wl}-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ ;;
+ *)
+ _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~
+ echo "-hidden">> $lib.exp~
+ $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname ${wl}-input ${wl}$lib.exp `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib~
+ $RM $lib.exp'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
+ ;;
+ esac
+
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ #
+ # There doesn't appear to be a way to prevent this compiler from
+ # explicitly linking system object files so we need to strip them
+ # from the output so that they don't get included in the library
+ # dependencies.
+ output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`$ECHO "X$templist" | $Xsed -e "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed'
+ ;;
+ *)
+ if test "$GXX" = yes && test "$with_gnu_ld" = no; then
+ _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
+ case $host in
+ osf3*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ ;;
+ esac
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"'
+
+ else
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+ esac
+ ;;
+
+ psos*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+
+ sunos4*)
+ case $cc_basename in
+ CC*)
+ # Sun C++ 4.x
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ lcc*)
+ # Lucid
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ *)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+ ;;
+
+ solaris*)
+ case $cc_basename in
+ CC*)
+ # Sun C++ 4.2, 5.x and Centerline C++
+ _LT_TAGVAR(archive_cmds_need_lc,$1)=yes
+ _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ case $host_os in
+ solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
+ *)
+ # The compiler driver will combine and reorder linker options,
+ # but understands `-z linker_flag'.
+ # Supported since Solaris 2.6 (maybe 2.5.1?)
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract'
+ ;;
+ esac
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+
+ output_verbose_link_cmd='echo'
+
+ # Archives containing C++ object files must be created using
+ # "CC -xar", where "CC" is the Sun C++ compiler. This is
+ # necessary to make sure instantiated templates are included
+ # in the archive.
+ _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs'
+ ;;
+ gcx*)
+ # Green Hills C++ Compiler
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
+
+ # The C++ compiler must be used to create the archive.
+ _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs'
+ ;;
+ *)
+ # GNU C++ compiler with Solaris linker
+ if test "$GXX" = yes && test "$with_gnu_ld" = no; then
+ _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-z ${wl}defs'
+ if $CC --version | $GREP -v '^2\.7' > /dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -shared -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"'
+ else
+ # g++ 2.7 appears to require `-G' NOT `-shared' on this
+ # platform.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"'
+ fi
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $wl$libdir'
+ case $host_os in
+ solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
+ *)
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract'
+ ;;
+ esac
+ fi
+ ;;
+ esac
+ ;;
+
+ sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*)
+ _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text'
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ runpath_var='LD_RUN_PATH'
+
+ case $cc_basename in
+ CC*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ ;;
+
+ sysv5* | sco3.2v5* | sco5v6*)
+ # Note: We can NOT use -z defs as we might desire, because we do not
+ # link with -lc, and that would cause any symbols used from libc to
+ # always be unresolved, which means just about no library would
+ # ever link correctly. If we're not using GNU ld we use -z text
+ # though, which does catch some bad symbols but isn't as heavy-handed
+ # as -z defs.
+ _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text'
+ _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs'
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport'
+ runpath_var='LD_RUN_PATH'
+
+ case $cc_basename in
+ CC*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ ;;
+
+ tandem*)
+ case $cc_basename in
+ NCC*)
+ # NonStop-UX NCC 3.20
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ *)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+ ;;
+
+ vxworks*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+
+ *)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+
+ AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)])
+ test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no
+
+ _LT_TAGVAR(GCC, $1)="$GXX"
+ _LT_TAGVAR(LD, $1)="$LD"
+
+ ## CAVEAT EMPTOR:
+ ## There is no encapsulation within the following macros, do not change
+ ## the running order or otherwise move them around unless you know exactly
+ ## what you are doing...
+ _LT_SYS_HIDDEN_LIBDEPS($1)
+ _LT_COMPILER_PIC($1)
+ _LT_COMPILER_C_O($1)
+ _LT_COMPILER_FILE_LOCKS($1)
+ _LT_LINKER_SHLIBS($1)
+ _LT_SYS_DYNAMIC_LINKER($1)
+ _LT_LINKER_HARDCODE_LIBPATH($1)
+
+ _LT_CONFIG($1)
+ fi # test -n "$compiler"
+
+ CC=$lt_save_CC
+ LDCXX=$LD
+ LD=$lt_save_LD
+ GCC=$lt_save_GCC
+ with_gnu_ld=$lt_save_with_gnu_ld
+ lt_cv_path_LDCXX=$lt_cv_path_LD
+ lt_cv_path_LD=$lt_save_path_LD
+ lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld
+ lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld
+fi # test "$_lt_caught_CXX_error" != yes
+
+AC_LANG_POP
+])# _LT_LANG_CXX_CONFIG
+
+
+# _LT_SYS_HIDDEN_LIBDEPS([TAGNAME])
+# ---------------------------------
+# Figure out "hidden" library dependencies from verbose
+# compiler output when linking a shared library.
+# Parse the compiler output and extract the necessary
+# objects, libraries and library flags.
+m4_defun([_LT_SYS_HIDDEN_LIBDEPS],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+# Dependencies to place before and after the object being linked:
+_LT_TAGVAR(predep_objects, $1)=
+_LT_TAGVAR(postdep_objects, $1)=
+_LT_TAGVAR(predeps, $1)=
+_LT_TAGVAR(postdeps, $1)=
+_LT_TAGVAR(compiler_lib_search_path, $1)=
+
+dnl we can't use the lt_simple_compile_test_code here,
+dnl because it contains code intended for an executable,
+dnl not a library. It's possible we should let each
+dnl tag define a new lt_????_link_test_code variable,
+dnl but it's only used here...
+m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF
+int a;
+void foo (void) { a = 0; }
+_LT_EOF
+], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF
+class Foo
+{
+public:
+ Foo (void) { a = 0; }
+private:
+ int a;
+};
+_LT_EOF
+], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF
+ subroutine foo
+ implicit none
+ integer*4 a
+ a=0
+ return
+ end
+_LT_EOF
+], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF
+ subroutine foo
+ implicit none
+ integer a
+ a=0
+ return
+ end
+_LT_EOF
+], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF
+public class foo {
+ private int a;
+ public void bar (void) {
+ a = 0;
+ }
+};
+_LT_EOF
+])
+dnl Parse the compiler output and extract the necessary
+dnl objects, libraries and library flags.
+if AC_TRY_EVAL(ac_compile); then
+ # Parse the compiler output and extract the necessary
+ # objects, libraries and library flags.
+
+ # Sentinel used to keep track of whether or not we are before
+ # the conftest object file.
+ pre_test_object_deps_done=no
+
+ for p in `eval "$output_verbose_link_cmd"`; do
+ case $p in
+
+ -L* | -R* | -l*)
+ # Some compilers place space between "-{L,R}" and the path.
+ # Remove the space.
+ if test $p = "-L" ||
+ test $p = "-R"; then
+ prev=$p
+ continue
+ else
+ prev=
+ fi
+
+ if test "$pre_test_object_deps_done" = no; then
+ case $p in
+ -L* | -R*)
+ # Internal compiler library paths should come after those
+ # provided the user. The postdeps already come after the
+ # user supplied libs so there is no need to process them.
+ if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then
+ _LT_TAGVAR(compiler_lib_search_path, $1)="${prev}${p}"
+ else
+ _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} ${prev}${p}"
+ fi
+ ;;
+ # The "-l" case would never come before the object being
+ # linked, so don't bother handling this case.
+ esac
+ else
+ if test -z "$_LT_TAGVAR(postdeps, $1)"; then
+ _LT_TAGVAR(postdeps, $1)="${prev}${p}"
+ else
+ _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} ${prev}${p}"
+ fi
+ fi
+ ;;
+
+ *.$objext)
+ # This assumes that the test object file only shows up
+ # once in the compiler output.
+ if test "$p" = "conftest.$objext"; then
+ pre_test_object_deps_done=yes
+ continue
+ fi
+
+ if test "$pre_test_object_deps_done" = no; then
+ if test -z "$_LT_TAGVAR(predep_objects, $1)"; then
+ _LT_TAGVAR(predep_objects, $1)="$p"
+ else
+ _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p"
+ fi
+ else
+ if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then
+ _LT_TAGVAR(postdep_objects, $1)="$p"
+ else
+ _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p"
+ fi
+ fi
+ ;;
+
+ *) ;; # Ignore the rest.
+
+ esac
+ done
+
+ # Clean up.
+ rm -f a.out a.exe
+else
+ echo "libtool.m4: error: problem compiling $1 test program"
+fi
+
+$RM -f confest.$objext
+
+# PORTME: override above test on systems where it is broken
+m4_if([$1], [CXX],
+[case $host_os in
+interix[[3-9]]*)
+ # Interix 3.5 installs completely hosed .la files for C++, so rather than
+ # hack all around it, let's just trust "g++" to DTRT.
+ _LT_TAGVAR(predep_objects,$1)=
+ _LT_TAGVAR(postdep_objects,$1)=
+ _LT_TAGVAR(postdeps,$1)=
+ ;;
+
+linux*)
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*)
+ # Sun C++ 5.9
+
+ # The more standards-conforming stlport4 library is
+ # incompatible with the Cstd library. Avoid specifying
+ # it if it's in CXXFLAGS. Ignore libCrun as
+ # -library=stlport4 depends on it.
+ case " $CXX $CXXFLAGS " in
+ *" -library=stlport4 "*)
+ solaris_use_stlport4=yes
+ ;;
+ esac
+
+ if test "$solaris_use_stlport4" != yes; then
+ _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun'
+ fi
+ ;;
+ esac
+ ;;
+
+solaris*)
+ case $cc_basename in
+ CC*)
+ # The more standards-conforming stlport4 library is
+ # incompatible with the Cstd library. Avoid specifying
+ # it if it's in CXXFLAGS. Ignore libCrun as
+ # -library=stlport4 depends on it.
+ case " $CXX $CXXFLAGS " in
+ *" -library=stlport4 "*)
+ solaris_use_stlport4=yes
+ ;;
+ esac
+
+ # Adding this requires a known-good setup of shared libraries for
+ # Sun compiler versions before 5.6, else PIC objects from an old
+ # archive will be linked into the output, leading to subtle bugs.
+ if test "$solaris_use_stlport4" != yes; then
+ _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun'
+ fi
+ ;;
+ esac
+ ;;
+esac
+])
+
+case " $_LT_TAGVAR(postdeps, $1) " in
+*" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;;
+esac
+ _LT_TAGVAR(compiler_lib_search_dirs, $1)=
+if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then
+ _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | ${SED} -e 's! -L! !g' -e 's!^ !!'`
+fi
+_LT_TAGDECL([], [compiler_lib_search_dirs], [1],
+ [The directories searched by this compiler when creating a shared library])
+_LT_TAGDECL([], [predep_objects], [1],
+ [Dependencies to place before and after the objects being linked to
+ create a shared library])
+_LT_TAGDECL([], [postdep_objects], [1])
+_LT_TAGDECL([], [predeps], [1])
+_LT_TAGDECL([], [postdeps], [1])
+_LT_TAGDECL([], [compiler_lib_search_path], [1],
+ [The library search path used internally by the compiler when linking
+ a shared library])
+])# _LT_SYS_HIDDEN_LIBDEPS
+
+
+# _LT_PROG_F77
+# ------------
+# Since AC_PROG_F77 is broken, in that it returns the empty string
+# if there is no fortran compiler, we have our own version here.
+m4_defun([_LT_PROG_F77],
+[
+pushdef([AC_MSG_ERROR], [_lt_disable_F77=yes])
+AC_PROG_F77
+if test -z "$F77" || test "X$F77" = "Xno"; then
+ _lt_disable_F77=yes
+fi
+popdef([AC_MSG_ERROR])
+])# _LT_PROG_F77
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([_LT_PROG_F77], [])
+
+
+# _LT_LANG_F77_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for a Fortran 77 compiler are
+# suitably defined. These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_F77_CONFIG],
+[AC_REQUIRE([_LT_PROG_F77])dnl
+AC_LANG_PUSH(Fortran 77)
+
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+_LT_TAGVAR(allow_undefined_flag, $1)=
+_LT_TAGVAR(always_export_symbols, $1)=no
+_LT_TAGVAR(archive_expsym_cmds, $1)=
+_LT_TAGVAR(export_dynamic_flag_spec, $1)=
+_LT_TAGVAR(hardcode_direct, $1)=no
+_LT_TAGVAR(hardcode_direct_absolute, $1)=no
+_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+_LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)=
+_LT_TAGVAR(hardcode_libdir_separator, $1)=
+_LT_TAGVAR(hardcode_minus_L, $1)=no
+_LT_TAGVAR(hardcode_automatic, $1)=no
+_LT_TAGVAR(inherit_rpath, $1)=no
+_LT_TAGVAR(module_cmds, $1)=
+_LT_TAGVAR(module_expsym_cmds, $1)=
+_LT_TAGVAR(link_all_deplibs, $1)=unknown
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(no_undefined_flag, $1)=
+_LT_TAGVAR(whole_archive_flag_spec, $1)=
+_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+
+# Source file extension for f77 test sources.
+ac_ext=f
+
+# Object file extension for compiled f77 test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# No sense in running all these tests if we already determined that
+# the F77 compiler isn't working. Some variables (like enable_shared)
+# are currently assumed to apply to all compilers on this platform,
+# and will be corrupted by setting them based on a non-working compiler.
+if test "$_lt_disable_F77" != yes; then
+ # Code to be used in simple compile tests
+ lt_simple_compile_test_code="\
+ subroutine t
+ return
+ end
+"
+
+ # Code to be used in simple link tests
+ lt_simple_link_test_code="\
+ program t
+ end
+"
+
+ # ltmain only uses $CC for tagged configurations so make sure $CC is set.
+ _LT_TAG_COMPILER
+
+ # save warnings/boilerplate of simple test code
+ _LT_COMPILER_BOILERPLATE
+ _LT_LINKER_BOILERPLATE
+
+ # Allow CC to be a program name with arguments.
+ lt_save_CC="$CC"
+ lt_save_GCC=$GCC
+ CC=${F77-"f77"}
+ compiler=$CC
+ _LT_TAGVAR(compiler, $1)=$CC
+ _LT_CC_BASENAME([$compiler])
+ GCC=$G77
+ if test -n "$compiler"; then
+ AC_MSG_CHECKING([if libtool supports shared libraries])
+ AC_MSG_RESULT([$can_build_shared])
+
+ AC_MSG_CHECKING([whether to build shared libraries])
+ test "$can_build_shared" = "no" && enable_shared=no
+
+ # On AIX, shared libraries and static libraries use the same namespace, and
+ # are all built from PIC.
+ case $host_os in
+ aix3*)
+ test "$enable_shared" = yes && enable_static=no
+ if test -n "$RANLIB"; then
+ archive_cmds="$archive_cmds~\$RANLIB \$lib"
+ postinstall_cmds='$RANLIB $lib'
+ fi
+ ;;
+ aix[[4-9]]*)
+ if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
+ test "$enable_shared" = yes && enable_static=no
+ fi
+ ;;
+ esac
+ AC_MSG_RESULT([$enable_shared])
+
+ AC_MSG_CHECKING([whether to build static libraries])
+ # Make sure either enable_shared or enable_static is yes.
+ test "$enable_shared" = yes || enable_static=yes
+ AC_MSG_RESULT([$enable_static])
+
+ _LT_TAGVAR(GCC, $1)="$G77"
+ _LT_TAGVAR(LD, $1)="$LD"
+
+ ## CAVEAT EMPTOR:
+ ## There is no encapsulation within the following macros, do not change
+ ## the running order or otherwise move them around unless you know exactly
+ ## what you are doing...
+ _LT_COMPILER_PIC($1)
+ _LT_COMPILER_C_O($1)
+ _LT_COMPILER_FILE_LOCKS($1)
+ _LT_LINKER_SHLIBS($1)
+ _LT_SYS_DYNAMIC_LINKER($1)
+ _LT_LINKER_HARDCODE_LIBPATH($1)
+
+ _LT_CONFIG($1)
+ fi # test -n "$compiler"
+
+ GCC=$lt_save_GCC
+ CC="$lt_save_CC"
+fi # test "$_lt_disable_F77" != yes
+
+AC_LANG_POP
+])# _LT_LANG_F77_CONFIG
+
+
+# _LT_PROG_FC
+# -----------
+# Since AC_PROG_FC is broken, in that it returns the empty string
+# if there is no fortran compiler, we have our own version here.
+m4_defun([_LT_PROG_FC],
+[
+pushdef([AC_MSG_ERROR], [_lt_disable_FC=yes])
+AC_PROG_FC
+if test -z "$FC" || test "X$FC" = "Xno"; then
+ _lt_disable_FC=yes
+fi
+popdef([AC_MSG_ERROR])
+])# _LT_PROG_FC
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([_LT_PROG_FC], [])
+
+
+# _LT_LANG_FC_CONFIG([TAG])
+# -------------------------
+# Ensure that the configuration variables for a Fortran compiler are
+# suitably defined. These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_FC_CONFIG],
+[AC_REQUIRE([_LT_PROG_FC])dnl
+AC_LANG_PUSH(Fortran)
+
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+_LT_TAGVAR(allow_undefined_flag, $1)=
+_LT_TAGVAR(always_export_symbols, $1)=no
+_LT_TAGVAR(archive_expsym_cmds, $1)=
+_LT_TAGVAR(export_dynamic_flag_spec, $1)=
+_LT_TAGVAR(hardcode_direct, $1)=no
+_LT_TAGVAR(hardcode_direct_absolute, $1)=no
+_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+_LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)=
+_LT_TAGVAR(hardcode_libdir_separator, $1)=
+_LT_TAGVAR(hardcode_minus_L, $1)=no
+_LT_TAGVAR(hardcode_automatic, $1)=no
+_LT_TAGVAR(inherit_rpath, $1)=no
+_LT_TAGVAR(module_cmds, $1)=
+_LT_TAGVAR(module_expsym_cmds, $1)=
+_LT_TAGVAR(link_all_deplibs, $1)=unknown
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(no_undefined_flag, $1)=
+_LT_TAGVAR(whole_archive_flag_spec, $1)=
+_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+
+# Source file extension for fc test sources.
+ac_ext=${ac_fc_srcext-f}
+
+# Object file extension for compiled fc test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# No sense in running all these tests if we already determined that
+# the FC compiler isn't working. Some variables (like enable_shared)
+# are currently assumed to apply to all compilers on this platform,
+# and will be corrupted by setting them based on a non-working compiler.
+if test "$_lt_disable_FC" != yes; then
+ # Code to be used in simple compile tests
+ lt_simple_compile_test_code="\
+ subroutine t
+ return
+ end
+"
+
+ # Code to be used in simple link tests
+ lt_simple_link_test_code="\
+ program t
+ end
+"
+
+ # ltmain only uses $CC for tagged configurations so make sure $CC is set.
+ _LT_TAG_COMPILER
+
+ # save warnings/boilerplate of simple test code
+ _LT_COMPILER_BOILERPLATE
+ _LT_LINKER_BOILERPLATE
+
+ # Allow CC to be a program name with arguments.
+ lt_save_CC="$CC"
+ lt_save_GCC=$GCC
+ CC=${FC-"f95"}
+ compiler=$CC
+ GCC=$ac_cv_fc_compiler_gnu
+
+ _LT_TAGVAR(compiler, $1)=$CC
+ _LT_CC_BASENAME([$compiler])
+
+ if test -n "$compiler"; then
+ AC_MSG_CHECKING([if libtool supports shared libraries])
+ AC_MSG_RESULT([$can_build_shared])
+
+ AC_MSG_CHECKING([whether to build shared libraries])
+ test "$can_build_shared" = "no" && enable_shared=no
+
+ # On AIX, shared libraries and static libraries use the same namespace, and
+ # are all built from PIC.
+ case $host_os in
+ aix3*)
+ test "$enable_shared" = yes && enable_static=no
+ if test -n "$RANLIB"; then
+ archive_cmds="$archive_cmds~\$RANLIB \$lib"
+ postinstall_cmds='$RANLIB $lib'
+ fi
+ ;;
+ aix[[4-9]]*)
+ if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
+ test "$enable_shared" = yes && enable_static=no
+ fi
+ ;;
+ esac
+ AC_MSG_RESULT([$enable_shared])
+
+ AC_MSG_CHECKING([whether to build static libraries])
+ # Make sure either enable_shared or enable_static is yes.
+ test "$enable_shared" = yes || enable_static=yes
+ AC_MSG_RESULT([$enable_static])
+
+ _LT_TAGVAR(GCC, $1)="$ac_cv_fc_compiler_gnu"
+ _LT_TAGVAR(LD, $1)="$LD"
+
+ ## CAVEAT EMPTOR:
+ ## There is no encapsulation within the following macros, do not change
+ ## the running order or otherwise move them around unless you know exactly
+ ## what you are doing...
+ _LT_SYS_HIDDEN_LIBDEPS($1)
+ _LT_COMPILER_PIC($1)
+ _LT_COMPILER_C_O($1)
+ _LT_COMPILER_FILE_LOCKS($1)
+ _LT_LINKER_SHLIBS($1)
+ _LT_SYS_DYNAMIC_LINKER($1)
+ _LT_LINKER_HARDCODE_LIBPATH($1)
+
+ _LT_CONFIG($1)
+ fi # test -n "$compiler"
+
+ GCC=$lt_save_GCC
+ CC="$lt_save_CC"
+fi # test "$_lt_disable_FC" != yes
+
+AC_LANG_POP
+])# _LT_LANG_FC_CONFIG
+
+
+# _LT_LANG_GCJ_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for the GNU Java Compiler compiler
+# are suitably defined. These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_GCJ_CONFIG],
+[AC_REQUIRE([LT_PROG_GCJ])dnl
+AC_LANG_SAVE
+
+# Source file extension for Java test sources.
+ac_ext=java
+
+# Object file extension for compiled Java test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="class foo {}"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }'
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+_LT_TAG_COMPILER
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+# Allow CC to be a program name with arguments.
+lt_save_CC="$CC"
+lt_save_GCC=$GCC
+GCC=yes
+CC=${GCJ-"gcj"}
+compiler=$CC
+_LT_TAGVAR(compiler, $1)=$CC
+_LT_TAGVAR(LD, $1)="$LD"
+_LT_CC_BASENAME([$compiler])
+
+# GCJ did not exist at the time GCC didn't implicitly link libc in.
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+
+if test -n "$compiler"; then
+ _LT_COMPILER_NO_RTTI($1)
+ _LT_COMPILER_PIC($1)
+ _LT_COMPILER_C_O($1)
+ _LT_COMPILER_FILE_LOCKS($1)
+ _LT_LINKER_SHLIBS($1)
+ _LT_LINKER_HARDCODE_LIBPATH($1)
+
+ _LT_CONFIG($1)
+fi
+
+AC_LANG_RESTORE
+
+GCC=$lt_save_GCC
+CC="$lt_save_CC"
+])# _LT_LANG_GCJ_CONFIG
+
+
+# _LT_LANG_RC_CONFIG([TAG])
+# -------------------------
+# Ensure that the configuration variables for the Windows resource compiler
+# are suitably defined. These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_RC_CONFIG],
+[AC_REQUIRE([LT_PROG_RC])dnl
+AC_LANG_SAVE
+
+# Source file extension for RC test sources.
+ac_ext=rc
+
+# Object file extension for compiled RC test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }'
+
+# Code to be used in simple link tests
+lt_simple_link_test_code="$lt_simple_compile_test_code"
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+_LT_TAG_COMPILER
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+# Allow CC to be a program name with arguments.
+lt_save_CC="$CC"
+lt_save_GCC=$GCC
+GCC=
+CC=${RC-"windres"}
+compiler=$CC
+_LT_TAGVAR(compiler, $1)=$CC
+_LT_CC_BASENAME([$compiler])
+_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes
+
+if test -n "$compiler"; then
+ :
+ _LT_CONFIG($1)
+fi
+
+GCC=$lt_save_GCC
+AC_LANG_RESTORE
+CC="$lt_save_CC"
+])# _LT_LANG_RC_CONFIG
+
+
+# LT_PROG_GCJ
+# -----------
+AC_DEFUN([LT_PROG_GCJ],
+[m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ],
+ [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ],
+ [AC_CHECK_TOOL(GCJ, gcj,)
+ test "x${GCJFLAGS+set}" = xset || GCJFLAGS="-g -O2"
+ AC_SUBST(GCJFLAGS)])])[]dnl
+])
+
+# Old name:
+AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([LT_AC_PROG_GCJ], [])
+
+
+# LT_PROG_RC
+# ----------
+AC_DEFUN([LT_PROG_RC],
+[AC_CHECK_TOOL(RC, windres,)
+])
+
+# Old name:
+AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([LT_AC_PROG_RC], [])
+
+
+# _LT_DECL_EGREP
+# --------------
+# If we don't have a new enough Autoconf to choose the best grep
+# available, choose the one first in the user's PATH.
+m4_defun([_LT_DECL_EGREP],
+[AC_REQUIRE([AC_PROG_EGREP])dnl
+AC_REQUIRE([AC_PROG_FGREP])dnl
+test -z "$GREP" && GREP=grep
+_LT_DECL([], [GREP], [1], [A grep program that handles long lines])
+_LT_DECL([], [EGREP], [1], [An ERE matcher])
+_LT_DECL([], [FGREP], [1], [A literal string matcher])
+dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too
+AC_SUBST([GREP])
+])
+
+
+# _LT_DECL_OBJDUMP
+# --------------
+# If we don't have a new enough Autoconf to choose the best objdump
+# available, choose the one first in the user's PATH.
+m4_defun([_LT_DECL_OBJDUMP],
+[AC_CHECK_TOOL(OBJDUMP, objdump, false)
+test -z "$OBJDUMP" && OBJDUMP=objdump
+_LT_DECL([], [OBJDUMP], [1], [An object symbol dumper])
+AC_SUBST([OBJDUMP])
+])
+
+
+# _LT_DECL_SED
+# ------------
+# Check for a fully-functional sed program, that truncates
+# as few characters as possible. Prefer GNU sed if found.
+m4_defun([_LT_DECL_SED],
+[AC_PROG_SED
+test -z "$SED" && SED=sed
+Xsed="$SED -e 1s/^X//"
+_LT_DECL([], [SED], [1], [A sed program that does not truncate output])
+_LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"],
+ [Sed that helps us avoid accidentally triggering echo(1) options like -n])
+])# _LT_DECL_SED
+
+m4_ifndef([AC_PROG_SED], [
+# NOTE: This macro has been submitted for inclusion into #
+# GNU Autoconf as AC_PROG_SED. When it is available in #
+# a released version of Autoconf we should remove this #
+# macro and use it instead. #
+
+m4_defun([AC_PROG_SED],
+[AC_MSG_CHECKING([for a sed that does not truncate output])
+AC_CACHE_VAL(lt_cv_path_SED,
+[# Loop through the user's path and test for sed and gsed.
+# Then use that list of sed's as ones to test for truncation.
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for lt_ac_prog in sed gsed; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then
+ lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext"
+ fi
+ done
+ done
+done
+IFS=$as_save_IFS
+lt_ac_max=0
+lt_ac_count=0
+# Add /usr/xpg4/bin/sed as it is typically found on Solaris
+# along with /bin/sed that truncates output.
+for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do
+ test ! -f $lt_ac_sed && continue
+ cat /dev/null > conftest.in
+ lt_ac_count=0
+ echo $ECHO_N "0123456789$ECHO_C" >conftest.in
+ # Check for GNU sed and select it if it is found.
+ if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then
+ lt_cv_path_SED=$lt_ac_sed
+ break
+ fi
+ while true; do
+ cat conftest.in conftest.in >conftest.tmp
+ mv conftest.tmp conftest.in
+ cp conftest.in conftest.nl
+ echo >>conftest.nl
+ $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break
+ cmp -s conftest.out conftest.nl || break
+ # 10000 chars as input seems more than enough
+ test $lt_ac_count -gt 10 && break
+ lt_ac_count=`expr $lt_ac_count + 1`
+ if test $lt_ac_count -gt $lt_ac_max; then
+ lt_ac_max=$lt_ac_count
+ lt_cv_path_SED=$lt_ac_sed
+ fi
+ done
+done
+])
+SED=$lt_cv_path_SED
+AC_SUBST([SED])
+AC_MSG_RESULT([$SED])
+])#AC_PROG_SED
+])#m4_ifndef
+
+# Old name:
+AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([LT_AC_PROG_SED], [])
+
+
+# _LT_CHECK_SHELL_FEATURES
+# ------------------------
+# Find out whether the shell is Bourne or XSI compatible,
+# or has some other useful features.
+m4_defun([_LT_CHECK_SHELL_FEATURES],
+[AC_MSG_CHECKING([whether the shell understands some XSI constructs])
+# Try some XSI features
+xsi_shell=no
+( _lt_dummy="a/b/c"
+ test "${_lt_dummy##*/},${_lt_dummy%/*},"${_lt_dummy%"$_lt_dummy"}, \
+ = c,a/b,, \
+ && eval 'test $(( 1 + 1 )) -eq 2 \
+ && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \
+ && xsi_shell=yes
+AC_MSG_RESULT([$xsi_shell])
+_LT_CONFIG_LIBTOOL_INIT([xsi_shell='$xsi_shell'])
+
+AC_MSG_CHECKING([whether the shell understands "+="])
+lt_shell_append=no
+( foo=bar; set foo baz; eval "$[1]+=\$[2]" && test "$foo" = barbaz ) \
+ >/dev/null 2>&1 \
+ && lt_shell_append=yes
+AC_MSG_RESULT([$lt_shell_append])
+_LT_CONFIG_LIBTOOL_INIT([lt_shell_append='$lt_shell_append'])
+
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ lt_unset=unset
+else
+ lt_unset=false
+fi
+_LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl
+
+# test EBCDIC or ASCII
+case `echo X|tr X '\101'` in
+ A) # ASCII based system
+ # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr
+ lt_SP2NL='tr \040 \012'
+ lt_NL2SP='tr \015\012 \040\040'
+ ;;
+ *) # EBCDIC based system
+ lt_SP2NL='tr \100 \n'
+ lt_NL2SP='tr \r\n \100\100'
+ ;;
+esac
+_LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl
+_LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl
+])# _LT_CHECK_SHELL_FEATURES
+
+
+# _LT_PROG_XSI_SHELLFNS
+# ---------------------
+# Bourne and XSI compatible variants of some useful shell functions.
+m4_defun([_LT_PROG_XSI_SHELLFNS],
+[case $xsi_shell in
+ yes)
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_dirname file append nondir_replacement
+# Compute the dirname of FILE. If nonempty, add APPEND to the result,
+# otherwise set result to NONDIR_REPLACEMENT.
+func_dirname ()
+{
+ case ${1} in
+ */*) func_dirname_result="${1%/*}${2}" ;;
+ * ) func_dirname_result="${3}" ;;
+ esac
+}
+
+# func_basename file
+func_basename ()
+{
+ func_basename_result="${1##*/}"
+}
+
+# func_dirname_and_basename file append nondir_replacement
+# perform func_basename and func_dirname in a single function
+# call:
+# dirname: Compute the dirname of FILE. If nonempty,
+# add APPEND to the result, otherwise set result
+# to NONDIR_REPLACEMENT.
+# value returned in "$func_dirname_result"
+# basename: Compute filename of FILE.
+# value retuned in "$func_basename_result"
+# Implementation must be kept synchronized with func_dirname
+# and func_basename. For efficiency, we do not delegate to
+# those functions but instead duplicate the functionality here.
+func_dirname_and_basename ()
+{
+ case ${1} in
+ */*) func_dirname_result="${1%/*}${2}" ;;
+ * ) func_dirname_result="${3}" ;;
+ esac
+ func_basename_result="${1##*/}"
+}
+
+# func_stripname prefix suffix name
+# strip PREFIX and SUFFIX off of NAME.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+func_stripname ()
+{
+ # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are
+ # positional parameters, so assign one to ordinary parameter first.
+ func_stripname_result=${3}
+ func_stripname_result=${func_stripname_result#"${1}"}
+ func_stripname_result=${func_stripname_result%"${2}"}
+}
+
+# func_opt_split
+func_opt_split ()
+{
+ func_opt_split_opt=${1%%=*}
+ func_opt_split_arg=${1#*=}
+}
+
+# func_lo2o object
+func_lo2o ()
+{
+ case ${1} in
+ *.lo) func_lo2o_result=${1%.lo}.${objext} ;;
+ *) func_lo2o_result=${1} ;;
+ esac
+}
+
+# func_xform libobj-or-source
+func_xform ()
+{
+ func_xform_result=${1%.*}.lo
+}
+
+# func_arith arithmetic-term...
+func_arith ()
+{
+ func_arith_result=$(( $[*] ))
+}
+
+# func_len string
+# STRING may not start with a hyphen.
+func_len ()
+{
+ func_len_result=${#1}
+}
+
+_LT_EOF
+ ;;
+ *) # Bourne compatible functions.
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_dirname file append nondir_replacement
+# Compute the dirname of FILE. If nonempty, add APPEND to the result,
+# otherwise set result to NONDIR_REPLACEMENT.
+func_dirname ()
+{
+ # Extract subdirectory from the argument.
+ func_dirname_result=`$ECHO "X${1}" | $Xsed -e "$dirname"`
+ if test "X$func_dirname_result" = "X${1}"; then
+ func_dirname_result="${3}"
+ else
+ func_dirname_result="$func_dirname_result${2}"
+ fi
+}
+
+# func_basename file
+func_basename ()
+{
+ func_basename_result=`$ECHO "X${1}" | $Xsed -e "$basename"`
+}
+
+dnl func_dirname_and_basename
+dnl A portable version of this function is already defined in general.m4sh
+dnl so there is no need for it here.
+
+# func_stripname prefix suffix name
+# strip PREFIX and SUFFIX off of NAME.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+# func_strip_suffix prefix name
+func_stripname ()
+{
+ case ${2} in
+ .*) func_stripname_result=`$ECHO "X${3}" \
+ | $Xsed -e "s%^${1}%%" -e "s%\\\\${2}\$%%"`;;
+ *) func_stripname_result=`$ECHO "X${3}" \
+ | $Xsed -e "s%^${1}%%" -e "s%${2}\$%%"`;;
+ esac
+}
+
+# sed scripts:
+my_sed_long_opt='1s/^\(-[[^=]]*\)=.*/\1/;q'
+my_sed_long_arg='1s/^-[[^=]]*=//'
+
+# func_opt_split
+func_opt_split ()
+{
+ func_opt_split_opt=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_opt"`
+ func_opt_split_arg=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_arg"`
+}
+
+# func_lo2o object
+func_lo2o ()
+{
+ func_lo2o_result=`$ECHO "X${1}" | $Xsed -e "$lo2o"`
+}
+
+# func_xform libobj-or-source
+func_xform ()
+{
+ func_xform_result=`$ECHO "X${1}" | $Xsed -e 's/\.[[^.]]*$/.lo/'`
+}
+
+# func_arith arithmetic-term...
+func_arith ()
+{
+ func_arith_result=`expr "$[@]"`
+}
+
+# func_len string
+# STRING may not start with a hyphen.
+func_len ()
+{
+ func_len_result=`expr "$[1]" : ".*" 2>/dev/null || echo $max_cmd_len`
+}
+
+_LT_EOF
+esac
+
+case $lt_shell_append in
+ yes)
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_append var value
+# Append VALUE to the end of shell variable VAR.
+func_append ()
+{
+ eval "$[1]+=\$[2]"
+}
+_LT_EOF
+ ;;
+ *)
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_append var value
+# Append VALUE to the end of shell variable VAR.
+func_append ()
+{
+ eval "$[1]=\$$[1]\$[2]"
+}
+
+_LT_EOF
+ ;;
+ esac
+])
+
+# Helper functions for option handling. -*- Autoconf -*-
+#
+# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
+# Written by Gary V. Vaughan, 2004
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# serial 6 ltoptions.m4
+
+# This is to help aclocal find these macros, as it can't see m4_define.
+AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])])
+
+
+# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME)
+# ------------------------------------------
+m4_define([_LT_MANGLE_OPTION],
+[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])])
+
+
+# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME)
+# ---------------------------------------
+# Set option OPTION-NAME for macro MACRO-NAME, and if there is a
+# matching handler defined, dispatch to it. Other OPTION-NAMEs are
+# saved as a flag.
+m4_define([_LT_SET_OPTION],
+[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl
+m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]),
+ _LT_MANGLE_DEFUN([$1], [$2]),
+ [m4_warning([Unknown $1 option `$2'])])[]dnl
+])
+
+
+# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET])
+# ------------------------------------------------------------
+# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
+m4_define([_LT_IF_OPTION],
+[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])])
+
+
+# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET)
+# -------------------------------------------------------
+# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME
+# are set.
+m4_define([_LT_UNLESS_OPTIONS],
+[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
+ [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option),
+ [m4_define([$0_found])])])[]dnl
+m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3
+])[]dnl
+])
+
+
+# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST)
+# ----------------------------------------
+# OPTION-LIST is a space-separated list of Libtool options associated
+# with MACRO-NAME. If any OPTION has a matching handler declared with
+# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about
+# the unknown option and exit.
+m4_defun([_LT_SET_OPTIONS],
+[# Set options
+m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
+ [_LT_SET_OPTION([$1], _LT_Option)])
+
+m4_if([$1],[LT_INIT],[
+ dnl
+ dnl Simply set some default values (i.e off) if boolean options were not
+ dnl specified:
+ _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no
+ ])
+ _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no
+ ])
+ dnl
+ dnl If no reference was made to various pairs of opposing options, then
+ dnl we run the default mode handler for the pair. For example, if neither
+ dnl `shared' nor `disable-shared' was passed, we enable building of shared
+ dnl archives by default:
+ _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED])
+ _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC])
+ _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC])
+ _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install],
+ [_LT_ENABLE_FAST_INSTALL])
+ ])
+])# _LT_SET_OPTIONS
+
+
+
+# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME)
+# -----------------------------------------
+m4_define([_LT_MANGLE_DEFUN],
+[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])])
+
+
+# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE)
+# -----------------------------------------------
+m4_define([LT_OPTION_DEFINE],
+[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl
+])# LT_OPTION_DEFINE
+
+
+# dlopen
+# ------
+LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes
+])
+
+AU_DEFUN([AC_LIBTOOL_DLOPEN],
+[_LT_SET_OPTION([LT_INIT], [dlopen])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the `dlopen' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], [])
+
+
+# win32-dll
+# ---------
+# Declare package support for building win32 dll's.
+LT_OPTION_DEFINE([LT_INIT], [win32-dll],
+[enable_win32_dll=yes
+
+case $host in
+*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-cegcc*)
+ AC_CHECK_TOOL(AS, as, false)
+ AC_CHECK_TOOL(DLLTOOL, dlltool, false)
+ AC_CHECK_TOOL(OBJDUMP, objdump, false)
+ ;;
+esac
+
+test -z "$AS" && AS=as
+_LT_DECL([], [AS], [0], [Assembler program])dnl
+
+test -z "$DLLTOOL" && DLLTOOL=dlltool
+_LT_DECL([], [DLLTOOL], [0], [DLL creation program])dnl
+
+test -z "$OBJDUMP" && OBJDUMP=objdump
+_LT_DECL([], [OBJDUMP], [0], [Object dumper program])dnl
+])# win32-dll
+
+AU_DEFUN([AC_LIBTOOL_WIN32_DLL],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+_LT_SET_OPTION([LT_INIT], [win32-dll])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the `win32-dll' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], [])
+
+
+# _LT_ENABLE_SHARED([DEFAULT])
+# ----------------------------
+# implement the --enable-shared flag, and supports the `shared' and
+# `disable-shared' LT_INIT options.
+# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'.
+m4_define([_LT_ENABLE_SHARED],
+[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl
+AC_ARG_ENABLE([shared],
+ [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@],
+ [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])],
+ [p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_shared=yes ;;
+ no) enable_shared=no ;;
+ *)
+ enable_shared=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ for pkg in $enableval; do
+ IFS="$lt_save_ifs"
+ if test "X$pkg" = "X$p"; then
+ enable_shared=yes
+ fi
+ done
+ IFS="$lt_save_ifs"
+ ;;
+ esac],
+ [enable_shared=]_LT_ENABLE_SHARED_DEFAULT)
+
+ _LT_DECL([build_libtool_libs], [enable_shared], [0],
+ [Whether or not to build shared libraries])
+])# _LT_ENABLE_SHARED
+
+LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])])
+LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])])
+
+# Old names:
+AC_DEFUN([AC_ENABLE_SHARED],
+[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared])
+])
+
+AC_DEFUN([AC_DISABLE_SHARED],
+[_LT_SET_OPTION([LT_INIT], [disable-shared])
+])
+
+AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)])
+AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_ENABLE_SHARED], [])
+dnl AC_DEFUN([AM_DISABLE_SHARED], [])
+
+
+
+# _LT_ENABLE_STATIC([DEFAULT])
+# ----------------------------
+# implement the --enable-static flag, and support the `static' and
+# `disable-static' LT_INIT options.
+# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'.
+m4_define([_LT_ENABLE_STATIC],
+[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl
+AC_ARG_ENABLE([static],
+ [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@],
+ [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])],
+ [p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_static=yes ;;
+ no) enable_static=no ;;
+ *)
+ enable_static=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ for pkg in $enableval; do
+ IFS="$lt_save_ifs"
+ if test "X$pkg" = "X$p"; then
+ enable_static=yes
+ fi
+ done
+ IFS="$lt_save_ifs"
+ ;;
+ esac],
+ [enable_static=]_LT_ENABLE_STATIC_DEFAULT)
+
+ _LT_DECL([build_old_libs], [enable_static], [0],
+ [Whether or not to build static libraries])
+])# _LT_ENABLE_STATIC
+
+LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])])
+LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])])
+
+# Old names:
+AC_DEFUN([AC_ENABLE_STATIC],
+[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static])
+])
+
+AC_DEFUN([AC_DISABLE_STATIC],
+[_LT_SET_OPTION([LT_INIT], [disable-static])
+])
+
+AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)])
+AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_ENABLE_STATIC], [])
+dnl AC_DEFUN([AM_DISABLE_STATIC], [])
+
+
+
+# _LT_ENABLE_FAST_INSTALL([DEFAULT])
+# ----------------------------------
+# implement the --enable-fast-install flag, and support the `fast-install'
+# and `disable-fast-install' LT_INIT options.
+# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'.
+m4_define([_LT_ENABLE_FAST_INSTALL],
+[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl
+AC_ARG_ENABLE([fast-install],
+ [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@],
+ [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])],
+ [p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_fast_install=yes ;;
+ no) enable_fast_install=no ;;
+ *)
+ enable_fast_install=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ for pkg in $enableval; do
+ IFS="$lt_save_ifs"
+ if test "X$pkg" = "X$p"; then
+ enable_fast_install=yes
+ fi
+ done
+ IFS="$lt_save_ifs"
+ ;;
+ esac],
+ [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT)
+
+_LT_DECL([fast_install], [enable_fast_install], [0],
+ [Whether or not to optimize for fast installation])dnl
+])# _LT_ENABLE_FAST_INSTALL
+
+LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])])
+LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])])
+
+# Old names:
+AU_DEFUN([AC_ENABLE_FAST_INSTALL],
+[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you put
+the `fast-install' option into LT_INIT's first parameter.])
+])
+
+AU_DEFUN([AC_DISABLE_FAST_INSTALL],
+[_LT_SET_OPTION([LT_INIT], [disable-fast-install])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you put
+the `disable-fast-install' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], [])
+dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], [])
+
+
+# _LT_WITH_PIC([MODE])
+# --------------------
+# implement the --with-pic flag, and support the `pic-only' and `no-pic'
+# LT_INIT options.
+# MODE is either `yes' or `no'. If omitted, it defaults to `both'.
+m4_define([_LT_WITH_PIC],
+[AC_ARG_WITH([pic],
+ [AS_HELP_STRING([--with-pic],
+ [try to use only PIC/non-PIC objects @<:@default=use both@:>@])],
+ [pic_mode="$withval"],
+ [pic_mode=default])
+
+test -z "$pic_mode" && pic_mode=m4_default([$1], [default])
+
+_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl
+])# _LT_WITH_PIC
+
+LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])])
+LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])])
+
+# Old name:
+AU_DEFUN([AC_LIBTOOL_PICMODE],
+[_LT_SET_OPTION([LT_INIT], [pic-only])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the `pic-only' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_PICMODE], [])
+
+
+m4_define([_LTDL_MODE], [])
+LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive],
+ [m4_define([_LTDL_MODE], [nonrecursive])])
+LT_OPTION_DEFINE([LTDL_INIT], [recursive],
+ [m4_define([_LTDL_MODE], [recursive])])
+LT_OPTION_DEFINE([LTDL_INIT], [subproject],
+ [m4_define([_LTDL_MODE], [subproject])])
+
+m4_define([_LTDL_TYPE], [])
+LT_OPTION_DEFINE([LTDL_INIT], [installable],
+ [m4_define([_LTDL_TYPE], [installable])])
+LT_OPTION_DEFINE([LTDL_INIT], [convenience],
+ [m4_define([_LTDL_TYPE], [convenience])])
+
+# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*-
+#
+# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
+# Written by Gary V. Vaughan, 2004
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# serial 6 ltsugar.m4
+
+# This is to help aclocal find these macros, as it can't see m4_define.
+AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])])
+
+
+# lt_join(SEP, ARG1, [ARG2...])
+# -----------------------------
+# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their
+# associated separator.
+# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier
+# versions in m4sugar had bugs.
+m4_define([lt_join],
+[m4_if([$#], [1], [],
+ [$#], [2], [[$2]],
+ [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])])
+m4_define([_lt_join],
+[m4_if([$#$2], [2], [],
+ [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])])
+
+
+# lt_car(LIST)
+# lt_cdr(LIST)
+# ------------
+# Manipulate m4 lists.
+# These macros are necessary as long as will still need to support
+# Autoconf-2.59 which quotes differently.
+m4_define([lt_car], [[$1]])
+m4_define([lt_cdr],
+[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])],
+ [$#], 1, [],
+ [m4_dquote(m4_shift($@))])])
+m4_define([lt_unquote], $1)
+
+
+# lt_append(MACRO-NAME, STRING, [SEPARATOR])
+# ------------------------------------------
+# Redefine MACRO-NAME to hold its former content plus `SEPARATOR'`STRING'.
+# Note that neither SEPARATOR nor STRING are expanded; they are appended
+# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked).
+# No SEPARATOR is output if MACRO-NAME was previously undefined (different
+# than defined and empty).
+#
+# This macro is needed until we can rely on Autoconf 2.62, since earlier
+# versions of m4sugar mistakenly expanded SEPARATOR but not STRING.
+m4_define([lt_append],
+[m4_define([$1],
+ m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])])
+
+
+
+# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...])
+# ----------------------------------------------------------
+# Produce a SEP delimited list of all paired combinations of elements of
+# PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list
+# has the form PREFIXmINFIXSUFFIXn.
+# Needed until we can rely on m4_combine added in Autoconf 2.62.
+m4_define([lt_combine],
+[m4_if(m4_eval([$# > 3]), [1],
+ [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl
+[[m4_foreach([_Lt_prefix], [$2],
+ [m4_foreach([_Lt_suffix],
+ ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[,
+ [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])])
+
+
+# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ])
+# -----------------------------------------------------------------------
+# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited
+# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ.
+m4_define([lt_if_append_uniq],
+[m4_ifdef([$1],
+ [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1],
+ [lt_append([$1], [$2], [$3])$4],
+ [$5])],
+ [lt_append([$1], [$2], [$3])$4])])
+
+
+# lt_dict_add(DICT, KEY, VALUE)
+# -----------------------------
+m4_define([lt_dict_add],
+[m4_define([$1($2)], [$3])])
+
+
+# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE)
+# --------------------------------------------
+m4_define([lt_dict_add_subkey],
+[m4_define([$1($2:$3)], [$4])])
+
+
+# lt_dict_fetch(DICT, KEY, [SUBKEY])
+# ----------------------------------
+m4_define([lt_dict_fetch],
+[m4_ifval([$3],
+ m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]),
+ m4_ifdef([$1($2)], [m4_defn([$1($2)])]))])
+
+
+# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE])
+# -----------------------------------------------------------------
+m4_define([lt_if_dict_fetch],
+[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4],
+ [$5],
+ [$6])])
+
+
+# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...])
+# --------------------------------------------------------------
+m4_define([lt_dict_filter],
+[m4_if([$5], [], [],
+ [lt_join(m4_quote(m4_default([$4], [[, ]])),
+ lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]),
+ [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl
+])
+
+# ltversion.m4 -- version numbers -*- Autoconf -*-
+#
+# Copyright (C) 2004 Free Software Foundation, Inc.
+# Written by Scott James Remnant, 2004
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# Generated from ltversion.in.
+
+# serial 3017 ltversion.m4
+# This file is part of GNU Libtool
+
+m4_define([LT_PACKAGE_VERSION], [2.2.6b])
+m4_define([LT_PACKAGE_REVISION], [1.3017])
+
+AC_DEFUN([LTVERSION_VERSION],
+[macro_version='2.2.6b'
+macro_revision='1.3017'
+_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?])
+_LT_DECL(, macro_revision, 0)
+])
+
+# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*-
+#
+# Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc.
+# Written by Scott James Remnant, 2004.
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# serial 4 lt~obsolete.m4
+
+# These exist entirely to fool aclocal when bootstrapping libtool.
+#
+# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN)
+# which have later been changed to m4_define as they aren't part of the
+# exported API, or moved to Autoconf or Automake where they belong.
+#
+# The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN
+# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us
+# using a macro with the same name in our local m4/libtool.m4 it'll
+# pull the old libtool.m4 in (it doesn't see our shiny new m4_define
+# and doesn't know about Autoconf macros at all.)
+#
+# So we provide this file, which has a silly filename so it's always
+# included after everything else. This provides aclocal with the
+# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything
+# because those macros already exist, or will be overwritten later.
+# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6.
+#
+# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here.
+# Yes, that means every name once taken will need to remain here until
+# we give up compatibility with versions before 1.7, at which point
+# we need to keep only those names which we still refer to.
+
+# This is to help aclocal find these macros, as it can't see m4_define.
+AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])])
+
+m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])])
+m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])])
+m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])])
+m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])])
+m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])])
+m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])])
+m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])])
+m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])])
+m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])])
+m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])])
+m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])])
+m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])])
+m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])])
+m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])])
+m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])])
+m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])])
+m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])])
+m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])])
+m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])])
+m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])])
+m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])])
+m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])])
+m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])])
+m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])])
+m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])])
+m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])])
+m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])])
+m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])])
+m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])])
+m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])])
+m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])])
+m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])])
+m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])])
+m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])])
+m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])])
+m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])])
+m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])])
+m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])])
+m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])])
+m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])])
+m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])])
+m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])])
+m4_ifndef([AC_LIBTOOL_RC], [AC_DEFUN([AC_LIBTOOL_RC])])
+m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])])
+m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])])
+m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])])
+m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])])
+m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])])
+m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])])
+m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])])
+m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])])
+
+# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*-
+#
+# Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
+#
+# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# PKG_PROG_PKG_CONFIG([MIN-VERSION])
+# ----------------------------------
+AC_DEFUN([PKG_PROG_PKG_CONFIG],
+[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
+m4_pattern_allow([^PKG_CONFIG(_PATH)?$])
+AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])dnl
+if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
+ AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
+fi
+if test -n "$PKG_CONFIG"; then
+ _pkg_min_version=m4_default([$1], [0.9.0])
+ AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version])
+ if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ PKG_CONFIG=""
+ fi
+
+fi[]dnl
+])# PKG_PROG_PKG_CONFIG
+
+# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+#
+# Check to see whether a particular set of modules exists. Similar
+# to PKG_CHECK_MODULES(), but does not set variables or print errors.
+#
+#
+# Similar to PKG_CHECK_MODULES, make sure that the first instance of
+# this or PKG_CHECK_MODULES is called, or make sure to call
+# PKG_CHECK_EXISTS manually
+# --------------------------------------------------------------
+AC_DEFUN([PKG_CHECK_EXISTS],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+if test -n "$PKG_CONFIG" && \
+ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
+ m4_ifval([$2], [$2], [:])
+m4_ifvaln([$3], [else
+ $3])dnl
+fi])
+
+
+# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
+# ---------------------------------------------
+m4_define([_PKG_CONFIG],
+[if test -n "$$1"; then
+ pkg_cv_[]$1="$$1"
+ elif test -n "$PKG_CONFIG"; then
+ PKG_CHECK_EXISTS([$3],
+ [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`],
+ [pkg_failed=yes])
+ else
+ pkg_failed=untried
+fi[]dnl
+])# _PKG_CONFIG
+
+# _PKG_SHORT_ERRORS_SUPPORTED
+# -----------------------------
+AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi[]dnl
+])# _PKG_SHORT_ERRORS_SUPPORTED
+
+
+# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
+# [ACTION-IF-NOT-FOUND])
+#
+#
+# Note that if there is a possibility the first call to
+# PKG_CHECK_MODULES might not happen, you should be sure to include an
+# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
+#
+#
+# --------------------------------------------------------------
+AC_DEFUN([PKG_CHECK_MODULES],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
+AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
+
+pkg_failed=no
+AC_MSG_CHECKING([for $1])
+
+_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
+_PKG_CONFIG([$1][_LIBS], [libs], [$2])
+
+m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
+and $1[]_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.])
+
+if test $pkg_failed = yes; then
+ _PKG_SHORT_ERRORS_SUPPORTED
+ if test $_pkg_short_errors_supported = yes; then
+ $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "$2" 2>&1`
+ else
+ $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors "$2" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
+
+ ifelse([$4], , [AC_MSG_ERROR(dnl
+[Package requirements ($2) were not met:
+
+$$1_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+_PKG_TEXT
+])],
+ [AC_MSG_RESULT([no])
+ $4])
+elif test $pkg_failed = untried; then
+ ifelse([$4], , [AC_MSG_FAILURE(dnl
+[The pkg-config script could not be found or is too old. Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+_PKG_TEXT
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.])],
+ [$4])
+else
+ $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
+ $1[]_LIBS=$pkg_cv_[]$1[]_LIBS
+ AC_MSG_RESULT([yes])
+ ifelse([$3], , :, [$3])
+fi[]dnl
+])# PKG_CHECK_MODULES
+
+# Copyright (C) 2002, 2003, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_AUTOMAKE_VERSION(VERSION)
+# ----------------------------
+# Automake X.Y traces this macro to ensure aclocal.m4 has been
+# generated from the m4 files accompanying Automake X.Y.
+# (This private macro should not be called outside this file.)
+AC_DEFUN([AM_AUTOMAKE_VERSION],
+[am__api_version='1.11'
+dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
+dnl require some minimum version. Point them to the right macro.
+m4_if([$1], [1.11.1], [],
+ [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
+])
+
+# _AM_AUTOCONF_VERSION(VERSION)
+# -----------------------------
+# aclocal traces this macro to find the Autoconf version.
+# This is a private macro too. Using m4_define simplifies
+# the logic in aclocal, which can simply ignore this definition.
+m4_define([_AM_AUTOCONF_VERSION], [])
+
+# AM_SET_CURRENT_AUTOMAKE_VERSION
+# -------------------------------
+# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
+# This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
+AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
+[AM_AUTOMAKE_VERSION([1.11.1])dnl
+m4_ifndef([AC_AUTOCONF_VERSION],
+ [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
+_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
+
+# AM_AUX_DIR_EXPAND -*- Autoconf -*-
+
+# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets
+# $ac_aux_dir to `$srcdir/foo'. In other projects, it is set to
+# `$srcdir', `$srcdir/..', or `$srcdir/../..'.
+#
+# Of course, Automake must honor this variable whenever it calls a
+# tool from the auxiliary directory. The problem is that $srcdir (and
+# therefore $ac_aux_dir as well) can be either absolute or relative,
+# depending on how configure is run. This is pretty annoying, since
+# it makes $ac_aux_dir quite unusable in subdirectories: in the top
+# source directory, any form will work fine, but in subdirectories a
+# relative path needs to be adjusted first.
+#
+# $ac_aux_dir/missing
+# fails when called from a subdirectory if $ac_aux_dir is relative
+# $top_srcdir/$ac_aux_dir/missing
+# fails if $ac_aux_dir is absolute,
+# fails when called from a subdirectory in a VPATH build with
+# a relative $ac_aux_dir
+#
+# The reason of the latter failure is that $top_srcdir and $ac_aux_dir
+# are both prefixed by $srcdir. In an in-source build this is usually
+# harmless because $srcdir is `.', but things will broke when you
+# start a VPATH build or use an absolute $srcdir.
+#
+# So we could use something similar to $top_srcdir/$ac_aux_dir/missing,
+# iff we strip the leading $srcdir from $ac_aux_dir. That would be:
+# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"`
+# and then we would define $MISSING as
+# MISSING="\${SHELL} $am_aux_dir/missing"
+# This will work as long as MISSING is not called from configure, because
+# unfortunately $(top_srcdir) has no meaning in configure.
+# However there are other variables, like CC, which are often used in
+# configure, and could therefore not use this "fixed" $ac_aux_dir.
+#
+# Another solution, used here, is to always expand $ac_aux_dir to an
+# absolute PATH. The drawback is that using absolute paths prevent a
+# configured tree to be moved without reconfiguration.
+
+AC_DEFUN([AM_AUX_DIR_EXPAND],
+[dnl Rely on autoconf to set up CDPATH properly.
+AC_PREREQ([2.50])dnl
+# expand $ac_aux_dir to an absolute path
+am_aux_dir=`cd $ac_aux_dir && pwd`
+])
+
+# AM_CONDITIONAL -*- Autoconf -*-
+
+# Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005, 2006, 2008
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 9
+
+# AM_CONDITIONAL(NAME, SHELL-CONDITION)
+# -------------------------------------
+# Define a conditional.
+AC_DEFUN([AM_CONDITIONAL],
+[AC_PREREQ(2.52)dnl
+ ifelse([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])],
+ [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
+AC_SUBST([$1_TRUE])dnl
+AC_SUBST([$1_FALSE])dnl
+_AM_SUBST_NOTMAKE([$1_TRUE])dnl
+_AM_SUBST_NOTMAKE([$1_FALSE])dnl
+m4_define([_AM_COND_VALUE_$1], [$2])dnl
+if $2; then
+ $1_TRUE=
+ $1_FALSE='#'
+else
+ $1_TRUE='#'
+ $1_FALSE=
+fi
+AC_CONFIG_COMMANDS_PRE(
+[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then
+ AC_MSG_ERROR([[conditional "$1" was never defined.
+Usually this means the macro was only invoked conditionally.]])
+fi])])
+
+# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 10
+
+# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be
+# written in clear, in which case automake, when reading aclocal.m4,
+# will think it sees a *use*, and therefore will trigger all it's
+# C support machinery. Also note that it means that autoscan, seeing
+# CC etc. in the Makefile, will ask for an AC_PROG_CC use...
+
+
+# _AM_DEPENDENCIES(NAME)
+# ----------------------
+# See how the compiler implements dependency checking.
+# NAME is "CC", "CXX", "GCJ", or "OBJC".
+# We try a few techniques and use that to set a single cache variable.
+#
+# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was
+# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular
+# dependency, and given that the user is not expected to run this macro,
+# just rely on AC_PROG_CC.
+AC_DEFUN([_AM_DEPENDENCIES],
+[AC_REQUIRE([AM_SET_DEPDIR])dnl
+AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl
+AC_REQUIRE([AM_MAKE_INCLUDE])dnl
+AC_REQUIRE([AM_DEP_TRACK])dnl
+
+ifelse([$1], CC, [depcc="$CC" am_compiler_list=],
+ [$1], CXX, [depcc="$CXX" am_compiler_list=],
+ [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'],
+ [$1], UPC, [depcc="$UPC" am_compiler_list=],
+ [$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'],
+ [depcc="$$1" am_compiler_list=])
+
+AC_CACHE_CHECK([dependency style of $depcc],
+ [am_cv_$1_dependencies_compiler_type],
+[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+ # We make a subdir and do the tests there. Otherwise we can end up
+ # making bogus files that we don't know about and never remove. For
+ # instance it was reported that on HP-UX the gcc test will end up
+ # making a dummy file named `D' -- because `-MD' means `put the output
+ # in D'.
+ mkdir conftest.dir
+ # Copy depcomp to subdir because otherwise we won't find it if we're
+ # using a relative directory.
+ cp "$am_depcomp" conftest.dir
+ cd conftest.dir
+ # We will build objects and dependencies in a subdirectory because
+ # it helps to detect inapplicable dependency modes. For instance
+ # both Tru64's cc and ICC support -MD to output dependencies as a
+ # side effect of compilation, but ICC will put the dependencies in
+ # the current directory while Tru64 will put them in the object
+ # directory.
+ mkdir sub
+
+ am_cv_$1_dependencies_compiler_type=none
+ if test "$am_compiler_list" = ""; then
+ am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp`
+ fi
+ am__universal=false
+ m4_case([$1], [CC],
+ [case " $depcc " in #(
+ *\ -arch\ *\ -arch\ *) am__universal=true ;;
+ esac],
+ [CXX],
+ [case " $depcc " in #(
+ *\ -arch\ *\ -arch\ *) am__universal=true ;;
+ esac])
+
+ for depmode in $am_compiler_list; do
+ # Setup a source with many dependencies, because some compilers
+ # like to wrap large dependency lists on column 80 (with \), and
+ # we should not choose a depcomp mode which is confused by this.
+ #
+ # We need to recreate these files for each test, as the compiler may
+ # overwrite some of them when testing with obscure command lines.
+ # This happens at least with the AIX C compiler.
+ : > sub/conftest.c
+ for i in 1 2 3 4 5 6; do
+ echo '#include "conftst'$i'.h"' >> sub/conftest.c
+ # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
+ # Solaris 8's {/usr,}/bin/sh.
+ touch sub/conftst$i.h
+ done
+ echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+ # We check with `-c' and `-o' for the sake of the "dashmstdout"
+ # mode. It turns out that the SunPro C++ compiler does not properly
+ # handle `-M -o', and we need to detect this. Also, some Intel
+ # versions had trouble with output in subdirs
+ am__obj=sub/conftest.${OBJEXT-o}
+ am__minus_obj="-o $am__obj"
+ case $depmode in
+ gcc)
+ # This depmode causes a compiler race in universal mode.
+ test "$am__universal" = false || continue
+ ;;
+ nosideeffect)
+ # after this tag, mechanisms are not by side-effect, so they'll
+ # only be used when explicitly requested
+ if test "x$enable_dependency_tracking" = xyes; then
+ continue
+ else
+ break
+ fi
+ ;;
+ msvisualcpp | msvcmsys)
+ # This compiler won't grok `-c -o', but also, the minuso test has
+ # not run yet. These depmodes are late enough in the game, and
+ # so weak that their functioning should not be impacted.
+ am__obj=conftest.${OBJEXT-o}
+ am__minus_obj=
+ ;;
+ none) break ;;
+ esac
+ if depmode=$depmode \
+ source=sub/conftest.c object=$am__obj \
+ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
+ >/dev/null 2>conftest.err &&
+ grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
+ ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+ # icc doesn't choke on unknown options, it will just issue warnings
+ # or remarks (even with -Werror). So we grep stderr for any message
+ # that says an option was ignored or not supported.
+ # When given -MP, icc 7.0 and 7.1 complain thusly:
+ # icc: Command line warning: ignoring option '-M'; no argument required
+ # The diagnosis changed in icc 8.0:
+ # icc: Command line remark: option '-MP' not supported
+ if (grep 'ignoring option' conftest.err ||
+ grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+ am_cv_$1_dependencies_compiler_type=$depmode
+ break
+ fi
+ fi
+ done
+
+ cd ..
+ rm -rf conftest.dir
+else
+ am_cv_$1_dependencies_compiler_type=none
+fi
+])
+AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type])
+AM_CONDITIONAL([am__fastdep$1], [
+ test "x$enable_dependency_tracking" != xno \
+ && test "$am_cv_$1_dependencies_compiler_type" = gcc3])
+])
+
+
+# AM_SET_DEPDIR
+# -------------
+# Choose a directory name for dependency files.
+# This macro is AC_REQUIREd in _AM_DEPENDENCIES
+AC_DEFUN([AM_SET_DEPDIR],
+[AC_REQUIRE([AM_SET_LEADING_DOT])dnl
+AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl
+])
+
+
+# AM_DEP_TRACK
+# ------------
+AC_DEFUN([AM_DEP_TRACK],
+[AC_ARG_ENABLE(dependency-tracking,
+[ --disable-dependency-tracking speeds up one-time build
+ --enable-dependency-tracking do not reject slow dependency extractors])
+if test "x$enable_dependency_tracking" != xno; then
+ am_depcomp="$ac_aux_dir/depcomp"
+ AMDEPBACKSLASH='\'
+fi
+AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
+AC_SUBST([AMDEPBACKSLASH])dnl
+_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl
+])
+
+# Generate code to set up dependency tracking. -*- Autoconf -*-
+
+# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2008
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+#serial 5
+
+# _AM_OUTPUT_DEPENDENCY_COMMANDS
+# ------------------------------
+AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
+[{
+ # Autoconf 2.62 quotes --file arguments for eval, but not when files
+ # are listed without --file. Let's play safe and only enable the eval
+ # if we detect the quoting.
+ case $CONFIG_FILES in
+ *\'*) eval set x "$CONFIG_FILES" ;;
+ *) set x $CONFIG_FILES ;;
+ esac
+ shift
+ for mf
+ do
+ # Strip MF so we end up with the name of the file.
+ mf=`echo "$mf" | sed -e 's/:.*$//'`
+ # Check whether this is an Automake generated Makefile or not.
+ # We used to match only the files named `Makefile.in', but
+ # some people rename them; so instead we look at the file content.
+ # Grep'ing the first line is not enough: some people post-process
+ # each Makefile.in and add a new line on top of each file to say so.
+ # Grep'ing the whole file is not good either: AIX grep has a line
+ # limit of 2048, but all sed's we know have understand at least 4000.
+ if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
+ dirpart=`AS_DIRNAME("$mf")`
+ else
+ continue
+ fi
+ # Extract the definition of DEPDIR, am__include, and am__quote
+ # from the Makefile without running `make'.
+ DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
+ test -z "$DEPDIR" && continue
+ am__include=`sed -n 's/^am__include = //p' < "$mf"`
+ test -z "am__include" && continue
+ am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
+ # When using ansi2knr, U may be empty or an underscore; expand it
+ U=`sed -n 's/^U = //p' < "$mf"`
+ # Find all dependency output files, they are included files with
+ # $(DEPDIR) in their names. We invoke sed twice because it is the
+ # simplest approach to changing $(DEPDIR) to its actual value in the
+ # expansion.
+ for file in `sed -n "
+ s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
+ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
+ # Make sure the directory exists.
+ test -f "$dirpart/$file" && continue
+ fdir=`AS_DIRNAME(["$file"])`
+ AS_MKDIR_P([$dirpart/$fdir])
+ # echo "creating $dirpart/$file"
+ echo '# dummy' > "$dirpart/$file"
+ done
+ done
+}
+])# _AM_OUTPUT_DEPENDENCY_COMMANDS
+
+
+# AM_OUTPUT_DEPENDENCY_COMMANDS
+# -----------------------------
+# This macro should only be invoked once -- use via AC_REQUIRE.
+#
+# This code is only required when automatic dependency tracking
+# is enabled. FIXME. This creates each `.P' file that we will
+# need in order to bootstrap the dependency handling code.
+AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
+[AC_CONFIG_COMMANDS([depfiles],
+ [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS],
+ [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"])
+])
+
+# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 8
+
+# AM_CONFIG_HEADER is obsolete. It has been replaced by AC_CONFIG_HEADERS.
+AU_DEFUN([AM_CONFIG_HEADER], [AC_CONFIG_HEADERS($@)])
+
+# Do all the work for Automake. -*- Autoconf -*-
+
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+# 2005, 2006, 2008, 2009 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 16
+
+# This macro actually does too much. Some checks are only needed if
+# your package does certain things. But this isn't really a big deal.
+
+# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE])
+# AM_INIT_AUTOMAKE([OPTIONS])
+# -----------------------------------------------
+# The call with PACKAGE and VERSION arguments is the old style
+# call (pre autoconf-2.50), which is being phased out. PACKAGE
+# and VERSION should now be passed to AC_INIT and removed from
+# the call to AM_INIT_AUTOMAKE.
+# We support both call styles for the transition. After
+# the next Automake release, Autoconf can make the AC_INIT
+# arguments mandatory, and then we can depend on a new Autoconf
+# release and drop the old call support.
+AC_DEFUN([AM_INIT_AUTOMAKE],
+[AC_PREREQ([2.62])dnl
+dnl Autoconf wants to disallow AM_ names. We explicitly allow
+dnl the ones we care about.
+m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
+AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl
+AC_REQUIRE([AC_PROG_INSTALL])dnl
+if test "`cd $srcdir && pwd`" != "`pwd`"; then
+ # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
+ # is not polluted with repeated "-I."
+ AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl
+ # test to see if srcdir already configured
+ if test -f $srcdir/config.status; then
+ AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
+ fi
+fi
+
+# test whether we have cygpath
+if test -z "$CYGPATH_W"; then
+ if (cygpath --version) >/dev/null 2>/dev/null; then
+ CYGPATH_W='cygpath -w'
+ else
+ CYGPATH_W=echo
+ fi
+fi
+AC_SUBST([CYGPATH_W])
+
+# Define the identity of the package.
+dnl Distinguish between old-style and new-style calls.
+m4_ifval([$2],
+[m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl
+ AC_SUBST([PACKAGE], [$1])dnl
+ AC_SUBST([VERSION], [$2])],
+[_AM_SET_OPTIONS([$1])dnl
+dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT.
+m4_if(m4_ifdef([AC_PACKAGE_NAME], 1)m4_ifdef([AC_PACKAGE_VERSION], 1), 11,,
+ [m4_fatal([AC_INIT should be called with package and version arguments])])dnl
+ AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
+ AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl
+
+_AM_IF_OPTION([no-define],,
+[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package])
+ AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl
+
+# Some tools Automake needs.
+AC_REQUIRE([AM_SANITY_CHECK])dnl
+AC_REQUIRE([AC_ARG_PROGRAM])dnl
+AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version})
+AM_MISSING_PROG(AUTOCONF, autoconf)
+AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version})
+AM_MISSING_PROG(AUTOHEADER, autoheader)
+AM_MISSING_PROG(MAKEINFO, makeinfo)
+AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
+AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl
+AC_REQUIRE([AM_PROG_MKDIR_P])dnl
+# We need awk for the "check" target. The system "awk" is bad on
+# some platforms.
+AC_REQUIRE([AC_PROG_AWK])dnl
+AC_REQUIRE([AC_PROG_MAKE_SET])dnl
+AC_REQUIRE([AM_SET_LEADING_DOT])dnl
+_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])],
+ [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])],
+ [_AM_PROG_TAR([v7])])])
+_AM_IF_OPTION([no-dependencies],,
+[AC_PROVIDE_IFELSE([AC_PROG_CC],
+ [_AM_DEPENDENCIES(CC)],
+ [define([AC_PROG_CC],
+ defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_CXX],
+ [_AM_DEPENDENCIES(CXX)],
+ [define([AC_PROG_CXX],
+ defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_OBJC],
+ [_AM_DEPENDENCIES(OBJC)],
+ [define([AC_PROG_OBJC],
+ defn([AC_PROG_OBJC])[_AM_DEPENDENCIES(OBJC)])])dnl
+])
+_AM_IF_OPTION([silent-rules], [AC_REQUIRE([AM_SILENT_RULES])])dnl
+dnl The `parallel-tests' driver may need to know about EXEEXT, so add the
+dnl `am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This macro
+dnl is hooked onto _AC_COMPILER_EXEEXT early, see below.
+AC_CONFIG_COMMANDS_PRE(dnl
+[m4_provide_if([_AM_COMPILER_EXEEXT],
+ [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl
+])
+
+dnl Hook into `_AC_COMPILER_EXEEXT' early to learn its expansion. Do not
+dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further
+dnl mangled by Autoconf and run in a shell conditional statement.
+m4_define([_AC_COMPILER_EXEEXT],
+m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])])
+
+
+# When config.status generates a header, we must update the stamp-h file.
+# This file resides in the same directory as the config header
+# that is generated. The stamp files are numbered to have different names.
+
+# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the
+# loop where config.status creates the headers, so we can generate
+# our stamp files there.
+AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK],
+[# Compute $1's index in $config_headers.
+_am_arg=$1
+_am_stamp_count=1
+for _am_header in $config_headers :; do
+ case $_am_header in
+ $_am_arg | $_am_arg:* )
+ break ;;
+ * )
+ _am_stamp_count=`expr $_am_stamp_count + 1` ;;
+ esac
+done
+echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
+
+# Copyright (C) 2001, 2003, 2005, 2008 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_PROG_INSTALL_SH
+# ------------------
+# Define $install_sh.
+AC_DEFUN([AM_PROG_INSTALL_SH],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+if test x"${install_sh}" != xset; then
+ case $am_aux_dir in
+ *\ * | *\ *)
+ install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
+ *)
+ install_sh="\${SHELL} $am_aux_dir/install-sh"
+ esac
+fi
+AC_SUBST(install_sh)])
+
+# Copyright (C) 2003, 2005 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 2
+
+# Check whether the underlying file-system supports filenames
+# with a leading dot. For instance MS-DOS doesn't.
+AC_DEFUN([AM_SET_LEADING_DOT],
+[rm -rf .tst 2>/dev/null
+mkdir .tst 2>/dev/null
+if test -d .tst; then
+ am__leading_dot=.
+else
+ am__leading_dot=_
+fi
+rmdir .tst 2>/dev/null
+AC_SUBST([am__leading_dot])])
+
+# Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2005
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 5
+
+# AM_PROG_LEX
+# -----------
+# Autoconf leaves LEX=: if lex or flex can't be found. Change that to a
+# "missing" invocation, for better error output.
+AC_DEFUN([AM_PROG_LEX],
+[AC_PREREQ(2.50)dnl
+AC_REQUIRE([AM_MISSING_HAS_RUN])dnl
+AC_REQUIRE([AC_PROG_LEX])dnl
+if test "$LEX" = :; then
+ LEX=${am_missing_run}flex
+fi])
+
+# Add --enable-maintainer-mode option to configure. -*- Autoconf -*-
+# From Jim Meyering
+
+# Copyright (C) 1996, 1998, 2000, 2001, 2002, 2003, 2004, 2005, 2008
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 5
+
+# AM_MAINTAINER_MODE([DEFAULT-MODE])
+# ----------------------------------
+# Control maintainer-specific portions of Makefiles.
+# Default is to disable them, unless `enable' is passed literally.
+# For symmetry, `disable' may be passed as well. Anyway, the user
+# can override the default with the --enable/--disable switch.
+AC_DEFUN([AM_MAINTAINER_MODE],
+[m4_case(m4_default([$1], [disable]),
+ [enable], [m4_define([am_maintainer_other], [disable])],
+ [disable], [m4_define([am_maintainer_other], [enable])],
+ [m4_define([am_maintainer_other], [enable])
+ m4_warn([syntax], [unexpected argument to AM@&t@_MAINTAINER_MODE: $1])])
+AC_MSG_CHECKING([whether to am_maintainer_other maintainer-specific portions of Makefiles])
+ dnl maintainer-mode's default is 'disable' unless 'enable' is passed
+ AC_ARG_ENABLE([maintainer-mode],
+[ --][am_maintainer_other][-maintainer-mode am_maintainer_other make rules and dependencies not useful
+ (and sometimes confusing) to the casual installer],
+ [USE_MAINTAINER_MODE=$enableval],
+ [USE_MAINTAINER_MODE=]m4_if(am_maintainer_other, [enable], [no], [yes]))
+ AC_MSG_RESULT([$USE_MAINTAINER_MODE])
+ AM_CONDITIONAL([MAINTAINER_MODE], [test $USE_MAINTAINER_MODE = yes])
+ MAINT=$MAINTAINER_MODE_TRUE
+ AC_SUBST([MAINT])dnl
+]
+)
+
+AU_DEFUN([jm_MAINTAINER_MODE], [AM_MAINTAINER_MODE])
+
+# Check to see how 'make' treats includes. -*- Autoconf -*-
+
+# Copyright (C) 2001, 2002, 2003, 2005, 2009 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 4
+
+# AM_MAKE_INCLUDE()
+# -----------------
+# Check to see how make treats includes.
+AC_DEFUN([AM_MAKE_INCLUDE],
+[am_make=${MAKE-make}
+cat > confinc << 'END'
+am__doit:
+ @echo this is the am__doit target
+.PHONY: am__doit
+END
+# If we don't find an include directive, just comment out the code.
+AC_MSG_CHECKING([for style of include used by $am_make])
+am__include="#"
+am__quote=
+_am_result=none
+# First try GNU make style include.
+echo "include confinc" > confmf
+# Ignore all kinds of additional output from `make'.
+case `$am_make -s -f confmf 2> /dev/null` in #(
+*the\ am__doit\ target*)
+ am__include=include
+ am__quote=
+ _am_result=GNU
+ ;;
+esac
+# Now try BSD make style include.
+if test "$am__include" = "#"; then
+ echo '.include "confinc"' > confmf
+ case `$am_make -s -f confmf 2> /dev/null` in #(
+ *the\ am__doit\ target*)
+ am__include=.include
+ am__quote="\""
+ _am_result=BSD
+ ;;
+ esac
+fi
+AC_SUBST([am__include])
+AC_SUBST([am__quote])
+AC_MSG_RESULT([$_am_result])
+rm -f confinc confmf
+])
+
+# Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005, 2008
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 6
+
+# AM_PROG_CC_C_O
+# --------------
+# Like AC_PROG_CC_C_O, but changed for automake.
+AC_DEFUN([AM_PROG_CC_C_O],
+[AC_REQUIRE([AC_PROG_CC_C_O])dnl
+AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+AC_REQUIRE_AUX_FILE([compile])dnl
+# FIXME: we rely on the cache variable name because
+# there is no other way.
+set dummy $CC
+am_cc=`echo $[2] | sed ['s/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/']`
+eval am_t=\$ac_cv_prog_cc_${am_cc}_c_o
+if test "$am_t" != yes; then
+ # Losing compiler, so override with the script.
+ # FIXME: It is wrong to rewrite CC.
+ # But if we don't then we get into trouble of one sort or another.
+ # A longer-term fix would be to have automake use am__CC in this case,
+ # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)"
+ CC="$am_aux_dir/compile $CC"
+fi
+dnl Make sure AC_PROG_CC is never called again, or it will override our
+dnl setting of CC.
+m4_define([AC_PROG_CC],
+ [m4_fatal([AC_PROG_CC cannot be called after AM_PROG_CC_C_O])])
+])
+
+# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*-
+
+# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2004, 2005, 2008
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 6
+
+# AM_MISSING_PROG(NAME, PROGRAM)
+# ------------------------------
+AC_DEFUN([AM_MISSING_PROG],
+[AC_REQUIRE([AM_MISSING_HAS_RUN])
+$1=${$1-"${am_missing_run}$2"}
+AC_SUBST($1)])
+
+
+# AM_MISSING_HAS_RUN
+# ------------------
+# Define MISSING if not defined so far and test if it supports --run.
+# If it does, set am_missing_run to use it, otherwise, to nothing.
+AC_DEFUN([AM_MISSING_HAS_RUN],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+AC_REQUIRE_AUX_FILE([missing])dnl
+if test x"${MISSING+set}" != xset; then
+ case $am_aux_dir in
+ *\ * | *\ *)
+ MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
+ *)
+ MISSING="\${SHELL} $am_aux_dir/missing" ;;
+ esac
+fi
+# Use eval to expand $SHELL
+if eval "$MISSING --run true"; then
+ am_missing_run="$MISSING --run "
+else
+ am_missing_run=
+ AC_MSG_WARN([`missing' script is too old or missing])
+fi
+])
+
+# Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_PROG_MKDIR_P
+# ---------------
+# Check for `mkdir -p'.
+AC_DEFUN([AM_PROG_MKDIR_P],
+[AC_PREREQ([2.60])dnl
+AC_REQUIRE([AC_PROG_MKDIR_P])dnl
+dnl Automake 1.8 to 1.9.6 used to define mkdir_p. We now use MKDIR_P,
+dnl while keeping a definition of mkdir_p for backward compatibility.
+dnl @MKDIR_P@ is magic: AC_OUTPUT adjusts its value for each Makefile.
+dnl However we cannot define mkdir_p as $(MKDIR_P) for the sake of
+dnl Makefile.ins that do not define MKDIR_P, so we do our own
+dnl adjustment using top_builddir (which is defined more often than
+dnl MKDIR_P).
+AC_SUBST([mkdir_p], ["$MKDIR_P"])dnl
+case $mkdir_p in
+ [[\\/$]]* | ?:[[\\/]]*) ;;
+ */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;;
+esac
+])
+
+# Helper functions for option handling. -*- Autoconf -*-
+
+# Copyright (C) 2001, 2002, 2003, 2005, 2008 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 4
+
+# _AM_MANGLE_OPTION(NAME)
+# -----------------------
+AC_DEFUN([_AM_MANGLE_OPTION],
+[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])])
+
+# _AM_SET_OPTION(NAME)
+# ------------------------------
+# Set option NAME. Presently that only means defining a flag for this option.
+AC_DEFUN([_AM_SET_OPTION],
+[m4_define(_AM_MANGLE_OPTION([$1]), 1)])
+
+# _AM_SET_OPTIONS(OPTIONS)
+# ----------------------------------
+# OPTIONS is a space-separated list of Automake options.
+AC_DEFUN([_AM_SET_OPTIONS],
+[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])])
+
+# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET])
+# -------------------------------------------
+# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
+AC_DEFUN([_AM_IF_OPTION],
+[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
+
+# Check to make sure that the build environment is sane. -*- Autoconf -*-
+
+# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005, 2008
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 5
+
+# AM_SANITY_CHECK
+# ---------------
+AC_DEFUN([AM_SANITY_CHECK],
+[AC_MSG_CHECKING([whether build environment is sane])
+# Just in case
+sleep 1
+echo timestamp > conftest.file
+# Reject unsafe characters in $srcdir or the absolute working directory
+# name. Accept space and tab only in the latter.
+am_lf='
+'
+case `pwd` in
+ *[[\\\"\#\$\&\'\`$am_lf]]*)
+ AC_MSG_ERROR([unsafe absolute working directory name]);;
+esac
+case $srcdir in
+ *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*)
+ AC_MSG_ERROR([unsafe srcdir value: `$srcdir']);;
+esac
+
+# Do `set' in a subshell so we don't clobber the current shell's
+# arguments. Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+ set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
+ if test "$[*]" = "X"; then
+ # -L didn't work.
+ set X `ls -t "$srcdir/configure" conftest.file`
+ fi
+ rm -f conftest.file
+ if test "$[*]" != "X $srcdir/configure conftest.file" \
+ && test "$[*]" != "X conftest.file $srcdir/configure"; then
+
+ # If neither matched, then we have a broken ls. This can happen
+ # if, for instance, CONFIG_SHELL is bash and it inherits a
+ # broken ls alias from the environment. This has actually
+ # happened. Such a system could not be considered "sane".
+ AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken
+alias in your environment])
+ fi
+
+ test "$[2]" = conftest.file
+ )
+then
+ # Ok.
+ :
+else
+ AC_MSG_ERROR([newly created file is older than distributed files!
+Check your system clock])
+fi
+AC_MSG_RESULT(yes)])
+
+# Copyright (C) 2009 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 1
+
+# AM_SILENT_RULES([DEFAULT])
+# --------------------------
+# Enable less verbose build rules; with the default set to DEFAULT
+# (`yes' being less verbose, `no' or empty being verbose).
+AC_DEFUN([AM_SILENT_RULES],
+[AC_ARG_ENABLE([silent-rules],
+[ --enable-silent-rules less verbose build output (undo: `make V=1')
+ --disable-silent-rules verbose build output (undo: `make V=0')])
+case $enable_silent_rules in
+yes) AM_DEFAULT_VERBOSITY=0;;
+no) AM_DEFAULT_VERBOSITY=1;;
+*) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);;
+esac
+AC_SUBST([AM_DEFAULT_VERBOSITY])dnl
+AM_BACKSLASH='\'
+AC_SUBST([AM_BACKSLASH])dnl
+_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl
+])
+
+# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_PROG_INSTALL_STRIP
+# ---------------------
+# One issue with vendor `install' (even GNU) is that you can't
+# specify the program used to strip binaries. This is especially
+# annoying in cross-compiling environments, where the build's strip
+# is unlikely to handle the host's binaries.
+# Fortunately install-sh will honor a STRIPPROG variable, so we
+# always use install-sh in `make install-strip', and initialize
+# STRIPPROG with the value of the STRIP variable (set by the user).
+AC_DEFUN([AM_PROG_INSTALL_STRIP],
+[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
+# Installed binaries are usually stripped using `strip' when the user
+# run `make install-strip'. However `strip' might not be the right
+# tool to use in cross-compilation environments, therefore Automake
+# will honor the `STRIP' environment variable to overrule this program.
+dnl Don't test for $cross_compiling = yes, because it might be `maybe'.
+if test "$cross_compiling" != no; then
+ AC_CHECK_TOOL([STRIP], [strip], :)
+fi
+INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
+AC_SUBST([INSTALL_STRIP_PROGRAM])])
+
+# Copyright (C) 2006, 2008 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 2
+
+# _AM_SUBST_NOTMAKE(VARIABLE)
+# ---------------------------
+# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in.
+# This macro is traced by Automake.
+AC_DEFUN([_AM_SUBST_NOTMAKE])
+
+# AM_SUBST_NOTMAKE(VARIABLE)
+# ---------------------------
+# Public sister of _AM_SUBST_NOTMAKE.
+AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
+
+# Check how to create a tarball. -*- Autoconf -*-
+
+# Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 2
+
+# _AM_PROG_TAR(FORMAT)
+# --------------------
+# Check how to create a tarball in format FORMAT.
+# FORMAT should be one of `v7', `ustar', or `pax'.
+#
+# Substitute a variable $(am__tar) that is a command
+# writing to stdout a FORMAT-tarball containing the directory
+# $tardir.
+# tardir=directory && $(am__tar) > result.tar
+#
+# Substitute a variable $(am__untar) that extract such
+# a tarball read from stdin.
+# $(am__untar) < result.tar
+AC_DEFUN([_AM_PROG_TAR],
+[# Always define AMTAR for backward compatibility.
+AM_MISSING_PROG([AMTAR], [tar])
+m4_if([$1], [v7],
+ [am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'],
+ [m4_case([$1], [ustar],, [pax],,
+ [m4_fatal([Unknown tar format])])
+AC_MSG_CHECKING([how to create a $1 tar archive])
+# Loop over all known methods to create a tar archive until one works.
+_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none'
+_am_tools=${am_cv_prog_tar_$1-$_am_tools}
+# Do not fold the above two line into one, because Tru64 sh and
+# Solaris sh will not grok spaces in the rhs of `-'.
+for _am_tool in $_am_tools
+do
+ case $_am_tool in
+ gnutar)
+ for _am_tar in tar gnutar gtar;
+ do
+ AM_RUN_LOG([$_am_tar --version]) && break
+ done
+ am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"'
+ am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"'
+ am__untar="$_am_tar -xf -"
+ ;;
+ plaintar)
+ # Must skip GNU tar: if it does not support --format= it doesn't create
+ # ustar tarball either.
+ (tar --version) >/dev/null 2>&1 && continue
+ am__tar='tar chf - "$$tardir"'
+ am__tar_='tar chf - "$tardir"'
+ am__untar='tar xf -'
+ ;;
+ pax)
+ am__tar='pax -L -x $1 -w "$$tardir"'
+ am__tar_='pax -L -x $1 -w "$tardir"'
+ am__untar='pax -r'
+ ;;
+ cpio)
+ am__tar='find "$$tardir" -print | cpio -o -H $1 -L'
+ am__tar_='find "$tardir" -print | cpio -o -H $1 -L'
+ am__untar='cpio -i -H $1 -d'
+ ;;
+ none)
+ am__tar=false
+ am__tar_=false
+ am__untar=false
+ ;;
+ esac
+
+ # If the value was cached, stop now. We just wanted to have am__tar
+ # and am__untar set.
+ test -n "${am_cv_prog_tar_$1}" && break
+
+ # tar/untar a dummy directory, and stop if the command works
+ rm -rf conftest.dir
+ mkdir conftest.dir
+ echo GrepMe > conftest.dir/file
+ AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar])
+ rm -rf conftest.dir
+ if test -s conftest.tar; then
+ AM_RUN_LOG([$am__untar <conftest.tar])
+ grep GrepMe conftest.dir/file >/dev/null 2>&1 && break
+ fi
+done
+rm -rf conftest.dir
+
+AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool])
+AC_MSG_RESULT([$am_cv_prog_tar_$1])])
+AC_SUBST([am__tar])
+AC_SUBST([am__untar])
+]) # _AM_PROG_TAR
+
+m4_include([acinclude.m4])
diff --git a/attrib/att.c b/attrib/att.c
new file mode 100644
index 0000000..08000e0
--- /dev/null
+++ b/attrib/att.c
@@ -0,0 +1,968 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2010 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
+ *
+ */
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/uuid.h>
+
+#include <glib.h>
+
+#include "att.h"
+
+const char *att_ecode2str(uint8_t status)
+{
+ switch (status) {
+ case ATT_ECODE_INVALID_HANDLE:
+ return "Invalid handle";
+ case ATT_ECODE_READ_NOT_PERM:
+ return "Atribute can't be read";
+ case ATT_ECODE_WRITE_NOT_PERM:
+ return "Attribute can't be written";
+ case ATT_ECODE_INVALID_PDU:
+ return "Attribute PDU was invalid";
+ case ATT_ECODE_INSUFF_AUTHEN:
+ return "Attribute requires authentication before read/write";
+ case ATT_ECODE_REQ_NOT_SUPP:
+ return "Server doesn't support the request received";
+ case ATT_ECODE_INVALID_OFFSET:
+ return "Offset past the end of the attribute";
+ case ATT_ECODE_INSUFF_AUTHO:
+ return "Attribute requires authorization before read/write";
+ case ATT_ECODE_PREP_QUEUE_FULL:
+ return "Too many prepare writes have been queued";
+ case ATT_ECODE_ATTR_NOT_FOUND:
+ return "No attribute found within the given range";
+ case ATT_ECODE_ATTR_NOT_LONG:
+ return "Attribute can't be read/written using Read Blob Req";
+ case ATT_ECODE_INSUFF_ENCR_KEY_SIZE:
+ return "Encryption Key Size is insufficient";
+ case ATT_ECODE_INVAL_ATTR_VALUE_LEN:
+ return "Attribute value length is invalid";
+ case ATT_ECODE_UNLIKELY:
+ return "Request attribute has encountered an unlikely error";
+ case ATT_ECODE_INSUFF_ENC:
+ return "Encryption required before read/write";
+ case ATT_ECODE_UNSUPP_GRP_TYPE:
+ return "Attribute type is not a supported grouping attribute";
+ case ATT_ECODE_INSUFF_RESOURCES:
+ return "Insufficient Resources to complete the request";
+ case ATT_ECODE_IO:
+ return "Internal application error: I/O";
+ default:
+ return "Unexpected error code";
+ }
+}
+
+void att_data_list_free(struct att_data_list *list)
+{
+ if (list == NULL)
+ return;
+
+ if (list->data) {
+ int i;
+ for (i = 0; i < list->num; i++)
+ g_free(list->data[i]);
+ }
+
+ g_free(list->data);
+ g_free(list);
+}
+
+struct att_data_list *att_data_list_alloc(uint16_t num, uint16_t len)
+{
+ struct att_data_list *list;
+ int i;
+
+ list = g_new0(struct att_data_list, 1);
+ list->len = len;
+ list->num = num;
+
+ list->data = g_malloc0(sizeof(uint8_t *) * num);
+
+ for (i = 0; i < num; i++)
+ list->data[i] = g_malloc0(sizeof(uint8_t) * len);
+
+ return list;
+}
+
+uint16_t enc_read_by_grp_req(uint16_t start, uint16_t end, bt_uuid_t *uuid,
+ uint8_t *pdu, int len)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(start) + sizeof(end);
+ uint16_t length;
+
+ if (!uuid)
+ return 0;
+
+ if (uuid->type == BT_UUID16)
+ length = 2;
+ else if (uuid->type == BT_UUID128)
+ length = 16;
+ else
+ return 0;
+
+ if (len < min_len + length)
+ return 0;
+
+ pdu[0] = ATT_OP_READ_BY_GROUP_REQ;
+ att_put_u16(start, &pdu[1]);
+ att_put_u16(end, &pdu[3]);
+
+ att_put_uuid(*uuid, &pdu[5]);
+
+ return min_len + length;
+}
+
+uint16_t dec_read_by_grp_req(const uint8_t *pdu, int len, uint16_t *start,
+ uint16_t *end, bt_uuid_t *uuid)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(*start) + sizeof(*end);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (start == NULL || end == NULL || uuid == NULL)
+ return 0;
+
+ if (pdu[0] != ATT_OP_READ_BY_GROUP_REQ)
+ return 0;
+
+ if (len < min_len + 2)
+ return 0;
+
+ *start = att_get_u16(&pdu[1]);
+ *end = att_get_u16(&pdu[3]);
+ if (len == min_len + 2)
+ *uuid = att_get_uuid16(&pdu[5]);
+ else
+ *uuid = att_get_uuid128(&pdu[5]);
+
+ return len;
+}
+
+uint16_t enc_read_by_grp_resp(struct att_data_list *list, uint8_t *pdu,
+ int len)
+{
+ int i;
+ uint16_t w;
+ uint8_t *ptr;
+
+ if (list == NULL)
+ return 0;
+
+ if (len < list->len + 2)
+ return 0;
+
+ pdu[0] = ATT_OP_READ_BY_GROUP_RESP;
+ pdu[1] = list->len;
+
+ ptr = &pdu[2];
+
+ for (i = 0, w = 2; i < list->num && w + list->len <= len; i++) {
+ memcpy(ptr, list->data[i], list->len);
+ ptr += list->len;
+ w += list->len;
+ }
+
+ return w;
+}
+
+struct att_data_list *dec_read_by_grp_resp(const uint8_t *pdu, int len)
+{
+ struct att_data_list *list;
+ const uint8_t *ptr;
+ uint16_t elen, num;
+ int i;
+
+ if (pdu[0] != ATT_OP_READ_BY_GROUP_RESP)
+ return NULL;
+
+ elen = pdu[1];
+ num = (len - 2) / elen;
+ list = att_data_list_alloc(num, elen);
+
+ ptr = &pdu[2];
+
+ for (i = 0; i < num; i++) {
+ memcpy(list->data[i], ptr, list->len);
+ ptr += list->len;
+ }
+
+ return list;
+}
+
+uint16_t enc_find_by_type_req(uint16_t start, uint16_t end, bt_uuid_t *uuid,
+ const uint8_t *value, int vlen, uint8_t *pdu, int len)
+{
+ uint16_t min_len = sizeof(pdu[0]) + sizeof(start) + sizeof(end) +
+ sizeof(uint16_t);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (!uuid)
+ return 0;
+
+ if (uuid->type != BT_UUID16)
+ return 0;
+
+ if (len < min_len)
+ return 0;
+
+ if (vlen > len - min_len)
+ vlen = len - min_len;
+
+ pdu[0] = ATT_OP_FIND_BY_TYPE_REQ;
+ att_put_u16(start, &pdu[1]);
+ att_put_u16(end, &pdu[3]);
+ att_put_uuid16(*uuid, &pdu[5]);
+
+ if (vlen > 0) {
+ memcpy(&pdu[7], value, vlen);
+ return min_len + vlen;
+ }
+
+ return min_len;
+}
+
+uint16_t dec_find_by_type_req(const uint8_t *pdu, int len, uint16_t *start,
+ uint16_t *end, bt_uuid_t *uuid, uint8_t *value, int *vlen)
+{
+ int valuelen;
+ uint16_t min_len = sizeof(pdu[0]) + sizeof(*start) +
+ sizeof(*end) + sizeof(uint16_t);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (len < min_len)
+ return 0;
+
+ if (pdu[0] != ATT_OP_FIND_BY_TYPE_REQ)
+ return 0;
+
+ /* First requested handle number */
+ if (start)
+ *start = att_get_u16(&pdu[1]);
+
+ /* Last requested handle number */
+ if (end)
+ *end = att_get_u16(&pdu[3]);
+
+ /* Always UUID16 */
+ if (uuid)
+ *uuid = att_get_uuid16(&pdu[5]);
+
+ valuelen = len - min_len;
+
+ /* Attribute value to find */
+ if (valuelen > 0 && value)
+ memcpy(value, pdu + min_len, valuelen);
+
+ if (vlen)
+ *vlen = valuelen;
+
+ return len;
+}
+
+uint16_t enc_find_by_type_resp(GSList *matches, uint8_t *pdu, int len)
+{
+ GSList *l;
+ uint16_t offset;
+
+ if (pdu == NULL || len < 5)
+ return 0;
+
+ pdu[0] = ATT_OP_FIND_BY_TYPE_RESP;
+
+ for (l = matches, offset = 1; l && len >= (offset + 4);
+ l = l->next, offset += 4) {
+ struct att_range *range = l->data;
+
+ att_put_u16(range->start, &pdu[offset]);
+ att_put_u16(range->end, &pdu[offset + 2]);
+ }
+
+ return offset;
+}
+
+GSList *dec_find_by_type_resp(const uint8_t *pdu, int len)
+{
+ struct att_range *range;
+ GSList *matches;
+ int offset;
+
+ if (pdu == NULL || len < 5)
+ return NULL;
+
+ if (pdu[0] != ATT_OP_FIND_BY_TYPE_RESP)
+ return NULL;
+
+ for (offset = 1, matches = NULL; len >= (offset + 4); offset += 4) {
+ range = g_new0(struct att_range, 1);
+ range->start = att_get_u16(&pdu[offset]);
+ range->end = att_get_u16(&pdu[offset + 2]);
+
+ matches = g_slist_append(matches, range);
+ }
+
+ return matches;
+}
+
+uint16_t enc_read_by_type_req(uint16_t start, uint16_t end, bt_uuid_t *uuid,
+ uint8_t *pdu, int len)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(start) + sizeof(end);
+ uint16_t length;
+
+ if (!uuid)
+ return 0;
+
+ if (uuid->type == BT_UUID16)
+ length = 2;
+ else if (uuid->type == BT_UUID128)
+ length = 16;
+ else
+ return 0;
+
+ if (len < min_len + length)
+ return 0;
+
+ pdu[0] = ATT_OP_READ_BY_TYPE_REQ;
+ att_put_u16(start, &pdu[1]);
+ att_put_u16(end, &pdu[3]);
+
+ att_put_uuid(*uuid, &pdu[5]);
+
+ return min_len + length;
+}
+
+uint16_t dec_read_by_type_req(const uint8_t *pdu, int len, uint16_t *start,
+ uint16_t *end, bt_uuid_t *uuid)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(*start) + sizeof(*end);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (start == NULL || end == NULL || uuid == NULL)
+ return 0;
+
+ if (len < min_len + 2)
+ return 0;
+
+ if (pdu[0] != ATT_OP_READ_BY_TYPE_REQ)
+ return 0;
+
+ *start = att_get_u16(&pdu[1]);
+ *end = att_get_u16(&pdu[3]);
+
+ if (len == min_len + 2)
+ *uuid = att_get_uuid16(&pdu[5]);
+ else
+ *uuid = att_get_uuid128(&pdu[5]);
+
+ return len;
+}
+
+uint16_t enc_read_by_type_resp(struct att_data_list *list, uint8_t *pdu, int len)
+{
+ uint8_t *ptr;
+ int i, w, l;
+
+ if (list == NULL)
+ return 0;
+
+ if (pdu == NULL)
+ return 0;
+
+ l = MIN(len - 2, list->len);
+
+ pdu[0] = ATT_OP_READ_BY_TYPE_RESP;
+ pdu[1] = l;
+ ptr = &pdu[2];
+
+ for (i = 0, w = 2; i < list->num && w + l <= len; i++) {
+ memcpy(ptr, list->data[i], l);
+ ptr += l;
+ w += l;
+ }
+
+ return w;
+}
+
+struct att_data_list *dec_read_by_type_resp(const uint8_t *pdu, int len)
+{
+ struct att_data_list *list;
+ const uint8_t *ptr;
+ uint16_t elen, num;
+ int i;
+
+ if (pdu[0] != ATT_OP_READ_BY_TYPE_RESP)
+ return NULL;
+
+ elen = pdu[1];
+ num = (len - 2) / elen;
+ list = att_data_list_alloc(num, elen);
+
+ ptr = &pdu[2];
+
+ for (i = 0; i < num; i++) {
+ memcpy(list->data[i], ptr, list->len);
+ ptr += list->len;
+ }
+
+ return list;
+}
+
+uint16_t enc_write_cmd(uint16_t handle, const uint8_t *value, int vlen,
+ uint8_t *pdu, int len)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (len < min_len)
+ return 0;
+
+ if (vlen > len - min_len)
+ vlen = len - min_len;
+
+ pdu[0] = ATT_OP_WRITE_CMD;
+ att_put_u16(handle, &pdu[1]);
+
+ if (vlen > 0) {
+ memcpy(&pdu[3], value, vlen);
+ return min_len + vlen;
+ }
+
+ return min_len;
+}
+
+uint16_t dec_write_cmd(const uint8_t *pdu, int len, uint16_t *handle,
+ uint8_t *value, int *vlen)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(*handle);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (value == NULL || vlen == NULL || handle == NULL)
+ return 0;
+
+ if (len < min_len)
+ return 0;
+
+ if (pdu[0] != ATT_OP_WRITE_CMD)
+ return 0;
+
+ *handle = att_get_u16(&pdu[1]);
+ memcpy(value, pdu + min_len, len - min_len);
+ *vlen = len - min_len;
+
+ return len;
+}
+
+uint16_t enc_write_req(uint16_t handle, const uint8_t *value, int vlen,
+ uint8_t *pdu, int len)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (len < min_len)
+ return 0;
+
+ if (vlen > len - min_len)
+ vlen = len - min_len;
+
+ pdu[0] = ATT_OP_WRITE_REQ;
+ att_put_u16(handle, &pdu[1]);
+
+ if (vlen > 0) {
+ memcpy(&pdu[3], value, vlen);
+ return min_len + vlen;
+ }
+
+ return min_len;
+}
+
+uint16_t dec_write_req(const uint8_t *pdu, int len, uint16_t *handle,
+ uint8_t *value, int *vlen)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(*handle);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (value == NULL || vlen == NULL || handle == NULL)
+ return 0;
+
+ if (len < min_len)
+ return 0;
+
+ if (pdu[0] != ATT_OP_WRITE_REQ)
+ return 0;
+
+ *handle = att_get_u16(&pdu[1]);
+ *vlen = len - min_len;
+ if (*vlen > 0)
+ memcpy(value, pdu + min_len, *vlen);
+
+ return len;
+}
+
+uint16_t enc_write_resp(uint8_t *pdu, int len)
+{
+ if (pdu == NULL)
+ return 0;
+
+ pdu[0] = ATT_OP_WRITE_RESP;
+
+ return sizeof(pdu[0]);
+}
+
+uint16_t dec_write_resp(const uint8_t *pdu, int len)
+{
+ if (pdu == NULL)
+ return 0;
+
+ if (pdu[0] != ATT_OP_WRITE_RESP)
+ return 0;
+
+ return len;
+}
+
+uint16_t enc_read_req(uint16_t handle, uint8_t *pdu, int len)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (len < min_len)
+ return 0;
+
+ pdu[0] = ATT_OP_READ_REQ;
+ att_put_u16(handle, &pdu[1]);
+
+ return min_len;
+}
+
+uint16_t enc_read_blob_req(uint16_t handle, uint16_t offset, uint8_t *pdu,
+ int len)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle) +
+ sizeof(offset);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (len < min_len)
+ return 0;
+
+ pdu[0] = ATT_OP_READ_BLOB_REQ;
+ att_put_u16(handle, &pdu[1]);
+ att_put_u16(offset, &pdu[3]);
+
+ return min_len;
+}
+
+uint16_t dec_read_req(const uint8_t *pdu, int len, uint16_t *handle)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(*handle);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (handle == NULL)
+ return 0;
+
+ if (len < min_len)
+ return 0;
+
+ if (pdu[0] != ATT_OP_READ_REQ)
+ return 0;
+
+ *handle = att_get_u16(&pdu[1]);
+
+ return min_len;
+}
+
+uint16_t dec_read_blob_req(const uint8_t *pdu, int len, uint16_t *handle,
+ uint16_t *offset)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(*handle) +
+ sizeof(*offset);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (handle == NULL)
+ return 0;
+
+ if (offset == NULL)
+ return 0;
+
+ if (len < min_len)
+ return 0;
+
+ if (pdu[0] != ATT_OP_READ_BLOB_REQ)
+ return 0;
+
+ *handle = att_get_u16(&pdu[1]);
+ *offset = att_get_u16(&pdu[3]);
+
+ return min_len;
+}
+
+uint16_t enc_read_resp(uint8_t *value, int vlen, uint8_t *pdu, int len)
+{
+ if (pdu == NULL)
+ return 0;
+
+ /* If the attribute value length is longer than the allowed PDU size,
+ * send only the octets that fit on the PDU. The remaining octets can
+ * be requested using the Read Blob Request. */
+ if (vlen > len - 1)
+ vlen = len - 1;
+
+ pdu[0] = ATT_OP_READ_RESP;
+
+ memcpy(pdu + 1, value, vlen);
+
+ return vlen + 1;
+}
+
+uint16_t enc_read_blob_resp(uint8_t *value, int vlen, uint16_t offset,
+ uint8_t *pdu, int len)
+{
+ if (pdu == NULL)
+ return 0;
+
+ vlen -= offset;
+ if (vlen > len - 1)
+ vlen = len - 1;
+
+ pdu[0] = ATT_OP_READ_BLOB_RESP;
+
+ memcpy(pdu + 1, &value[offset], vlen);
+
+ return vlen + 1;
+}
+
+uint16_t dec_read_resp(const uint8_t *pdu, int len, uint8_t *value, int *vlen)
+{
+ if (pdu == NULL)
+ return 0;
+
+ if (value == NULL || vlen == NULL)
+ return 0;
+
+ if (pdu[0] != ATT_OP_READ_RESP)
+ return 0;
+
+ memcpy(value, pdu + 1, len - 1);
+
+ *vlen = len - 1;
+
+ return len;
+}
+
+uint16_t enc_error_resp(uint8_t opcode, uint16_t handle, uint8_t status,
+ uint8_t *pdu, int len)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(opcode) +
+ sizeof(handle) + sizeof(status);
+ uint16_t u16;
+
+ if (len < min_len)
+ return 0;
+
+ u16 = htobs(handle);
+ pdu[0] = ATT_OP_ERROR;
+ pdu[1] = opcode;
+ memcpy(&pdu[2], &u16, sizeof(u16));
+ pdu[4] = status;
+
+ return min_len;
+}
+
+uint16_t enc_find_info_req(uint16_t start, uint16_t end, uint8_t *pdu, int len)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(start) + sizeof(end);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (len < min_len)
+ return 0;
+
+ pdu[0] = ATT_OP_FIND_INFO_REQ;
+ att_put_u16(start, &pdu[1]);
+ att_put_u16(end, &pdu[3]);
+
+ return min_len;
+}
+
+uint16_t dec_find_info_req(const uint8_t *pdu, int len, uint16_t *start,
+ uint16_t *end)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(*start) + sizeof(*end);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (len < min_len)
+ return 0;
+
+ if (start == NULL || end == NULL)
+ return 0;
+
+ if (pdu[0] != ATT_OP_FIND_INFO_REQ)
+ return 0;
+
+ *start = att_get_u16(&pdu[1]);
+ *end = att_get_u16(&pdu[3]);
+
+ return min_len;
+}
+
+uint16_t enc_find_info_resp(uint8_t format, struct att_data_list *list,
+ uint8_t *pdu, int len)
+{
+ uint8_t *ptr;
+ int i, w;
+
+ if (pdu == NULL)
+ return 0;
+
+ if (list == NULL)
+ return 0;
+
+ if (len < list->len + 2)
+ return 0;
+
+ pdu[0] = ATT_OP_FIND_INFO_RESP;
+ pdu[1] = format;
+ ptr = (void *) &pdu[2];
+
+ for (i = 0, w = 2; i < list->num && w + list->len <= len; i++) {
+ memcpy(ptr, list->data[i], list->len);
+ ptr += list->len;
+ w += list->len;
+ }
+
+ return w;
+}
+
+struct att_data_list *dec_find_info_resp(const uint8_t *pdu, int len,
+ uint8_t *format)
+{
+ struct att_data_list *list;
+ uint8_t *ptr;
+ uint16_t elen, num;
+ int i;
+
+ if (pdu == NULL)
+ return 0;
+
+ if (format == NULL)
+ return 0;
+
+ if (pdu[0] != ATT_OP_FIND_INFO_RESP)
+ return 0;
+
+ *format = pdu[1];
+ elen = sizeof(pdu[0]) + sizeof(*format);
+ if (*format == 0x01)
+ elen += 2;
+ else if (*format == 0x02)
+ elen += 16;
+
+ num = (len - 2) / elen;
+
+ ptr = (void *) &pdu[2];
+
+ list = att_data_list_alloc(num, elen);
+
+ for (i = 0; i < num; i++) {
+ memcpy(list->data[i], ptr, list->len);
+ ptr += list->len;
+ }
+
+ return list;
+}
+
+uint16_t enc_notification(struct attribute *a, uint8_t *pdu, int len)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(uint16_t);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (len < (a->len + min_len))
+ return 0;
+
+ pdu[0] = ATT_OP_HANDLE_NOTIFY;
+ att_put_u16(a->handle, &pdu[1]);
+ memcpy(&pdu[3], a->data, a->len);
+
+ return a->len + min_len;
+}
+
+uint16_t enc_indication(struct attribute *a, uint8_t *pdu, int len)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(uint16_t);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (len < (a->len + min_len))
+ return 0;
+
+ pdu[0] = ATT_OP_HANDLE_IND;
+ att_put_u16(a->handle, &pdu[1]);
+ memcpy(&pdu[3], a->data, a->len);
+
+ return a->len + min_len;
+}
+
+struct attribute *dec_indication(const uint8_t *pdu, int len)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(uint16_t);
+
+ struct attribute *a;
+
+ if (pdu == NULL)
+ return NULL;
+
+ if (pdu[0] != ATT_OP_HANDLE_IND)
+ return NULL;
+
+ if (len < min_len)
+ return NULL;
+
+ a = g_malloc0(sizeof(struct attribute) + len - min_len);
+ a->len = len - min_len;
+
+ a->handle = att_get_u16(&pdu[1]);
+ memcpy(a->data, &pdu[3], a->len);
+
+ return a;
+}
+
+uint16_t enc_confirmation(uint8_t *pdu, int len)
+{
+ const uint16_t min_len = sizeof(pdu[0]);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (len < min_len)
+ return 0;
+
+ pdu[0] = ATT_OP_HANDLE_CNF;
+
+ return min_len;
+}
+
+uint16_t enc_mtu_req(uint16_t mtu, uint8_t *pdu, int len)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(mtu);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (len < min_len)
+ return 0;
+
+ pdu[0] = ATT_OP_MTU_REQ;
+ att_put_u16(mtu, &pdu[1]);
+
+ return min_len;
+}
+
+uint16_t dec_mtu_req(const uint8_t *pdu, int len, uint16_t *mtu)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(*mtu);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (mtu == NULL)
+ return 0;
+
+ if (len < min_len)
+ return 0;
+
+ if (pdu[0] != ATT_OP_MTU_REQ)
+ return 0;
+
+ *mtu = att_get_u16(&pdu[1]);
+
+ return min_len;
+}
+
+uint16_t enc_mtu_resp(uint16_t mtu, uint8_t *pdu, int len)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(mtu);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (len < min_len)
+ return 0;
+
+ pdu[0] = ATT_OP_MTU_RESP;
+ att_put_u16(mtu, &pdu[1]);
+
+ return min_len;
+}
+
+uint16_t dec_mtu_resp(const uint8_t *pdu, int len, uint16_t *mtu)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(*mtu);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (mtu == NULL)
+ return 0;
+
+ if (len < min_len)
+ return 0;
+
+ if (pdu[0] != ATT_OP_MTU_RESP)
+ return 0;
+
+ *mtu = att_get_u16(&pdu[1]);
+
+ return min_len;
+}
diff --git a/attrib/att.h b/attrib/att.h
new file mode 100644
index 0000000..7a83bfa
--- /dev/null
+++ b/attrib/att.h
@@ -0,0 +1,306 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2010 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
+ *
+ */
+
+/* GATT Profile Attribute types */
+#define GATT_PRIM_SVC_UUID 0x2800
+#define GATT_SND_SVC_UUID 0x2801
+#define GATT_INCLUDE_UUID 0x2802
+#define GATT_CHARAC_UUID 0x2803
+
+/* GATT Characteristic Types */
+#define GATT_CHARAC_DEVICE_NAME 0x2A00
+#define GATT_CHARAC_APPEARANCE 0x2A01
+#define GATT_CHARAC_PERIPHERAL_PRIV_FLAG 0x2A02
+#define GATT_CHARAC_RECONNECTION_ADDRESS 0x2A03
+#define GATT_CHARAC_PERIPHERAL_PREF_CONN 0x2A04
+#define GATT_CHARAC_SERVICE_CHANGED 0x2A05
+
+/* GATT Characteristic Descriptors */
+#define GATT_CHARAC_EXT_PROPER_UUID 0x2900
+#define GATT_CHARAC_USER_DESC_UUID 0x2901
+#define GATT_CLIENT_CHARAC_CFG_UUID 0x2902
+#define GATT_SERVER_CHARAC_CFG_UUID 0x2903
+#define GATT_CHARAC_FMT_UUID 0x2904
+#define GATT_CHARAC_AGREG_FMT_UUID 0x2905
+
+/* Attribute Protocol Opcodes */
+#define ATT_OP_ERROR 0x01
+#define ATT_OP_MTU_REQ 0x02
+#define ATT_OP_MTU_RESP 0x03
+#define ATT_OP_FIND_INFO_REQ 0x04
+#define ATT_OP_FIND_INFO_RESP 0x05
+#define ATT_OP_FIND_BY_TYPE_REQ 0x06
+#define ATT_OP_FIND_BY_TYPE_RESP 0x07
+#define ATT_OP_READ_BY_TYPE_REQ 0x08
+#define ATT_OP_READ_BY_TYPE_RESP 0x09
+#define ATT_OP_READ_REQ 0x0A
+#define ATT_OP_READ_RESP 0x0B
+#define ATT_OP_READ_BLOB_REQ 0x0C
+#define ATT_OP_READ_BLOB_RESP 0x0D
+#define ATT_OP_READ_MULTI_REQ 0x0E
+#define ATT_OP_READ_MULTI_RESP 0x0F
+#define ATT_OP_READ_BY_GROUP_REQ 0x10
+#define ATT_OP_READ_BY_GROUP_RESP 0x11
+#define ATT_OP_WRITE_REQ 0x12
+#define ATT_OP_WRITE_RESP 0x13
+#define ATT_OP_WRITE_CMD 0x52
+#define ATT_OP_PREP_WRITE_REQ 0x16
+#define ATT_OP_PREP_WRITE_RESP 0x17
+#define ATT_OP_EXEC_WRITE_REQ 0x18
+#define ATT_OP_EXEC_WRITE_RESP 0x19
+#define ATT_OP_HANDLE_NOTIFY 0x1B
+#define ATT_OP_HANDLE_IND 0x1D
+#define ATT_OP_HANDLE_CNF 0x1E
+#define ATT_OP_SIGNED_WRITE_CMD 0xD2
+
+/* Error codes for Error response PDU */
+#define ATT_ECODE_INVALID_HANDLE 0x01
+#define ATT_ECODE_READ_NOT_PERM 0x02
+#define ATT_ECODE_WRITE_NOT_PERM 0x03
+#define ATT_ECODE_INVALID_PDU 0x04
+#define ATT_ECODE_INSUFF_AUTHEN 0x05
+#define ATT_ECODE_REQ_NOT_SUPP 0x06
+#define ATT_ECODE_INVALID_OFFSET 0x07
+#define ATT_ECODE_INSUFF_AUTHO 0x08
+#define ATT_ECODE_PREP_QUEUE_FULL 0x09
+#define ATT_ECODE_ATTR_NOT_FOUND 0x0A
+#define ATT_ECODE_ATTR_NOT_LONG 0x0B
+#define ATT_ECODE_INSUFF_ENCR_KEY_SIZE 0x0C
+#define ATT_ECODE_INVAL_ATTR_VALUE_LEN 0x0D
+#define ATT_ECODE_UNLIKELY 0x0E
+#define ATT_ECODE_INSUFF_ENC 0x0F
+#define ATT_ECODE_UNSUPP_GRP_TYPE 0x10
+#define ATT_ECODE_INSUFF_RESOURCES 0x11
+/* Application error */
+#define ATT_ECODE_IO 0xFF
+
+/* Characteristic Property bit field */
+#define ATT_CHAR_PROPER_BROADCAST 0x01
+#define ATT_CHAR_PROPER_READ 0x02
+#define ATT_CHAR_PROPER_WRITE_WITHOUT_RESP 0x04
+#define ATT_CHAR_PROPER_WRITE 0x08
+#define ATT_CHAR_PROPER_NOTIFY 0x10
+#define ATT_CHAR_PROPER_INDICATE 0x20
+#define ATT_CHAR_PROPER_AUTH 0x40
+#define ATT_CHAR_PROPER_EXT_PROPER 0x80
+
+
+#define ATT_MAX_MTU 256
+#define ATT_DEFAULT_L2CAP_MTU 48
+#define ATT_DEFAULT_LE_MTU 23
+
+/* Requirements for read/write operations */
+enum {
+ ATT_NONE, /* No restrictions */
+ ATT_AUTHENTICATION, /* Authentication required */
+ ATT_AUTHORIZATION, /* Authorization required */
+ ATT_NOT_PERMITTED, /* Operation not permitted */
+};
+
+struct attribute {
+ uint16_t handle;
+ bt_uuid_t uuid;
+ int read_reqs;
+ int write_reqs;
+ uint8_t (*read_cb)(struct attribute *a, gpointer user_data);
+ uint8_t (*write_cb)(struct attribute *a, gpointer user_data);
+ gpointer cb_user_data;
+ int len;
+ uint8_t data[0];
+};
+
+struct att_data_list {
+ uint16_t num;
+ uint16_t len;
+ uint8_t **data;
+};
+
+struct att_range {
+ uint16_t start;
+ uint16_t end;
+};
+
+struct att_primary {
+ char uuid[MAX_LEN_UUID_STR + 1];
+ uint16_t start;
+ uint16_t end;
+};
+
+struct att_char {
+ char uuid[MAX_LEN_UUID_STR + 1];
+ uint16_t handle;
+ uint8_t properties;
+ uint16_t value_handle;
+};
+
+/* These functions do byte conversion */
+static inline uint8_t att_get_u8(const void *ptr)
+{
+ const uint8_t *u8_ptr = ptr;
+ return bt_get_unaligned(u8_ptr);
+}
+
+static inline uint16_t att_get_u16(const void *ptr)
+{
+ const uint16_t *u16_ptr = ptr;
+ return btohs(bt_get_unaligned(u16_ptr));
+}
+
+static inline uint32_t att_get_u32(const void *ptr)
+{
+ const uint32_t *u32_ptr = ptr;
+ return btohl(bt_get_unaligned(u32_ptr));
+}
+
+static inline uint128_t att_get_u128(const void *ptr)
+{
+ const uint128_t *u128_ptr = ptr;
+ uint128_t dst;
+
+ btoh128(u128_ptr, &dst);
+
+ return dst;
+}
+
+static inline void att_put_u8(uint8_t src, void *dst)
+{
+ bt_put_unaligned(src, (uint8_t *) dst);
+}
+
+static inline void att_put_u16(uint16_t src, void *dst)
+{
+ bt_put_unaligned(htobs(src), (uint16_t *) dst);
+}
+
+static inline void att_put_u32(uint32_t src, void *dst)
+{
+ bt_put_unaligned(htobl(src), (uint32_t *) dst);
+}
+
+static inline void att_put_u128(uint128_t src, void *dst)
+{
+ uint128_t *d128 = dst;
+
+ htob128(&src, d128);
+}
+
+static inline void att_put_uuid16(bt_uuid_t src, void *dst)
+{
+ att_put_u16(src.value.u16, dst);
+}
+
+static inline void att_put_uuid128(bt_uuid_t src, void *dst)
+{
+ att_put_u128(src.value.u128, dst);
+}
+
+static inline void att_put_uuid(bt_uuid_t src, void *dst)
+{
+ if (src.type == BT_UUID16)
+ att_put_uuid16(src, dst);
+ else
+ att_put_uuid128(src, dst);
+}
+
+static inline bt_uuid_t att_get_uuid16(const void *ptr)
+{
+ bt_uuid_t uuid;
+
+ bt_uuid16_create(&uuid, att_get_u16(ptr));
+
+ return uuid;
+}
+
+static inline bt_uuid_t att_get_uuid128(const void *ptr)
+{
+ bt_uuid_t uuid;
+ uint128_t value;
+
+ value = att_get_u128(ptr);
+ bt_uuid128_create(&uuid, value);
+
+ return uuid;
+}
+
+struct att_data_list *att_data_list_alloc(uint16_t num, uint16_t len);
+void att_data_list_free(struct att_data_list *list);
+
+const char *att_ecode2str(uint8_t status);
+uint16_t enc_read_by_grp_req(uint16_t start, uint16_t end, bt_uuid_t *uuid,
+ uint8_t *pdu, int len);
+uint16_t dec_read_by_grp_req(const uint8_t *pdu, int len, uint16_t *start,
+ uint16_t *end, bt_uuid_t *uuid);
+uint16_t enc_read_by_grp_resp(struct att_data_list *list, uint8_t *pdu, int len);
+uint16_t enc_find_by_type_req(uint16_t start, uint16_t end, bt_uuid_t *uuid,
+ const uint8_t *value, int vlen, uint8_t *pdu, int len);
+uint16_t dec_find_by_type_req(const uint8_t *pdu, int len, uint16_t *start,
+ uint16_t *end, bt_uuid_t *uuid, uint8_t *value, int *vlen);
+uint16_t enc_find_by_type_resp(GSList *ranges, uint8_t *pdu, int len);
+GSList *dec_find_by_type_resp(const uint8_t *pdu, int len);
+struct att_data_list *dec_read_by_grp_resp(const uint8_t *pdu, int len);
+uint16_t enc_read_by_type_req(uint16_t start, uint16_t end, bt_uuid_t *uuid,
+ uint8_t *pdu, int len);
+uint16_t dec_read_by_type_req(const uint8_t *pdu, int len, uint16_t *start,
+ uint16_t *end, bt_uuid_t *uuid);
+uint16_t enc_read_by_type_resp(struct att_data_list *list, uint8_t *pdu,
+ int len);
+uint16_t enc_write_cmd(uint16_t handle, const uint8_t *value, int vlen,
+ uint8_t *pdu, int len);
+uint16_t dec_write_cmd(const uint8_t *pdu, int len, uint16_t *handle,
+ uint8_t *value, int *vlen);
+struct att_data_list *dec_read_by_type_resp(const uint8_t *pdu, int len);
+uint16_t enc_write_req(uint16_t handle, const uint8_t *value, int vlen,
+ uint8_t *pdu, int len);
+uint16_t dec_write_req(const uint8_t *pdu, int len, uint16_t *handle,
+ uint8_t *value, int *vlen);
+uint16_t enc_write_resp(uint8_t *pdu, int len);
+uint16_t dec_write_resp(const uint8_t *pdu, int len);
+uint16_t enc_read_req(uint16_t handle, uint8_t *pdu, int len);
+uint16_t enc_read_blob_req(uint16_t handle, uint16_t offset, uint8_t *pdu,
+ int len);
+uint16_t dec_read_req(const uint8_t *pdu, int len, uint16_t *handle);
+uint16_t dec_read_blob_req(const uint8_t *pdu, int len, uint16_t *handle,
+ uint16_t *offset);
+uint16_t enc_read_resp(uint8_t *value, int vlen, uint8_t *pdu, int len);
+uint16_t enc_read_blob_resp(uint8_t *value, int vlen, uint16_t offset,
+ uint8_t *pdu, int len);
+uint16_t dec_read_resp(const uint8_t *pdu, int len, uint8_t *value, int *vlen);
+uint16_t enc_error_resp(uint8_t opcode, uint16_t handle, uint8_t status,
+ uint8_t *pdu, int len);
+uint16_t enc_find_info_req(uint16_t start, uint16_t end, uint8_t *pdu, int len);
+uint16_t dec_find_info_req(const uint8_t *pdu, int len, uint16_t *start,
+ uint16_t *end);
+uint16_t enc_find_info_resp(uint8_t format, struct att_data_list *list,
+ uint8_t *pdu, int len);
+struct att_data_list *dec_find_info_resp(const uint8_t *pdu, int len,
+ uint8_t *format);
+uint16_t enc_notification(struct attribute *a, uint8_t *pdu, int len);
+uint16_t enc_indication(struct attribute *a, uint8_t *pdu, int len);
+struct attribute *dec_indication(const uint8_t *pdu, int len);
+uint16_t enc_confirmation(uint8_t *pdu, int len);
+
+uint16_t enc_mtu_req(uint16_t mtu, uint8_t *pdu, int len);
+uint16_t dec_mtu_req(const uint8_t *pdu, int len, uint16_t *mtu);
+uint16_t enc_mtu_resp(uint16_t mtu, uint8_t *pdu, int len);
+uint16_t dec_mtu_resp(const uint8_t *pdu, int len, uint16_t *mtu);
diff --git a/attrib/client.c b/attrib/client.c
new file mode 100644
index 0000000..54bdc79
--- /dev/null
+++ b/attrib/client.c
@@ -0,0 +1,1116 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2010 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 <errno.h>
+#include <stdlib.h>
+#include <glib.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/uuid.h>
+
+#include "adapter.h"
+#include "device.h"
+#include "log.h"
+#include "gdbus.h"
+#include "error.h"
+#include "dbus-common.h"
+#include "btio.h"
+#include "storage.h"
+
+#include "att.h"
+#include "gattrib.h"
+#include "gatt.h"
+#include "client.h"
+
+#define CHAR_INTERFACE "org.bluez.Characteristic"
+
+struct gatt_service {
+ struct btd_device *dev;
+ bdaddr_t sba;
+ bdaddr_t dba;
+ char *path;
+ GSList *primary;
+ GAttrib *attrib;
+ DBusMessage *msg;
+ int psm;
+ gboolean listen;
+};
+
+struct format {
+ guint8 format;
+ guint8 exponent;
+ guint16 unit;
+ guint8 namespace;
+ guint16 desc;
+} __attribute__ ((packed));
+
+struct primary {
+ struct gatt_service *gatt;
+ struct att_primary *att;
+ char *path;
+ GSList *chars;
+ GSList *watchers;
+};
+
+struct characteristic {
+ struct primary *prim;
+ char *path;
+ uint16_t handle;
+ uint16_t end;
+ uint8_t perm;
+ char type[MAX_LEN_UUID_STR + 1];
+ char *name;
+ char *desc;
+ struct format *format;
+ uint8_t *value;
+ size_t vlen;
+};
+
+struct query_data {
+ struct primary *prim;
+ struct characteristic *chr;
+ DBusMessage *msg;
+ uint16_t handle;
+};
+
+struct watcher {
+ guint id;
+ char *name;
+ char *path;
+ struct primary *prim;
+};
+
+static GSList *gatt_services = NULL;
+
+static DBusConnection *connection;
+
+static void characteristic_free(void *user_data)
+{
+ struct characteristic *chr = user_data;
+
+ g_free(chr->path);
+ g_free(chr->desc);
+ g_free(chr->format);
+ g_free(chr->value);
+ g_free(chr->name);
+ g_free(chr);
+}
+
+static void watcher_free(void *user_data)
+{
+ struct watcher *watcher = user_data;
+
+ g_free(watcher->path);
+ g_free(watcher->name);
+ g_free(watcher);
+}
+
+static void primary_free(void *user_data)
+{
+ struct primary *prim = user_data;
+ GSList *l;
+
+ for (l = prim->watchers; l; l = l->next) {
+ struct watcher *watcher = l->data;
+ g_dbus_remove_watch(connection, watcher->id);
+ }
+
+ g_slist_foreach(prim->chars, (GFunc) characteristic_free, NULL);
+ g_slist_free(prim->chars);
+ g_free(prim->path);
+ g_free(prim);
+}
+
+static void gatt_service_free(void *user_data)
+{
+ struct gatt_service *gatt = user_data;
+
+ g_slist_foreach(gatt->primary, (GFunc) primary_free, NULL);
+ g_slist_free(gatt->primary);
+ g_attrib_unref(gatt->attrib);
+ g_free(gatt->path);
+ btd_device_unref(gatt->dev);
+ g_free(gatt);
+}
+
+static int gatt_dev_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct gatt_service *gatt = a;
+ const struct btd_device *dev = b;
+
+ return gatt->dev != dev;
+}
+
+static int characteristic_handle_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct characteristic *chr = a;
+ uint16_t handle = GPOINTER_TO_UINT(b);
+
+ return chr->handle - handle;
+}
+
+static int watcher_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct watcher *watcher = a;
+ const struct watcher *match = b;
+ int ret;
+
+ ret = g_strcmp0(watcher->name, match->name);
+ if (ret != 0)
+ return ret;
+
+ return g_strcmp0(watcher->path, match->path);
+}
+
+static void append_char_dict(DBusMessageIter *iter, struct characteristic *chr)
+{
+ DBusMessageIter dict;
+ const char *name = "";
+ char *uuid;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ uuid = g_strdup(chr->type);
+ dict_append_entry(&dict, "UUID", DBUS_TYPE_STRING, &uuid);
+ g_free(uuid);
+
+ /* FIXME: Translate UUID to name. */
+ dict_append_entry(&dict, "Name", DBUS_TYPE_STRING, &name);
+
+ if (chr->desc)
+ dict_append_entry(&dict, "Description", DBUS_TYPE_STRING,
+ &chr->desc);
+
+ if (chr->value)
+ dict_append_array(&dict, "Value", DBUS_TYPE_BYTE, &chr->value,
+ chr->vlen);
+
+ /* FIXME: Missing Format, Value and Representation */
+
+ dbus_message_iter_close_container(iter, &dict);
+}
+
+static void watcher_exit(DBusConnection *conn, void *user_data)
+{
+ struct watcher *watcher = user_data;
+ struct primary *prim = watcher->prim;
+ struct gatt_service *gatt = prim->gatt;
+
+ DBG("%s watcher %s exited", prim->path, watcher->name);
+
+ prim->watchers = g_slist_remove(prim->watchers, watcher);
+
+ g_attrib_unref(gatt->attrib);
+}
+
+static int characteristic_set_value(struct characteristic *chr,
+ const uint8_t *value, size_t vlen)
+{
+ chr->value = g_try_realloc(chr->value, vlen);
+ if (chr->value == NULL)
+ return -ENOMEM;
+
+ memcpy(chr->value, value, vlen);
+ chr->vlen = vlen;
+
+ return 0;
+}
+
+static void update_watchers(gpointer data, gpointer user_data)
+{
+ struct watcher *w = data;
+ struct characteristic *chr = user_data;
+ DBusMessage *msg;
+
+ msg = dbus_message_new_method_call(w->name, w->path,
+ "org.bluez.Watcher", "ValueChanged");
+ if (msg == NULL)
+ return;
+
+ dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &chr->path,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
+ &chr->value, chr->vlen, DBUS_TYPE_INVALID);
+
+ dbus_message_set_no_reply(msg, TRUE);
+ g_dbus_send_message(connection, msg);
+}
+
+static void events_handler(const uint8_t *pdu, uint16_t len,
+ gpointer user_data)
+{
+ struct gatt_service *gatt = user_data;
+ struct characteristic *chr;
+ struct primary *prim;
+ GSList *lprim, *lchr;
+ uint8_t opdu[ATT_MAX_MTU];
+ guint handle;
+ uint16_t olen;
+
+ if (len < 3) {
+ DBG("Malformed notification/indication packet (opcode 0x%02x)",
+ pdu[0]);
+ return;
+ }
+
+ handle = att_get_u16(&pdu[1]);
+
+ for (lprim = gatt->primary, prim = NULL, chr = NULL; lprim;
+ lprim = lprim->next) {
+ prim = lprim->data;
+
+ lchr = g_slist_find_custom(prim->chars,
+ GUINT_TO_POINTER(handle), characteristic_handle_cmp);
+ if (lchr) {
+ chr = lchr->data;
+ break;
+ }
+ }
+
+ if (chr == NULL) {
+ DBG("Attribute handle 0x%02x not found", handle);
+ return;
+ }
+
+ switch (pdu[0]) {
+ case ATT_OP_HANDLE_IND:
+ olen = enc_confirmation(opdu, sizeof(opdu));
+ g_attrib_send(gatt->attrib, 0, opdu[0], opdu, olen,
+ NULL, NULL, NULL);
+ case ATT_OP_HANDLE_NOTIFY:
+ if (characteristic_set_value(chr, &pdu[3], len - 3) < 0)
+ DBG("Can't change Characteristic 0x%02x", handle);
+
+ g_slist_foreach(prim->watchers, update_watchers, chr);
+ break;
+ }
+}
+
+static void attrib_destroy(gpointer user_data)
+{
+ struct gatt_service *gatt = user_data;
+
+ gatt->attrib = NULL;
+}
+
+static void attrib_disconnect(gpointer user_data)
+{
+ struct gatt_service *gatt = user_data;
+
+ /* Remote initiated disconnection only */
+ g_attrib_unref(gatt->attrib);
+}
+
+static void connect_cb(GIOChannel *chan, GError *gerr, gpointer user_data)
+{
+ struct gatt_service *gatt = user_data;
+
+ if (gerr) {
+ if (gatt->msg) {
+ DBusMessage *reply = btd_error_failed(gatt->msg,
+ gerr->message);
+ g_dbus_send_message(connection, reply);
+ }
+
+ error("%s", gerr->message);
+ goto fail;
+ }
+
+ if (gatt->attrib == NULL)
+ return;
+
+ /* Listen mode: used for notification and indication */
+ if (gatt->listen == TRUE) {
+ g_attrib_register(gatt->attrib,
+ ATT_OP_HANDLE_NOTIFY,
+ events_handler, gatt, NULL);
+ g_attrib_register(gatt->attrib,
+ ATT_OP_HANDLE_IND,
+ events_handler, gatt, NULL);
+ return;
+ }
+
+ return;
+fail:
+ g_attrib_unref(gatt->attrib);
+}
+
+static int l2cap_connect(struct gatt_service *gatt, GError **gerr,
+ gboolean listen)
+{
+ GIOChannel *io;
+
+ if (gatt->attrib != NULL) {
+ gatt->attrib = g_attrib_ref(gatt->attrib);
+ gatt->listen = listen;
+ return 0;
+ }
+
+ /*
+ * FIXME: If the service doesn't support Client Characteristic
+ * Configuration it is necessary to poll the server from time
+ * to time checking for modifications.
+ */
+ if (gatt->psm < 0)
+ io = bt_io_connect(BT_IO_L2CAP, connect_cb, gatt, NULL, gerr,
+ BT_IO_OPT_SOURCE_BDADDR, &gatt->sba,
+ BT_IO_OPT_DEST_BDADDR, &gatt->dba,
+ BT_IO_OPT_CID, GATT_CID,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_INVALID);
+ else
+ io = bt_io_connect(BT_IO_L2CAP, connect_cb, gatt, NULL, gerr,
+ BT_IO_OPT_SOURCE_BDADDR, &gatt->sba,
+ BT_IO_OPT_DEST_BDADDR, &gatt->dba,
+ BT_IO_OPT_PSM, gatt->psm,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_INVALID);
+ if (!io)
+ return -1;
+
+ gatt->attrib = g_attrib_new(io);
+ g_io_channel_unref(io);
+ gatt->listen = listen;
+
+ g_attrib_set_destroy_function(gatt->attrib, attrib_destroy, gatt);
+ g_attrib_set_disconnect_function(gatt->attrib, attrib_disconnect,
+ gatt);
+
+ return 0;
+}
+
+static DBusMessage *register_watcher(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ const char *sender = dbus_message_get_sender(msg);
+ struct primary *prim = data;
+ struct watcher *watcher;
+ GError *gerr = NULL;
+ char *path;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ if (l2cap_connect(prim->gatt, &gerr, TRUE) < 0) {
+ DBusMessage *reply = btd_error_failed(msg, gerr->message);
+ g_error_free(gerr);
+ return reply;
+ }
+
+ watcher = g_new0(struct watcher, 1);
+ watcher->name = g_strdup(sender);
+ watcher->prim = prim;
+ watcher->path = g_strdup(path);
+ watcher->id = g_dbus_add_disconnect_watch(conn, sender, watcher_exit,
+ watcher, watcher_free);
+
+ prim->watchers = g_slist_append(prim->watchers, watcher);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_watcher(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ const char *sender = dbus_message_get_sender(msg);
+ struct primary *prim = data;
+ struct watcher *watcher, *match;
+ GSList *l;
+ char *path;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ match = g_new0(struct watcher, 1);
+ match->name = g_strdup(sender);
+ match->path = g_strdup(path);
+ l = g_slist_find_custom(prim->watchers, match, watcher_cmp);
+ watcher_free(match);
+ if (!l)
+ return btd_error_not_authorized(msg);
+
+ watcher = l->data;
+ g_dbus_remove_watch(conn, watcher->id);
+ prim->watchers = g_slist_remove(prim->watchers, watcher);
+ watcher_free(watcher);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *set_value(DBusConnection *conn, DBusMessage *msg,
+ DBusMessageIter *iter, struct characteristic *chr)
+{
+ struct gatt_service *gatt = chr->prim->gatt;
+ DBusMessageIter sub;
+ GError *gerr = NULL;
+ uint8_t *value;
+ int len;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY ||
+ dbus_message_iter_get_element_type(iter) != DBUS_TYPE_BYTE)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_recurse(iter, &sub);
+
+ dbus_message_iter_get_fixed_array(&sub, &value, &len);
+
+ if (l2cap_connect(gatt, &gerr, FALSE) < 0) {
+ DBusMessage *reply = btd_error_failed(msg, gerr->message);
+ g_error_free(gerr);
+ return reply;
+ }
+
+ gatt_write_cmd(gatt->attrib, chr->handle, value, len, NULL, NULL);
+
+ characteristic_set_value(chr, value, len);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *get_properties(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct characteristic *chr = data;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ append_char_dict(&iter, chr);
+
+ return reply;
+}
+
+static DBusMessage *set_property(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct characteristic *chr = data;
+ DBusMessageIter iter;
+ DBusMessageIter sub;
+ const char *property;
+
+ if (!dbus_message_iter_init(msg, &iter))
+ return btd_error_invalid_args(msg);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&iter, &property);
+ dbus_message_iter_next(&iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ if (g_str_equal("Value", property))
+ return set_value(conn, msg, &sub, chr);
+
+ return btd_error_invalid_args(msg);
+}
+
+static GDBusMethodTable char_methods[] = {
+ { "GetProperties", "", "a{sv}", get_properties },
+ { "SetProperty", "sv", "", set_property,
+ G_DBUS_METHOD_FLAG_ASYNC},
+ { }
+};
+
+static char *characteristic_list_to_string(GSList *chars)
+{
+ GString *characteristics;
+ GSList *l;
+
+ characteristics = g_string_new(NULL);
+
+ for (l = chars; l; l = l->next) {
+ struct characteristic *chr = l->data;
+ char chr_str[64];
+
+ memset(chr_str, 0, sizeof(chr_str));
+
+ snprintf(chr_str, sizeof(chr_str), "%04X#%02X#%04X#%s ",
+ chr->handle, chr->perm, chr->end, chr->type);
+
+ characteristics = g_string_append(characteristics, chr_str);
+ }
+
+ return g_string_free(characteristics, FALSE);
+}
+
+static void store_characteristics(struct gatt_service *gatt,
+ struct primary *prim)
+{
+ char *characteristics;
+ struct att_primary *att = prim->att;
+
+ characteristics = characteristic_list_to_string(prim->chars);
+
+ write_device_characteristics(&gatt->sba, &gatt->dba, att->start,
+ characteristics);
+
+ g_free(characteristics);
+}
+
+static void register_characteristics(struct primary *prim)
+{
+ GSList *lc;
+
+ for (lc = prim->chars; lc; lc = lc->next) {
+ struct characteristic *chr = lc->data;
+ g_dbus_register_interface(connection, chr->path,
+ CHAR_INTERFACE, char_methods,
+ NULL, NULL, chr, NULL);
+ DBG("Registered: %s", chr->path);
+ }
+}
+
+static GSList *string_to_characteristic_list(struct primary *prim,
+ const char *str)
+{
+ GSList *l = NULL;
+ char **chars;
+ int i;
+
+ if (str == NULL)
+ return NULL;
+
+ chars = g_strsplit(str, " ", 0);
+ if (chars == NULL)
+ return NULL;
+
+ for (i = 0; chars[i]; i++) {
+ struct characteristic *chr;
+ int ret;
+
+ chr = g_new0(struct characteristic, 1);
+
+ ret = sscanf(chars[i], "%04hX#%02hhX#%04hX#%s", &chr->handle,
+ &chr->perm, &chr->end, chr->type);
+ if (ret < 4) {
+ g_free(chr);
+ continue;
+ }
+
+ chr->prim = prim;
+ chr->path = g_strdup_printf("%s/characteristic%04x",
+ prim->path, chr->handle);
+
+ l = g_slist_append(l, chr);
+ }
+
+ g_strfreev(chars);
+
+ return l;
+}
+
+static void load_characteristics(gpointer data, gpointer user_data)
+{
+ struct primary *prim = data;
+ struct att_primary *att = prim->att;
+ struct gatt_service *gatt = user_data;
+ GSList *chrs_list;
+ char *str;
+
+ if (prim->chars) {
+ DBG("Characteristics already loaded");
+ return;
+ }
+
+ str = read_device_characteristics(&gatt->sba, &gatt->dba, att->start);
+ if (str == NULL)
+ return;
+
+ chrs_list = string_to_characteristic_list(prim, str);
+
+ free(str);
+
+ if (chrs_list == NULL)
+ return;
+
+ prim->chars = chrs_list;
+ register_characteristics(prim);
+
+ return;
+}
+
+static void store_attribute(struct gatt_service *gatt, uint16_t handle,
+ uint16_t type, uint8_t *value, gsize len)
+{
+ bt_uuid_t uuid;
+ char *str, *tmp;
+ guint i;
+
+ str = g_malloc0(MAX_LEN_UUID_STR + len * 2 + 1);
+
+ bt_uuid16_create(&uuid, type);
+ bt_uuid_to_string(&uuid, str, MAX_LEN_UUID_STR);
+
+ str[MAX_LEN_UUID_STR - 1] = '#';
+
+ for (i = 0, tmp = str + MAX_LEN_UUID_STR; i < len; i++, tmp += 2)
+ sprintf(tmp, "%02X", value[i]);
+
+ write_device_attribute(&gatt->sba, &gatt->dba, handle, str);
+ g_free(str);
+}
+
+static void update_char_desc(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ struct query_data *current = user_data;
+ struct gatt_service *gatt = current->prim->gatt;
+ struct characteristic *chr = current->chr;
+
+ if (status == 0) {
+
+ g_free(chr->desc);
+
+ chr->desc = g_malloc(len);
+ memcpy(chr->desc, pdu + 1, len - 1);
+ chr->desc[len - 1] = '\0';
+
+ store_attribute(gatt, current->handle,
+ GATT_CHARAC_USER_DESC_UUID,
+ (void *) chr->desc, len);
+ } else if (status == ATT_ECODE_INSUFF_ENC) {
+ GIOChannel *io = g_attrib_get_channel(gatt->attrib);
+
+ if (bt_io_set(io, BT_IO_L2CAP, NULL,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_HIGH,
+ BT_IO_OPT_INVALID)) {
+ gatt_read_char(gatt->attrib, current->handle, 0,
+ update_char_desc, current);
+ return;
+ }
+ }
+
+ g_attrib_unref(gatt->attrib);
+ g_free(current);
+}
+
+static void update_char_format(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ struct query_data *current = user_data;
+ struct gatt_service *gatt = current->prim->gatt;
+ struct characteristic *chr = current->chr;
+
+ if (status != 0)
+ goto done;
+
+ if (len < 8)
+ goto done;
+
+ g_free(chr->format);
+
+ chr->format = g_new0(struct format, 1);
+ memcpy(chr->format, pdu + 1, 7);
+
+ store_attribute(gatt, current->handle, GATT_CHARAC_FMT_UUID,
+ (void *) chr->format, sizeof(*chr->format));
+
+done:
+ g_attrib_unref(gatt->attrib);
+ g_free(current);
+}
+
+static void update_char_value(guint8 status, const guint8 *pdu,
+ guint16 len, gpointer user_data)
+{
+ struct query_data *current = user_data;
+ struct gatt_service *gatt = current->prim->gatt;
+ struct characteristic *chr = current->chr;
+
+ if (status == 0)
+ characteristic_set_value(chr, pdu + 1, len - 1);
+ else if (status == ATT_ECODE_INSUFF_ENC) {
+ GIOChannel *io = g_attrib_get_channel(gatt->attrib);
+
+ if (bt_io_set(io, BT_IO_L2CAP, NULL,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_HIGH,
+ BT_IO_OPT_INVALID)) {
+ gatt_read_char(gatt->attrib, chr->handle, 0,
+ update_char_value, current);
+ return;
+ }
+ }
+
+ g_attrib_unref(gatt->attrib);
+ g_free(current);
+}
+
+static int uuid_desc16_cmp(bt_uuid_t *uuid, guint16 desc)
+{
+ bt_uuid_t u16;
+
+ bt_uuid16_create(&u16, desc);
+
+ return bt_uuid_cmp(uuid, &u16);
+}
+
+static void descriptor_cb(guint8 status, const guint8 *pdu, guint16 plen,
+ gpointer user_data)
+{
+ struct query_data *current = user_data;
+ struct gatt_service *gatt = current->prim->gatt;
+ struct att_data_list *list;
+ guint8 format;
+ int i;
+
+ if (status != 0)
+ goto done;
+
+ DBG("Find Information Response received");
+
+ list = dec_find_info_resp(pdu, plen, &format);
+ if (list == NULL)
+ goto done;
+
+ for (i = 0; i < list->num; i++) {
+ guint16 handle;
+ bt_uuid_t uuid;
+ uint8_t *info = list->data[i];
+ struct query_data *qfmt;
+
+ handle = att_get_u16(info);
+
+ if (format == 0x01) {
+ uuid = att_get_uuid16(&info[2]);
+ } else {
+ /* Currently, only "user description" and "presentation
+ * format" descriptors are used, and both have 16-bit
+ * UUIDs. Therefore there is no need to support format
+ * 0x02 yet. */
+ continue;
+ }
+ qfmt = g_new0(struct query_data, 1);
+ qfmt->prim = current->prim;
+ qfmt->chr = current->chr;
+ qfmt->handle = handle;
+
+ if (uuid_desc16_cmp(&uuid, GATT_CHARAC_USER_DESC_UUID) == 0) {
+ gatt->attrib = g_attrib_ref(gatt->attrib);
+ gatt_read_char(gatt->attrib, handle, 0, update_char_desc,
+ qfmt);
+ } else if (uuid_desc16_cmp(&uuid, GATT_CHARAC_FMT_UUID) == 0) {
+ gatt->attrib = g_attrib_ref(gatt->attrib);
+ gatt_read_char(gatt->attrib, handle, 0,
+ update_char_format, qfmt);
+ } else
+ g_free(qfmt);
+ }
+
+ att_data_list_free(list);
+done:
+ g_attrib_unref(gatt->attrib);
+ g_free(current);
+}
+
+static void update_all_chars(gpointer data, gpointer user_data)
+{
+ struct query_data *qdesc, *qvalue;
+ struct characteristic *chr = data;
+ struct primary *prim = user_data;
+ struct gatt_service *gatt = prim->gatt;
+
+ qdesc = g_new0(struct query_data, 1);
+ qdesc->prim = prim;
+ qdesc->chr = chr;
+
+ gatt->attrib = g_attrib_ref(gatt->attrib);
+ gatt_find_info(gatt->attrib, chr->handle + 1, chr->end, descriptor_cb,
+ qdesc);
+
+ qvalue = g_new0(struct query_data, 1);
+ qvalue->prim = prim;
+ qvalue->chr = chr;
+
+ gatt->attrib = g_attrib_ref(gatt->attrib);
+ gatt_read_char(gatt->attrib, chr->handle, 0, update_char_value, qvalue);
+}
+
+static void char_discovered_cb(GSList *characteristics, guint8 status,
+ gpointer user_data)
+{
+ DBusMessage *reply;
+ DBusMessageIter iter, array_iter;
+ struct query_data *current = user_data;
+ struct primary *prim = current->prim;
+ struct att_primary *att = prim->att;
+ struct gatt_service *gatt = prim->gatt;
+ uint16_t *previous_end = NULL;
+ GSList *l;
+
+ if (status != 0) {
+ const char *str = att_ecode2str(status);
+
+ DBG("Discover all characteristics failed: %s", str);
+ reply = btd_error_failed(current->msg, str);
+ goto fail;
+ }
+
+ for (l = characteristics; l; l = l->next) {
+ struct att_char *current_chr = l->data;
+ struct characteristic *chr;
+ guint handle = current_chr->value_handle;
+ GSList *lchr;
+
+ lchr = g_slist_find_custom(prim->chars,
+ GUINT_TO_POINTER(handle), characteristic_handle_cmp);
+ if (lchr)
+ continue;
+
+ chr = g_new0(struct characteristic, 1);
+ chr->prim = prim;
+ chr->perm = current_chr->properties;
+ chr->handle = current_chr->value_handle;
+ chr->path = g_strdup_printf("%s/characteristic%04x",
+ prim->path, chr->handle);
+ strncpy(chr->type, current_chr->uuid, sizeof(chr->type));
+
+ if (previous_end)
+ *previous_end = current_chr->handle;
+
+ previous_end = &chr->end;
+
+ prim->chars = g_slist_append(prim->chars, chr);
+ }
+
+ if (previous_end)
+ *previous_end = att->end;
+
+ store_characteristics(gatt, prim);
+ register_characteristics(prim);
+
+ reply = dbus_message_new_method_return(current->msg);
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_OBJECT_PATH_AS_STRING, &array_iter);
+
+ for (l = prim->chars; l; l = l->next) {
+ struct characteristic *chr = l->data;
+
+ dbus_message_iter_append_basic(&array_iter,
+ DBUS_TYPE_OBJECT_PATH, &chr->path);
+ }
+
+ dbus_message_iter_close_container(&iter, &array_iter);
+
+ g_slist_foreach(prim->chars, update_all_chars, prim);
+
+fail:
+ g_dbus_send_message(connection, reply);
+ g_attrib_unref(gatt->attrib);
+ g_free(current);
+}
+
+static DBusMessage *discover_char(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct primary *prim = data;
+ struct att_primary *att = prim->att;
+ struct gatt_service *gatt = prim->gatt;
+ struct query_data *qchr;
+ GError *gerr = NULL;
+
+ if (l2cap_connect(prim->gatt, &gerr, FALSE) < 0) {
+ DBusMessage *reply = btd_error_failed(msg, gerr->message);
+ g_error_free(gerr);
+ return reply;
+ }
+
+ qchr = g_new0(struct query_data, 1);
+ qchr->prim = prim;
+ qchr->msg = dbus_message_ref(msg);
+
+ gatt_discover_char(gatt->attrib, att->start, att->end, NULL,
+ char_discovered_cb, qchr);
+
+ return NULL;
+}
+
+static DBusMessage *prim_get_properties(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct primary *prim = data;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ GSList *l;
+ char **chars;
+ const char *uuid;
+ int i;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ chars = g_new0(char *, g_slist_length(prim->chars) + 1);
+
+ for (i = 0, l = prim->chars; l; l = l->next, i++) {
+ struct characteristic *chr = l->data;
+ chars[i] = chr->path;
+ }
+
+ dict_append_array(&dict, "Characteristics", DBUS_TYPE_OBJECT_PATH,
+ &chars, i);
+ uuid = prim->att->uuid;
+ dict_append_entry(&dict, "UUID", DBUS_TYPE_STRING, &uuid);
+
+ g_free(chars);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static GDBusMethodTable prim_methods[] = {
+ { "DiscoverCharacteristics", "", "ao", discover_char,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "RegisterCharacteristicsWatcher", "o", "",
+ register_watcher },
+ { "UnregisterCharacteristicsWatcher", "o", "",
+ unregister_watcher },
+ { "GetProperties", "", "a{sv}",prim_get_properties },
+ { }
+};
+
+static void register_primaries(struct gatt_service *gatt, GSList *primaries)
+{
+ GSList *l;
+
+ for (l = primaries; l; l = l->next) {
+ struct att_primary *att = l->data;
+ struct primary *prim;
+
+ prim = g_new0(struct primary, 1);
+ prim->att = att;
+ prim->gatt = gatt;
+ prim->path = g_strdup_printf("%s/service%04x", gatt->path,
+ att->start);
+
+ g_dbus_register_interface(connection, prim->path,
+ CHAR_INTERFACE, prim_methods,
+ NULL, NULL, prim, NULL);
+ DBG("Registered: %s", prim->path);
+
+ gatt->primary = g_slist_append(gatt->primary, prim);
+ btd_device_add_service(gatt->dev, prim->path);
+ load_characteristics(prim, gatt);
+ }
+}
+
+int attrib_client_register(struct btd_device *device, int psm)
+{
+ struct btd_adapter *adapter = device_get_adapter(device);
+ const char *path = device_get_path(device);
+ struct gatt_service *gatt;
+ GSList *primaries = btd_device_get_primaries(device);
+ bdaddr_t sba, dba;
+
+ adapter_get_address(adapter, &sba);
+ device_get_address(device, &dba);
+
+ gatt = g_new0(struct gatt_service, 1);
+ gatt->dev = btd_device_ref(device);
+ gatt->listen = FALSE;
+ gatt->path = g_strdup(path);
+ bacpy(&gatt->sba, &sba);
+ bacpy(&gatt->dba, &dba);
+ gatt->psm = psm;
+
+ register_primaries(gatt, primaries);
+
+ gatt_services = g_slist_append(gatt_services, gatt);
+
+ return 0;
+}
+
+void attrib_client_unregister(struct btd_device *device)
+{
+ struct gatt_service *gatt;
+ GSList *l, *lp, *lc;
+
+ l = g_slist_find_custom(gatt_services, device, gatt_dev_cmp);
+ if (!l)
+ return;
+
+ gatt = l->data;
+ gatt_services = g_slist_remove(gatt_services, gatt);
+
+ for (lp = gatt->primary; lp; lp = lp->next) {
+ struct primary *prim = lp->data;
+ for (lc = prim->chars; lc; lc = lc->next) {
+ struct characteristic *chr = lc->data;
+ g_dbus_unregister_interface(connection, chr->path,
+ CHAR_INTERFACE);
+ }
+ g_dbus_unregister_interface(connection, prim->path,
+ CHAR_INTERFACE);
+ }
+
+ gatt_service_free(gatt);
+}
+
+int attrib_client_init(DBusConnection *conn)
+{
+
+ connection = dbus_connection_ref(conn);
+
+ /*
+ * FIXME: if the adapter supports BLE start scanning. Temporary
+ * solution, this approach doesn't allow to control scanning based
+ * on the discoverable property.
+ */
+
+ return 0;
+}
+
+void attrib_client_exit(void)
+{
+ dbus_connection_unref(connection);
+}
diff --git a/attrib/client.h b/attrib/client.h
new file mode 100644
index 0000000..50e2b5f
--- /dev/null
+++ b/attrib/client.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2010 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
+ *
+ */
+
+int attrib_client_init(DBusConnection *conn);
+void attrib_client_exit(void);
+int attrib_client_register(struct btd_device *device, int psm);
+void attrib_client_unregister(struct btd_device *device);
diff --git a/attrib/example.c b/attrib/example.c
new file mode 100644
index 0000000..fae288c
--- /dev/null
+++ b/attrib/example.c
@@ -0,0 +1,341 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2010 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 <arpa/inet.h>
+
+#include <bluetooth/uuid.h>
+
+#include <glib.h>
+
+#include "log.h"
+#include "attrib-server.h"
+
+#include "att.h"
+#include "example.h"
+
+/* FIXME: Not defined by SIG? UUID128? */
+#define OPCODES_SUPPORTED_UUID 0xA001
+#define BATTERY_STATE_SVC_UUID 0xA002
+#define BATTERY_STATE_UUID 0xA003
+#define THERM_HUMIDITY_SVC_UUID 0xA004
+#define MANUFACTURER_SVC_UUID 0xA005
+#define TEMPERATURE_UUID 0xA006
+#define FMT_CELSIUS_UUID 0xA007
+#define FMT_OUTSIDE_UUID 0xA008
+#define RELATIVE_HUMIDITY_UUID 0xA009
+#define FMT_PERCENT_UUID 0xA00A
+#define BLUETOOTH_SIG_UUID 0xA00B
+#define MANUFACTURER_NAME_UUID 0xA00C
+#define MANUFACTURER_SERIAL_UUID 0xA00D
+#define VENDOR_SPECIFIC_SVC_UUID 0xA00E
+#define VENDOR_SPECIFIC_TYPE_UUID 0xA00F
+#define FMT_KILOGRAM_UUID 0xA010
+#define FMT_HANGING_UUID 0xA011
+
+static GSList *sdp_handles = NULL;
+
+static int register_attributes(void)
+{
+ const char *desc_out_temp = "Outside Temperature";
+ const char *desc_out_hum = "Outside Relative Humidity";
+ const char *desc_weight = "Rucksack Weight";
+ const char *manufacturer_name1 = "ACME Temperature Sensor";
+ const char *manufacturer_name2 = "ACME Weighing Scales";
+ const char *serial1 = "237495-3282-A";
+ const char *serial2 = "11267-2327A00239";
+
+ const uint128_t char_weight_uuid_btorder = {
+ .data = { 0x80, 0x88, 0xF2, 0x18, 0x90, 0x2C, 0x45, 0x0B,
+ 0xB6, 0xC4, 0x62, 0x89, 0x1E, 0x8C, 0x25, 0xE9 } };
+ const uint128_t prim_weight_uuid_btorder = {
+ .data = { 0x4F, 0x0A, 0xC0, 0x96, 0x35, 0xD4, 0x49, 0x11,
+ 0x96, 0x31, 0xDE, 0xA8, 0xDC, 0x74, 0xEE, 0xFE } };
+
+ uint128_t char_weight_uuid;
+ uint8_t atval[256];
+ uint32_t handle;
+ bt_uuid_t uuid;
+ int len;
+
+ btoh128(&char_weight_uuid_btorder, &char_weight_uuid);
+
+ /* Battery state service: primary service definition */
+ bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
+ att_put_u16(BATTERY_STATE_SVC_UUID, &atval[0]);
+ attrib_db_add(0x0100, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2);
+
+ /* Battery: battery state characteristic */
+ bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+ atval[0] = ATT_CHAR_PROPER_READ;
+ att_put_u16(0x0110, &atval[1]);
+ att_put_u16(BATTERY_STATE_UUID, &atval[3]);
+ attrib_db_add(0x0106, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
+
+ /* Battery: battery state attribute */
+ bt_uuid16_create(&uuid, BATTERY_STATE_UUID);
+ atval[0] = 0x04;
+ attrib_db_add(0x0110, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 1);
+
+ /* Battery: Client Characteristic Configuration */
+ bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+ atval[0] = 0x00;
+ atval[1] = 0x00;
+ attrib_db_add(0x0111, &uuid, ATT_NONE, ATT_AUTHENTICATION, atval, 2);
+
+ /* Add an SDP record for the above service */
+ handle = attrib_create_sdp(0x0100, "Battery State Service");
+ if (handle)
+ sdp_handles = g_slist_prepend(sdp_handles, GUINT_TO_POINTER(handle));
+
+ /* Thermometer: primary service definition */
+ bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
+ att_put_u16(THERM_HUMIDITY_SVC_UUID, &atval[0]);
+ attrib_db_add(0x0200, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2);
+
+ /* Thermometer: Include */
+ bt_uuid16_create(&uuid, GATT_INCLUDE_UUID);
+ att_put_u16(0x0500, &atval[0]);
+ att_put_u16(0x0504, &atval[2]);
+ att_put_u16(MANUFACTURER_SVC_UUID, &atval[4]);
+ attrib_db_add(0x0201, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 6);
+
+ /* Thermometer: Include */
+ att_put_u16(0x0550, &atval[0]);
+ att_put_u16(0x0568, &atval[2]);
+ att_put_u16(VENDOR_SPECIFIC_SVC_UUID, &atval[4]);
+ attrib_db_add(0x0202, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 6);
+
+ /* Thermometer: temperature characteristic */
+ bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+ atval[0] = ATT_CHAR_PROPER_READ;
+ att_put_u16(0x0204, &atval[1]);
+ att_put_u16(TEMPERATURE_UUID, &atval[3]);
+ attrib_db_add(0x0203, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
+
+ /* Thermometer: temperature characteristic value */
+ bt_uuid16_create(&uuid, TEMPERATURE_UUID);
+ atval[0] = 0x8A;
+ atval[1] = 0x02;
+ attrib_db_add(0x0204, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2);
+
+ /* Thermometer: temperature characteristic format */
+ bt_uuid16_create(&uuid, GATT_CHARAC_FMT_UUID);
+ atval[0] = 0x0E;
+ atval[1] = 0xFE;
+ att_put_u16(FMT_CELSIUS_UUID, &atval[2]);
+ atval[4] = 0x01;
+ att_put_u16(FMT_OUTSIDE_UUID, &atval[5]);
+ attrib_db_add(0x0205, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 7);
+
+ /* Thermometer: characteristic user description */
+ bt_uuid16_create(&uuid, GATT_CHARAC_USER_DESC_UUID);
+ len = strlen(desc_out_temp);
+ strncpy((char *) atval, desc_out_temp, len);
+ attrib_db_add(0x0206, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, len);
+
+ /* Thermometer: relative humidity characteristic */
+ bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+ atval[0] = ATT_CHAR_PROPER_READ;
+ att_put_u16(0x0212, &atval[1]);
+ att_put_u16(RELATIVE_HUMIDITY_UUID, &atval[3]);
+ attrib_db_add(0x0210, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
+
+ /* Thermometer: relative humidity value */
+ bt_uuid16_create(&uuid, RELATIVE_HUMIDITY_UUID);
+ atval[0] = 0x27;
+ attrib_db_add(0x0212, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 1);
+
+ /* Thermometer: relative humidity characteristic format */
+ bt_uuid16_create(&uuid, GATT_CHARAC_FMT_UUID);
+ atval[0] = 0x04;
+ atval[1] = 0x00;
+ att_put_u16(FMT_PERCENT_UUID, &atval[2]);
+ att_put_u16(BLUETOOTH_SIG_UUID, &atval[4]);
+ att_put_u16(FMT_OUTSIDE_UUID, &atval[6]);
+ attrib_db_add(0x0213, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 8);
+
+ /* Thermometer: characteristic user description */
+ bt_uuid16_create(&uuid, GATT_CHARAC_USER_DESC_UUID);
+ len = strlen(desc_out_hum);
+ strncpy((char *) atval, desc_out_hum, len);
+ attrib_db_add(0x0214, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, len);
+
+ /* Add an SDP record for the above service */
+ handle = attrib_create_sdp(0x0200, "Thermometer");
+ if (handle)
+ sdp_handles = g_slist_prepend(sdp_handles, GUINT_TO_POINTER(handle));
+
+ /* Secondary Service: Manufacturer Service */
+ bt_uuid16_create(&uuid, GATT_SND_SVC_UUID);
+ att_put_u16(MANUFACTURER_SVC_UUID, &atval[0]);
+ attrib_db_add(0x0500, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2);
+
+ /* Manufacturer name characteristic definition */
+ bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+ atval[0] = ATT_CHAR_PROPER_READ;
+ att_put_u16(0x0502, &atval[1]);
+ att_put_u16(MANUFACTURER_NAME_UUID, &atval[3]);
+ attrib_db_add(0x0501, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
+
+ /* Manufacturer name characteristic value */
+ bt_uuid16_create(&uuid, MANUFACTURER_NAME_UUID);
+ len = strlen(manufacturer_name1);
+ strncpy((char *) atval, manufacturer_name1, len);
+ attrib_db_add(0x0502, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, len);
+
+ /* Manufacturer serial number characteristic */
+ bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+ atval[0] = ATT_CHAR_PROPER_READ;
+ att_put_u16(0x0504, &atval[1]);
+ att_put_u16(MANUFACTURER_SERIAL_UUID, &atval[3]);
+ attrib_db_add(0x0503, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
+
+ /* Manufacturer serial number characteristic value */
+ bt_uuid16_create(&uuid, MANUFACTURER_SERIAL_UUID);
+ len = strlen(serial1);
+ strncpy((char *) atval, serial1, len);
+ attrib_db_add(0x0504, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, len);
+
+ /* Secondary Service: Manufacturer Service */
+ bt_uuid16_create(&uuid, GATT_SND_SVC_UUID);
+ att_put_u16(MANUFACTURER_SVC_UUID, &atval[0]);
+ attrib_db_add(0x0505, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2);
+
+ /* Manufacturer name characteristic definition */
+ bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+ atval[0] = ATT_CHAR_PROPER_READ;
+ att_put_u16(0x0507, &atval[1]);
+ att_put_u16(MANUFACTURER_NAME_UUID, &atval[3]);
+ attrib_db_add(0x0506, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
+
+ /* Secondary Service: Vendor Specific Service */
+ bt_uuid16_create(&uuid, GATT_SND_SVC_UUID);
+ att_put_u16(VENDOR_SPECIFIC_SVC_UUID, &atval[0]);
+ attrib_db_add(0x0550, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2);
+
+ /* Vendor Specific Type characteristic definition */
+ bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+ atval[0] = ATT_CHAR_PROPER_READ;
+ att_put_u16(0x0568, &atval[1]);
+ att_put_u16(VENDOR_SPECIFIC_TYPE_UUID, &atval[3]);
+ attrib_db_add(0x0560, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
+
+ /* Vendor Specific Type characteristic value */
+ bt_uuid16_create(&uuid, VENDOR_SPECIFIC_TYPE_UUID);
+ atval[0] = 0x56;
+ atval[1] = 0x65;
+ atval[2] = 0x6E;
+ atval[3] = 0x64;
+ atval[4] = 0x6F;
+ atval[5] = 0x72;
+ attrib_db_add(0x0568, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 6);
+
+ /* Manufacturer name attribute */
+ bt_uuid16_create(&uuid, MANUFACTURER_NAME_UUID);
+ len = strlen(manufacturer_name2);
+ strncpy((char *) atval, manufacturer_name2, len);
+ attrib_db_add(0x0507, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, len);
+
+ /* Characteristic: serial number */
+ bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+ atval[0] = ATT_CHAR_PROPER_READ;
+ att_put_u16(0x0509, &atval[1]);
+ att_put_u16(MANUFACTURER_SERIAL_UUID, &atval[3]);
+ attrib_db_add(0x0508, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
+
+ /* Serial number characteristic value */
+ bt_uuid16_create(&uuid, MANUFACTURER_SERIAL_UUID);
+ len = strlen(serial2);
+ strncpy((char *) atval, serial2, len);
+ attrib_db_add(0x0509, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, len);
+
+ /* Weight service: primary service definition */
+ bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
+ memcpy(atval, &prim_weight_uuid_btorder, 16);
+ attrib_db_add(0x0680, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 16);
+
+ /* Weight: include */
+ bt_uuid16_create(&uuid, GATT_INCLUDE_UUID);
+ att_put_u16(0x0505, &atval[0]);
+ att_put_u16(0x0509, &atval[2]);
+ att_put_u16(MANUFACTURER_SVC_UUID, &atval[4]);
+ attrib_db_add(0x0681, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 6);
+
+ /* Weight: characteristic */
+ bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+ atval[0] = ATT_CHAR_PROPER_READ;
+ att_put_u16(0x0683, &atval[1]);
+ memcpy(&atval[3], &char_weight_uuid_btorder, 16);
+ attrib_db_add(0x0682, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 19);
+
+ /* Weight: characteristic value */
+ bt_uuid128_create(&uuid, char_weight_uuid);
+ atval[0] = 0x82;
+ atval[1] = 0x55;
+ atval[2] = 0x00;
+ atval[3] = 0x00;
+ attrib_db_add(0x0683, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 4);
+
+ /* Weight: characteristic format */
+ bt_uuid16_create(&uuid, GATT_CHARAC_FMT_UUID);
+ atval[0] = 0x08;
+ atval[1] = 0xFD;
+ att_put_u16(FMT_KILOGRAM_UUID, &atval[2]);
+ att_put_u16(BLUETOOTH_SIG_UUID, &atval[4]);
+ att_put_u16(FMT_HANGING_UUID, &atval[6]);
+ attrib_db_add(0x0684, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 8);
+
+ /* Weight: characteristic user description */
+ bt_uuid16_create(&uuid, GATT_CHARAC_USER_DESC_UUID);
+ len = strlen(desc_weight);
+ strncpy((char *) atval, desc_weight, len);
+ attrib_db_add(0x0685, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, len);
+
+ /* Add an SDP record for the above service */
+ handle = attrib_create_sdp(0x0680, "Weight Service");
+ if (handle)
+ sdp_handles = g_slist_prepend(sdp_handles, GUINT_TO_POINTER(handle));
+
+ return 0;
+}
+
+int server_example_init(void)
+{
+ return register_attributes();
+}
+
+void server_example_exit(void)
+{
+ while (sdp_handles) {
+ uint32_t handle = GPOINTER_TO_UINT(sdp_handles->data);
+
+ attrib_free_sdp(handle);
+ sdp_handles = g_slist_remove(sdp_handles, sdp_handles->data);
+ }
+}
diff --git a/attrib/example.h b/attrib/example.h
new file mode 100644
index 0000000..a2b07fe
--- /dev/null
+++ b/attrib/example.h
@@ -0,0 +1,26 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2010 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
+ *
+ */
+
+int server_example_init(void);
+void server_example_exit(void);
diff --git a/attrib/gatt.c b/attrib/gatt.c
new file mode 100644
index 0000000..0b69daf
--- /dev/null
+++ b/attrib/gatt.c
@@ -0,0 +1,577 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2010 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
+ *
+ */
+
+#include <stdint.h>
+#include <glib.h>
+#include <bluetooth/uuid.h>
+
+#include "att.h"
+#include "gattrib.h"
+#include "gatt.h"
+
+struct discover_primary {
+ GAttrib *attrib;
+ bt_uuid_t uuid;
+ GSList *primaries;
+ gatt_cb_t cb;
+ void *user_data;
+};
+
+struct discover_char {
+ GAttrib *attrib;
+ bt_uuid_t *uuid;
+ uint16_t end;
+ GSList *characteristics;
+ gatt_cb_t cb;
+ void *user_data;
+};
+
+static void discover_primary_free(struct discover_primary *dp)
+{
+ g_slist_free(dp->primaries);
+ g_attrib_unref(dp->attrib);
+ g_free(dp);
+}
+
+static void discover_char_free(struct discover_char *dc)
+{
+ g_slist_foreach(dc->characteristics, (GFunc) g_free, NULL);
+ g_slist_free(dc->characteristics);
+ g_attrib_unref(dc->attrib);
+ g_free(dc->uuid);
+ g_free(dc);
+}
+
+static guint16 encode_discover_primary(uint16_t start, uint16_t end,
+ bt_uuid_t *uuid, uint8_t *pdu, size_t len)
+{
+ bt_uuid_t prim;
+ guint16 plen;
+ uint8_t op;
+
+ bt_uuid16_create(&prim, GATT_PRIM_SVC_UUID);
+
+ if (uuid == NULL) {
+ /* Discover all primary services */
+ op = ATT_OP_READ_BY_GROUP_REQ;
+ plen = enc_read_by_grp_req(start, end, &prim, pdu, len);
+ } else {
+ uint16_t u16;
+ uint128_t u128;
+ const void *value;
+ int vlen;
+
+ /* Discover primary service by service UUID */
+ op = ATT_OP_FIND_BY_TYPE_REQ;
+
+ if (uuid->type == BT_UUID16) {
+ u16 = htobs(uuid->value.u16);
+ value = &u16;
+ vlen = sizeof(u16);
+ } else {
+ htob128(&uuid->value.u128, &u128);
+ value = &u128;
+ vlen = sizeof(u128);
+ }
+
+ plen = enc_find_by_type_req(start, end, &prim, value, vlen,
+ pdu, len);
+ }
+
+ return plen;
+}
+
+static void primary_by_uuid_cb(guint8 status, const guint8 *ipdu,
+ guint16 iplen, gpointer user_data)
+
+{
+ struct discover_primary *dp = user_data;
+ GSList *ranges, *last;
+ struct att_range *range;
+ uint8_t *buf;
+ guint16 oplen;
+ int err = 0, buflen;
+
+ if (status) {
+ err = status == ATT_ECODE_ATTR_NOT_FOUND ? 0 : status;
+ goto done;
+ }
+
+ ranges = dec_find_by_type_resp(ipdu, iplen);
+ if (ranges == NULL)
+ goto done;
+
+ dp->primaries = g_slist_concat(dp->primaries, ranges);
+
+ last = g_slist_last(ranges);
+ range = last->data;
+
+ if (range->end == 0xffff)
+ goto done;
+
+ buf = g_attrib_get_buffer(dp->attrib, &buflen);
+ oplen = encode_discover_primary(range->end + 1, 0xffff, &dp->uuid,
+ buf, buflen);
+
+ if (oplen == 0)
+ goto done;
+
+ g_attrib_send(dp->attrib, 0, buf[0], buf, oplen, primary_by_uuid_cb,
+ dp, NULL);
+ return;
+
+done:
+ dp->cb(dp->primaries, err, dp->user_data);
+ discover_primary_free(dp);
+}
+
+static void primary_all_cb(guint8 status, const guint8 *ipdu, guint16 iplen,
+ gpointer user_data)
+{
+ struct discover_primary *dp = user_data;
+ struct att_data_list *list;
+ unsigned int i, err;
+ uint16_t start, end;
+
+ if (status) {
+ err = status == ATT_ECODE_ATTR_NOT_FOUND ? 0 : status;
+ goto done;
+ }
+
+ list = dec_read_by_grp_resp(ipdu, iplen);
+ if (list == NULL) {
+ err = ATT_ECODE_IO;
+ goto done;
+ }
+
+ for (i = 0, end = 0; i < list->num; i++) {
+ const uint8_t *data = list->data[i];
+ struct att_primary *primary;
+ bt_uuid_t uuid;
+
+ start = att_get_u16(&data[0]);
+ end = att_get_u16(&data[2]);
+
+ if (list->len == 6) {
+ bt_uuid_t uuid16 = att_get_uuid16(&data[4]);
+ bt_uuid_to_uuid128(&uuid16, &uuid);
+ } else if (list->len == 20) {
+ uuid = att_get_uuid128(&data[4]);
+ } else {
+ /* Skipping invalid data */
+ continue;
+ }
+
+ primary = g_try_new0(struct att_primary, 1);
+ if (!primary) {
+ err = ATT_ECODE_INSUFF_RESOURCES;
+ goto done;
+ }
+ primary->start = start;
+ primary->end = end;
+ bt_uuid_to_string(&uuid, primary->uuid, sizeof(primary->uuid));
+ dp->primaries = g_slist_append(dp->primaries, primary);
+ }
+
+ att_data_list_free(list);
+ err = 0;
+
+ if (end != 0xffff) {
+ int buflen;
+ uint8_t *buf = g_attrib_get_buffer(dp->attrib, &buflen);
+ guint16 oplen = encode_discover_primary(end + 1, 0xffff, NULL,
+ buf, buflen);
+
+ g_attrib_send(dp->attrib, 0, buf[0], buf, oplen, primary_all_cb,
+ dp, NULL);
+
+ return;
+ }
+
+done:
+ dp->cb(dp->primaries, err, dp->user_data);
+ discover_primary_free(dp);
+}
+
+guint gatt_discover_primary(GAttrib *attrib, bt_uuid_t *uuid, gatt_cb_t func,
+ gpointer user_data)
+{
+ struct discover_primary *dp;
+ int buflen;
+ uint8_t *buf = g_attrib_get_buffer(attrib, &buflen);
+ GAttribResultFunc cb;
+ guint16 plen;
+
+ plen = encode_discover_primary(0x0001, 0xffff, uuid, buf, buflen);
+ if (plen == 0)
+ return 0;
+
+ dp = g_try_new0(struct discover_primary, 1);
+ if (dp == NULL)
+ return 0;
+
+ dp->attrib = g_attrib_ref(attrib);
+ dp->cb = func;
+ dp->user_data = user_data;
+
+ if (uuid) {
+ memcpy(&dp->uuid, uuid, sizeof(bt_uuid_t));
+ cb = primary_by_uuid_cb;
+ } else
+ cb = primary_all_cb;
+
+ return g_attrib_send(attrib, 0, buf[0], buf, plen, cb, dp, NULL);
+}
+
+static void char_discovered_cb(guint8 status, const guint8 *ipdu, guint16 iplen,
+ gpointer user_data)
+{
+ struct discover_char *dc = user_data;
+ struct att_data_list *list;
+ unsigned int i, err;
+ int buflen;
+ uint8_t *buf;
+ guint16 oplen;
+ bt_uuid_t uuid;
+ uint16_t last = 0;
+
+ if (status) {
+ err = status == ATT_ECODE_ATTR_NOT_FOUND ? 0 : status;
+ goto done;
+ }
+
+ list = dec_read_by_type_resp(ipdu, iplen);
+ if (list == NULL) {
+ err = ATT_ECODE_IO;
+ goto done;
+ }
+
+ for (i = 0; i < list->num; i++) {
+ uint8_t *value = list->data[i];
+ struct att_char *chars;
+ bt_uuid_t uuid;
+
+ last = att_get_u16(value);
+
+ if (list->len == 7) {
+ bt_uuid_t uuid16 = att_get_uuid16(&value[5]);
+ bt_uuid_to_uuid128(&uuid16, &uuid);
+ } else
+ uuid = att_get_uuid128(&value[5]);
+
+ chars = g_try_new0(struct att_char, 1);
+ if (!chars) {
+ err = ATT_ECODE_INSUFF_RESOURCES;
+ goto done;
+ }
+
+ if (dc->uuid && bt_uuid_cmp(dc->uuid, &uuid))
+ break;
+
+ chars->handle = last;
+ chars->properties = value[2];
+ chars->value_handle = att_get_u16(&value[3]);
+ bt_uuid_to_string(&uuid, chars->uuid, sizeof(chars->uuid));
+ dc->characteristics = g_slist_append(dc->characteristics,
+ chars);
+ }
+
+ att_data_list_free(list);
+ err = 0;
+
+ if (last != 0) {
+ buf = g_attrib_get_buffer(dc->attrib, &buflen);
+
+ bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+
+ oplen = enc_read_by_type_req(last + 1, dc->end, &uuid, buf,
+ buflen);
+
+ if (oplen == 0)
+ return;
+
+ g_attrib_send(dc->attrib, 0, buf[0], buf, oplen,
+ char_discovered_cb, dc, NULL);
+
+ return;
+ }
+
+done:
+ dc->cb(dc->characteristics, err, dc->user_data);
+ discover_char_free(dc);
+}
+
+guint gatt_discover_char(GAttrib *attrib, uint16_t start, uint16_t end,
+ bt_uuid_t *uuid, gatt_cb_t func,
+ gpointer user_data)
+{
+ int buflen;
+ uint8_t *buf = g_attrib_get_buffer(attrib, &buflen);
+ struct discover_char *dc;
+ bt_uuid_t type_uuid;
+ guint16 plen;
+
+ bt_uuid16_create(&type_uuid, GATT_CHARAC_UUID);
+
+ plen = enc_read_by_type_req(start, end, &type_uuid, buf, buflen);
+ if (plen == 0)
+ return 0;
+
+ dc = g_try_new0(struct discover_char, 1);
+ if (dc == NULL)
+ return 0;
+
+ dc->attrib = g_attrib_ref(attrib);
+ dc->cb = func;
+ dc->user_data = user_data;
+ dc->end = end;
+ dc->uuid = g_memdup(uuid, sizeof(bt_uuid_t));
+
+ return g_attrib_send(attrib, 0, buf[0], buf, plen, char_discovered_cb,
+ dc, NULL);
+}
+
+guint gatt_read_char_by_uuid(GAttrib *attrib, uint16_t start, uint16_t end,
+ bt_uuid_t *uuid, GAttribResultFunc func,
+ gpointer user_data)
+{
+ int buflen;
+ uint8_t *buf = g_attrib_get_buffer(attrib, &buflen);
+ guint16 plen;
+
+ plen = enc_read_by_type_req(start, end, uuid, buf, buflen);
+ if (plen == 0)
+ return 0;
+
+ return g_attrib_send(attrib, 0, ATT_OP_READ_BY_TYPE_REQ,
+ buf, plen, func, user_data, NULL);
+}
+
+struct read_long_data {
+ GAttrib *attrib;
+ GAttribResultFunc func;
+ gpointer user_data;
+ guint8 *buffer;
+ guint16 size;
+ guint16 handle;
+ guint id;
+ gint ref;
+};
+
+static void read_long_destroy(gpointer user_data)
+{
+ struct read_long_data *long_read = user_data;
+
+ if (g_atomic_int_dec_and_test(&long_read->ref) == FALSE)
+ return;
+
+ if (long_read->buffer != NULL)
+ g_free(long_read->buffer);
+
+ g_free(long_read);
+}
+
+static void read_blob_helper(guint8 status, const guint8 *rpdu, guint16 rlen,
+ gpointer user_data)
+{
+ struct read_long_data *long_read = user_data;
+ uint8_t *buf;
+ int buflen;
+ guint8 *tmp;
+ guint16 plen;
+ guint id;
+
+ if (status != 0 || rlen == 1) {
+ status = 0;
+ goto done;
+ }
+
+ tmp = g_try_realloc(long_read->buffer, long_read->size + rlen - 1);
+
+ if (tmp == NULL) {
+ status = ATT_ECODE_INSUFF_RESOURCES;
+ goto done;
+ }
+
+ memcpy(&tmp[long_read->size], &rpdu[1], rlen - 1);
+ long_read->buffer = tmp;
+ long_read->size += rlen - 1;
+
+ buf = g_attrib_get_buffer(long_read->attrib, &buflen);
+ if (rlen < buflen)
+ goto done;
+
+ plen = enc_read_blob_req(long_read->handle, long_read->size - 1,
+ buf, buflen);
+ id = g_attrib_send(long_read->attrib, long_read->id,
+ ATT_OP_READ_BLOB_REQ, buf, plen,
+ read_blob_helper, long_read, read_long_destroy);
+
+ if (id != 0) {
+ g_atomic_int_inc(&long_read->ref);
+ return;
+ }
+
+ status = ATT_ECODE_IO;
+
+done:
+ long_read->func(status, long_read->buffer, long_read->size,
+ long_read->user_data);
+}
+
+static void read_char_helper(guint8 status, const guint8 *rpdu,
+ guint16 rlen, gpointer user_data)
+{
+ struct read_long_data *long_read = user_data;
+ int buflen;
+ uint8_t *buf = g_attrib_get_buffer(long_read->attrib, &buflen);
+ guint16 plen;
+ guint id;
+
+ if (status != 0 || rlen < buflen)
+ goto done;
+
+ long_read->buffer = g_malloc(rlen);
+
+ if (long_read->buffer == NULL)
+ goto done;
+
+ memcpy(long_read->buffer, rpdu, rlen);
+ long_read->size = rlen;
+
+ plen = enc_read_blob_req(long_read->handle, rlen - 1, buf, buflen);
+ id = g_attrib_send(long_read->attrib, long_read->id,
+ ATT_OP_READ_BLOB_REQ, buf, plen, read_blob_helper,
+ long_read, read_long_destroy);
+
+ if (id != 0) {
+ g_atomic_int_inc(&long_read->ref);
+ return;
+ }
+
+ status = ATT_ECODE_IO;
+
+done:
+ long_read->func(status, rpdu, rlen, long_read->user_data);
+}
+
+guint gatt_read_char(GAttrib *attrib, uint16_t handle, uint16_t offset,
+ GAttribResultFunc func, gpointer user_data)
+{
+ uint8_t *buf;
+ int buflen;
+ guint16 plen;
+ guint id;
+ struct read_long_data *long_read;
+
+ long_read = g_try_new0(struct read_long_data, 1);
+
+ if (long_read == NULL)
+ return 0;
+
+ long_read->attrib = attrib;
+ long_read->func = func;
+ long_read->user_data = user_data;
+ long_read->handle = handle;
+
+ buf = g_attrib_get_buffer(attrib, &buflen);
+ if (offset > 0) {
+ plen = enc_read_blob_req(long_read->handle, offset, buf,
+ buflen);
+ id = g_attrib_send(attrib, 0, ATT_OP_READ_BLOB_REQ, buf, plen,
+ read_blob_helper, long_read, read_long_destroy);
+ } else {
+ plen = enc_read_req(handle, buf, buflen);
+ id = g_attrib_send(attrib, 0, ATT_OP_READ_REQ, buf, plen,
+ read_char_helper, long_read, read_long_destroy);
+ }
+
+ if (id == 0)
+ g_free(long_read);
+ else {
+ g_atomic_int_inc(&long_read->ref);
+ long_read->id = id;
+ }
+
+ return id;
+}
+
+guint gatt_write_char(GAttrib *attrib, uint16_t handle, uint8_t *value,
+ int vlen, GAttribResultFunc func, gpointer user_data)
+{
+ uint8_t *buf;
+ int buflen;
+ guint16 plen;
+
+ buf = g_attrib_get_buffer(attrib, &buflen);
+ if (func)
+ plen = enc_write_req(handle, value, vlen, buf, buflen);
+ else
+ plen = enc_write_cmd(handle, value, vlen, buf, buflen);
+
+ return g_attrib_send(attrib, 0, buf[0], buf, plen, func,
+ user_data, NULL);
+}
+
+guint gatt_exchange_mtu(GAttrib *attrib, uint16_t mtu, GAttribResultFunc func,
+ gpointer user_data)
+{
+ uint8_t *buf;
+ int buflen;
+ guint16 plen;
+
+ buf = g_attrib_get_buffer(attrib, &buflen);
+ plen = enc_mtu_req(mtu, buf, buflen);
+ return g_attrib_send(attrib, 0, ATT_OP_MTU_REQ, buf, plen, func,
+ user_data, NULL);
+}
+
+guint gatt_find_info(GAttrib *attrib, uint16_t start, uint16_t end,
+ GAttribResultFunc func, gpointer user_data)
+{
+ uint8_t *buf;
+ int buflen;
+ guint16 plen;
+
+ buf = g_attrib_get_buffer(attrib, &buflen);
+ plen = enc_find_info_req(start, end, buf, buflen);
+ if (plen == 0)
+ return 0;
+
+ return g_attrib_send(attrib, 0, ATT_OP_FIND_INFO_REQ, buf, plen, func,
+ user_data, NULL);
+}
+
+guint gatt_write_cmd(GAttrib *attrib, uint16_t handle, uint8_t *value, int vlen,
+ GDestroyNotify notify, gpointer user_data)
+{
+ uint8_t *buf;
+ int buflen;
+ guint16 plen;
+
+ buf = g_attrib_get_buffer(attrib, &buflen);
+ plen = enc_write_cmd(handle, value, vlen, buf, buflen);
+ return g_attrib_send(attrib, 0, ATT_OP_WRITE_CMD, buf, plen, NULL,
+ user_data, notify);
+}
diff --git a/attrib/gatt.h b/attrib/gatt.h
new file mode 100644
index 0000000..221d94d
--- /dev/null
+++ b/attrib/gatt.h
@@ -0,0 +1,53 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2010 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
+ *
+ */
+
+#define GATT_CID 4
+
+typedef void (*gatt_cb_t) (GSList *l, guint8 status, gpointer user_data);
+
+guint gatt_discover_primary(GAttrib *attrib, bt_uuid_t *uuid, gatt_cb_t func,
+ gpointer user_data);
+
+guint gatt_discover_char(GAttrib *attrib, uint16_t start, uint16_t end,
+ bt_uuid_t *uuid, gatt_cb_t func,
+ gpointer user_data);
+
+guint gatt_read_char(GAttrib *attrib, uint16_t handle, uint16_t offset,
+ GAttribResultFunc func, gpointer user_data);
+
+guint gatt_write_char(GAttrib *attrib, uint16_t handle, uint8_t *value,
+ int vlen, GAttribResultFunc func, gpointer user_data);
+
+guint gatt_find_info(GAttrib *attrib, uint16_t start, uint16_t end,
+ GAttribResultFunc func, gpointer user_data);
+
+guint gatt_write_cmd(GAttrib *attrib, uint16_t handle, uint8_t *value, int vlen,
+ GDestroyNotify notify, gpointer user_data);
+
+guint gatt_read_char_by_uuid(GAttrib *attrib, uint16_t start, uint16_t end,
+ bt_uuid_t *uuid, GAttribResultFunc func,
+ gpointer user_data);
+
+guint gatt_exchange_mtu(GAttrib *attrib, uint16_t mtu, GAttribResultFunc func,
+ gpointer user_data);
diff --git a/attrib/gattrib.c b/attrib/gattrib.c
new file mode 100644
index 0000000..290cd96
--- /dev/null
+++ b/attrib/gattrib.c
@@ -0,0 +1,636 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2010 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
+ *
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <glib.h>
+
+#include <stdio.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/uuid.h>
+
+#include "att.h"
+#include "btio.h"
+#include "gattrib.h"
+
+#define GATT_TIMEOUT 30
+
+struct _GAttrib {
+ GIOChannel *io;
+ gint refs;
+ uint8_t *buf;
+ int buflen;
+ guint read_watch;
+ guint write_watch;
+ guint timeout_watch;
+ GQueue *queue;
+ GSList *events;
+ guint next_cmd_id;
+ guint next_evt_id;
+ GDestroyNotify destroy;
+ GAttribDisconnectFunc disconnect;
+ gpointer destroy_user_data;
+ gpointer disc_user_data;
+};
+
+struct command {
+ guint id;
+ guint8 opcode;
+ guint8 *pdu;
+ guint16 len;
+ guint8 expected;
+ gboolean sent;
+ GAttribResultFunc func;
+ gpointer user_data;
+ GDestroyNotify notify;
+};
+
+struct event {
+ guint id;
+ guint8 expected;
+ GAttribNotifyFunc func;
+ gpointer user_data;
+ GDestroyNotify notify;
+};
+
+static guint8 opcode2expected(guint8 opcode)
+{
+ switch (opcode) {
+ case ATT_OP_MTU_REQ:
+ return ATT_OP_MTU_RESP;
+
+ case ATT_OP_FIND_INFO_REQ:
+ return ATT_OP_FIND_INFO_RESP;
+
+ case ATT_OP_FIND_BY_TYPE_REQ:
+ return ATT_OP_FIND_BY_TYPE_RESP;
+
+ case ATT_OP_READ_BY_TYPE_REQ:
+ return ATT_OP_READ_BY_TYPE_RESP;
+
+ case ATT_OP_READ_REQ:
+ return ATT_OP_READ_RESP;
+
+ case ATT_OP_READ_BLOB_REQ:
+ return ATT_OP_READ_BLOB_RESP;
+
+ case ATT_OP_READ_MULTI_REQ:
+ return ATT_OP_READ_MULTI_RESP;
+
+ case ATT_OP_READ_BY_GROUP_REQ:
+ return ATT_OP_READ_BY_GROUP_RESP;
+
+ case ATT_OP_WRITE_REQ:
+ return ATT_OP_WRITE_RESP;
+
+ case ATT_OP_PREP_WRITE_REQ:
+ return ATT_OP_PREP_WRITE_RESP;
+
+ case ATT_OP_EXEC_WRITE_REQ:
+ return ATT_OP_EXEC_WRITE_RESP;
+
+ case ATT_OP_HANDLE_IND:
+ return ATT_OP_HANDLE_CNF;
+ }
+
+ return 0;
+}
+
+static gboolean is_response(guint8 opcode)
+{
+ switch (opcode) {
+ case ATT_OP_ERROR:
+ case ATT_OP_MTU_RESP:
+ case ATT_OP_FIND_INFO_RESP:
+ case ATT_OP_FIND_BY_TYPE_RESP:
+ case ATT_OP_READ_BY_TYPE_RESP:
+ case ATT_OP_READ_RESP:
+ case ATT_OP_READ_BLOB_RESP:
+ case ATT_OP_READ_MULTI_RESP:
+ case ATT_OP_READ_BY_GROUP_RESP:
+ case ATT_OP_WRITE_RESP:
+ case ATT_OP_PREP_WRITE_RESP:
+ case ATT_OP_EXEC_WRITE_RESP:
+ case ATT_OP_HANDLE_CNF:
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+GAttrib *g_attrib_ref(GAttrib *attrib)
+{
+ if (!attrib)
+ return NULL;
+
+ g_atomic_int_inc(&attrib->refs);
+
+ return attrib;
+}
+
+static void command_destroy(struct command *cmd)
+{
+ if (cmd->notify)
+ cmd->notify(cmd->user_data);
+
+ g_free(cmd->pdu);
+ g_free(cmd);
+}
+
+static void event_destroy(struct event *evt)
+{
+ if (evt->notify)
+ evt->notify(evt->user_data);
+
+ g_free(evt);
+}
+
+static void attrib_destroy(GAttrib *attrib)
+{
+ GSList *l;
+ struct command *c;
+
+ while ((c = g_queue_pop_head(attrib->queue)))
+ command_destroy(c);
+
+ g_queue_free(attrib->queue);
+ attrib->queue = NULL;
+
+ for (l = attrib->events; l; l = l->next)
+ event_destroy(l->data);
+
+ g_slist_free(attrib->events);
+ attrib->events = NULL;
+
+ if (attrib->timeout_watch > 0)
+ g_source_remove(attrib->timeout_watch);
+
+ if (attrib->write_watch > 0)
+ g_source_remove(attrib->write_watch);
+
+ if (attrib->read_watch > 0) {
+ g_source_remove(attrib->read_watch);
+ g_io_channel_unref(attrib->io);
+ }
+
+ g_free(attrib->buf);
+
+ if (attrib->destroy)
+ attrib->destroy(attrib->destroy_user_data);
+
+ g_free(attrib);
+}
+
+void g_attrib_unref(GAttrib *attrib)
+{
+ if (!attrib)
+ return;
+
+ if (g_atomic_int_dec_and_test(&attrib->refs) == FALSE)
+ return;
+
+ attrib_destroy(attrib);
+}
+
+GIOChannel *g_attrib_get_channel(GAttrib *attrib)
+{
+ if (!attrib)
+ return NULL;
+
+ return attrib->io;
+}
+
+gboolean g_attrib_set_disconnect_function(GAttrib *attrib,
+ GAttribDisconnectFunc disconnect, gpointer user_data)
+{
+ if (attrib == NULL)
+ return FALSE;
+
+ attrib->disconnect = disconnect;
+ attrib->disc_user_data = user_data;
+
+ return TRUE;
+}
+
+gboolean g_attrib_set_destroy_function(GAttrib *attrib,
+ GDestroyNotify destroy, gpointer user_data)
+{
+ if (attrib == NULL)
+ return FALSE;
+
+ attrib->destroy = destroy;
+ attrib->destroy_user_data = user_data;
+
+ return TRUE;
+}
+
+static gboolean disconnect_timeout(gpointer data)
+{
+ struct _GAttrib *attrib = data;
+
+ attrib_destroy(attrib);
+
+ return FALSE;
+}
+
+static gboolean can_write_data(GIOChannel *io, GIOCondition cond,
+ gpointer data)
+{
+ struct _GAttrib *attrib = data;
+ struct command *cmd;
+ GError *gerr = NULL;
+ gsize len;
+ GIOStatus iostat;
+
+ if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
+ if (attrib->disconnect)
+ attrib->disconnect(attrib->disc_user_data);
+
+ return FALSE;
+ }
+
+ cmd = g_queue_peek_head(attrib->queue);
+ if (cmd == NULL)
+ return FALSE;
+
+ iostat = g_io_channel_write_chars(io, (gchar *) cmd->pdu, cmd->len,
+ &len, &gerr);
+ if (iostat != G_IO_STATUS_NORMAL)
+ return FALSE;
+
+ if (cmd->expected == 0) {
+ g_queue_pop_head(attrib->queue);
+ command_destroy(cmd);
+
+ return TRUE;
+ }
+
+ cmd->sent = TRUE;
+
+ if (attrib->timeout_watch == 0)
+ attrib->timeout_watch = g_timeout_add_seconds(GATT_TIMEOUT,
+ disconnect_timeout, attrib);
+
+ return FALSE;
+}
+
+static void destroy_sender(gpointer data)
+{
+ struct _GAttrib *attrib = data;
+
+ attrib->write_watch = 0;
+}
+
+static void wake_up_sender(struct _GAttrib *attrib)
+{
+ if (attrib->write_watch == 0)
+ attrib->write_watch = g_io_add_watch_full(attrib->io,
+ G_PRIORITY_DEFAULT, G_IO_OUT, can_write_data,
+ attrib, destroy_sender);
+}
+
+static gboolean received_data(GIOChannel *io, GIOCondition cond, gpointer data)
+{
+ struct _GAttrib *attrib = data;
+ struct command *cmd = NULL;
+ GSList *l;
+ uint8_t buf[512], status;
+ gsize len;
+ GIOStatus iostat;
+ gboolean qempty;
+
+ if (attrib->timeout_watch > 0) {
+ g_source_remove(attrib->timeout_watch);
+ attrib->timeout_watch = 0;
+ }
+
+ if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
+ attrib->read_watch = 0;
+ if (attrib->disconnect)
+ attrib->disconnect(attrib->disc_user_data);
+ return FALSE;
+ }
+
+ memset(buf, 0, sizeof(buf));
+
+ iostat = g_io_channel_read_chars(io, (gchar *) buf, sizeof(buf),
+ &len, NULL);
+ if (iostat != G_IO_STATUS_NORMAL) {
+ status = ATT_ECODE_IO;
+ goto done;
+ }
+
+ for (l = attrib->events; l; l = l->next) {
+ struct event *evt = l->data;
+
+ if (evt->expected == buf[0] ||
+ evt->expected == GATTRIB_ALL_EVENTS)
+ evt->func(buf, len, evt->user_data);
+ }
+
+ if (is_response(buf[0]) == FALSE)
+ return TRUE;
+
+ cmd = g_queue_pop_head(attrib->queue);
+ if (cmd == NULL) {
+ /* Keep the watch if we have events to report */
+ return attrib->events != NULL;
+ }
+
+ if (buf[0] == ATT_OP_ERROR) {
+ status = buf[4];
+ goto done;
+ }
+
+ if (cmd->expected != buf[0]) {
+ status = ATT_ECODE_IO;
+ goto done;
+ }
+
+ status = 0;
+
+done:
+ qempty = attrib->queue == NULL || g_queue_is_empty(attrib->queue);
+
+ if (cmd) {
+ if (cmd->func)
+ cmd->func(status, buf, len, cmd->user_data);
+
+ command_destroy(cmd);
+ }
+
+ if (!qempty)
+ wake_up_sender(attrib);
+
+ return TRUE;
+}
+
+GAttrib *g_attrib_new(GIOChannel *io)
+{
+ struct _GAttrib *attrib;
+ uint16_t omtu;
+
+ g_io_channel_set_encoding(io, NULL, NULL);
+ g_io_channel_set_buffered(io, FALSE);
+
+ attrib = g_try_new0(struct _GAttrib, 1);
+ if (attrib == NULL)
+ return NULL;
+
+ attrib->io = g_io_channel_ref(io);
+ attrib->queue = g_queue_new();
+
+ attrib->read_watch = g_io_add_watch(attrib->io,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ received_data, attrib);
+
+ if (bt_io_get(attrib->io, BT_IO_L2CAP, NULL,
+ BT_IO_OPT_OMTU, &omtu,
+ BT_IO_OPT_INVALID)) {
+ if (omtu > ATT_MAX_MTU)
+ omtu = ATT_MAX_MTU;
+ } else
+ omtu = ATT_DEFAULT_LE_MTU;
+
+ attrib->buf = g_malloc0(omtu);
+ attrib->buflen = omtu;
+
+ return g_attrib_ref(attrib);
+}
+
+guint g_attrib_send(GAttrib *attrib, guint id, guint8 opcode,
+ const guint8 *pdu, guint16 len, GAttribResultFunc func,
+ gpointer user_data, GDestroyNotify notify)
+{
+ struct command *c;
+
+ c = g_try_new0(struct command, 1);
+ if (c == NULL)
+ return 0;
+
+ c->opcode = opcode;
+ c->expected = opcode2expected(opcode);
+ c->pdu = g_malloc(len);
+ memcpy(c->pdu, pdu, len);
+ c->len = len;
+ c->func = func;
+ c->user_data = user_data;
+ c->notify = notify;
+
+ if (id) {
+ c->id = id;
+ g_queue_push_head(attrib->queue, c);
+ } else {
+ c->id = ++attrib->next_cmd_id;
+ g_queue_push_tail(attrib->queue, c);
+ }
+
+ if (g_queue_get_length(attrib->queue) == 1)
+ wake_up_sender(attrib);
+
+ return c->id;
+}
+
+static gint command_cmp_by_id(gconstpointer a, gconstpointer b)
+{
+ const struct command *cmd = a;
+ guint id = GPOINTER_TO_UINT(b);
+
+ return cmd->id - id;
+}
+
+gboolean g_attrib_cancel(GAttrib *attrib, guint id)
+{
+ GList *l;
+ struct command *cmd;
+
+ if (attrib == NULL || attrib->queue == NULL)
+ return FALSE;
+
+ l = g_queue_find_custom(attrib->queue, GUINT_TO_POINTER(id),
+ command_cmp_by_id);
+ if (l == NULL)
+ return FALSE;
+
+ cmd = l->data;
+
+ if (cmd == g_queue_peek_head(attrib->queue) && cmd->sent)
+ cmd->func = NULL;
+ else {
+ g_queue_remove(attrib->queue, cmd);
+ command_destroy(cmd);
+ }
+
+ return TRUE;
+}
+
+gboolean g_attrib_cancel_all(GAttrib *attrib)
+{
+ struct command *c, *head = NULL;
+ gboolean first = TRUE;
+
+ if (attrib == NULL || attrib->queue == NULL)
+ return FALSE;
+
+ while ((c = g_queue_pop_head(attrib->queue))) {
+ if (first && c->sent) {
+ /* If the command was sent ignore its callback ... */
+ c->func = NULL;
+ head = c;
+ continue;
+ }
+
+ first = FALSE;
+ command_destroy(c);
+ }
+
+ if (head) {
+ /* ... and put it back in the queue */
+ g_queue_push_head(attrib->queue, head);
+ }
+
+ return TRUE;
+}
+
+gboolean g_attrib_set_debug(GAttrib *attrib,
+ GAttribDebugFunc func, gpointer user_data)
+{
+ return TRUE;
+}
+
+uint8_t *g_attrib_get_buffer(GAttrib *attrib, int *len)
+{
+ if (len == NULL)
+ return NULL;
+
+ *len = attrib->buflen;
+
+ return attrib->buf;
+}
+
+gboolean g_attrib_set_mtu(GAttrib *attrib, int mtu)
+{
+ if (mtu < ATT_DEFAULT_LE_MTU)
+ mtu = ATT_DEFAULT_LE_MTU;
+
+ if (mtu > ATT_MAX_MTU)
+ mtu = ATT_MAX_MTU;
+
+ if (!bt_io_set(attrib->io, BT_IO_L2CAP, NULL,
+ BT_IO_OPT_OMTU, mtu,
+ BT_IO_OPT_INVALID))
+ return FALSE;
+
+ attrib->buf = g_realloc(attrib->buf, mtu);
+
+ attrib->buflen = mtu;
+
+ return TRUE;
+}
+
+guint g_attrib_register(GAttrib *attrib, guint8 opcode,
+ GAttribNotifyFunc func, gpointer user_data,
+ GDestroyNotify notify)
+{
+ struct event *event;
+
+ event = g_try_new0(struct event, 1);
+ if (event == NULL)
+ return 0;
+
+ event->expected = opcode;
+ event->func = func;
+ event->user_data = user_data;
+ event->notify = notify;
+ event->id = ++attrib->next_evt_id;
+
+ attrib->events = g_slist_append(attrib->events, event);
+
+ return event->id;
+}
+
+static gint event_cmp_by_id(gconstpointer a, gconstpointer b)
+{
+ const struct event *evt = a;
+ guint id = GPOINTER_TO_UINT(b);
+
+ return evt->id - id;
+}
+
+gboolean g_attrib_is_encrypted(GAttrib *attrib)
+{
+ BtIOSecLevel sec_level;
+
+ if (!bt_io_get(attrib->io, BT_IO_L2CAP, NULL,
+ BT_IO_OPT_SEC_LEVEL, &sec_level,
+ BT_IO_OPT_INVALID))
+ return FALSE;
+
+ return sec_level > BT_IO_SEC_LOW;
+}
+
+gboolean g_attrib_unregister(GAttrib *attrib, guint id)
+{
+ struct event *evt;
+ GSList *l;
+
+ l = g_slist_find_custom(attrib->events, GUINT_TO_POINTER(id),
+ event_cmp_by_id);
+ if (l == NULL)
+ return FALSE;
+
+ evt = l->data;
+
+ attrib->events = g_slist_remove(attrib->events, evt);
+
+ if (evt->notify)
+ evt->notify(evt->user_data);
+
+ g_free(evt);
+
+ return TRUE;
+}
+
+gboolean g_attrib_unregister_all(GAttrib *attrib)
+{
+ GSList *l;
+
+ if (attrib->events == NULL)
+ return FALSE;
+
+ for (l = attrib->events; l; l = l->next) {
+ struct event *evt = l->data;
+
+ if (evt->notify)
+ evt->notify(evt->user_data);
+
+ g_free(evt);
+ }
+
+ g_slist_free(attrib->events);
+ attrib->events = NULL;
+
+ return TRUE;
+}
diff --git a/attrib/gattrib.h b/attrib/gattrib.h
new file mode 100644
index 0000000..4c49879
--- /dev/null
+++ b/attrib/gattrib.h
@@ -0,0 +1,80 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2010 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
+ *
+ */
+#ifndef __GATTRIB_H
+#define __GATTRIB_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define GATTRIB_ALL_EVENTS 0xFF
+
+struct _GAttrib;
+typedef struct _GAttrib GAttrib;
+
+typedef void (*GAttribResultFunc) (guint8 status, const guint8 *pdu,
+ guint16 len, gpointer user_data);
+typedef void (*GAttribDisconnectFunc)(gpointer user_data);
+typedef void (*GAttribDebugFunc)(const char *str, gpointer user_data);
+typedef void (*GAttribNotifyFunc)(const guint8 *pdu, guint16 len,
+ gpointer user_data);
+
+GAttrib *g_attrib_new(GIOChannel *io);
+GAttrib *g_attrib_ref(GAttrib *attrib);
+void g_attrib_unref(GAttrib *attrib);
+
+GIOChannel *g_attrib_get_channel(GAttrib *attrib);
+
+gboolean g_attrib_set_disconnect_function(GAttrib *attrib,
+ GAttribDisconnectFunc disconnect, gpointer user_data);
+
+gboolean g_attrib_set_destroy_function(GAttrib *attrib,
+ GDestroyNotify destroy, gpointer user_data);
+
+guint g_attrib_send(GAttrib *attrib, guint id, guint8 opcode,
+ const guint8 *pdu, guint16 len, GAttribResultFunc func,
+ gpointer user_data, GDestroyNotify notify);
+
+gboolean g_attrib_cancel(GAttrib *attrib, guint id);
+gboolean g_attrib_cancel_all(GAttrib *attrib);
+
+gboolean g_attrib_set_debug(GAttrib *attrib,
+ GAttribDebugFunc func, gpointer user_data);
+
+guint g_attrib_register(GAttrib *attrib, guint8 opcode,
+ GAttribNotifyFunc func, gpointer user_data,
+ GDestroyNotify notify);
+
+gboolean g_attrib_is_encrypted(GAttrib *attrib);
+
+uint8_t *g_attrib_get_buffer(GAttrib *attrib, int *len);
+gboolean g_attrib_set_mtu(GAttrib *attrib, int mtu);
+
+gboolean g_attrib_unregister(GAttrib *attrib, guint id);
+gboolean g_attrib_unregister_all(GAttrib *attrib);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/attrib/gatttool.c b/attrib/gatttool.c
new file mode 100644
index 0000000..0dfbc04
--- /dev/null
+++ b/attrib/gatttool.c
@@ -0,0 +1,634 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2010 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 <errno.h>
+#include <glib.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/uuid.h>
+
+#include "att.h"
+#include "btio.h"
+#include "gattrib.h"
+#include "gatt.h"
+#include "gatttool.h"
+
+static gchar *opt_src = NULL;
+static gchar *opt_dst = NULL;
+static gchar *opt_value = NULL;
+static gchar *opt_sec_level = NULL;
+static bt_uuid_t *opt_uuid = NULL;
+static int opt_start = 0x0001;
+static int opt_end = 0xffff;
+static int opt_handle = -1;
+static int opt_mtu = 0;
+static int opt_psm = 0;
+static int opt_offset = 0;
+static gboolean opt_primary = FALSE;
+static gboolean opt_characteristics = FALSE;
+static gboolean opt_char_read = FALSE;
+static gboolean opt_listen = FALSE;
+static gboolean opt_char_desc = FALSE;
+static gboolean opt_char_write = FALSE;
+static gboolean opt_char_write_req = FALSE;
+static gboolean opt_interactive = FALSE;
+static GMainLoop *event_loop;
+static gboolean got_error = FALSE;
+
+struct characteristic_data {
+ GAttrib *attrib;
+ uint16_t start;
+ uint16_t end;
+};
+
+static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
+{
+ if (err) {
+ g_printerr("%s\n", err->message);
+ got_error = TRUE;
+ g_main_loop_quit(event_loop);
+ }
+}
+
+static void primary_all_cb(GSList *services, guint8 status, gpointer user_data)
+{
+ GSList *l;
+
+ if (status) {
+ g_printerr("Discover all primary services failed: %s\n",
+ att_ecode2str(status));
+ goto done;
+ }
+
+ for (l = services; l; l = l->next) {
+ struct att_primary *prim = l->data;
+ g_print("attr handle = 0x%04x, end grp handle = 0x%04x "
+ "uuid: %s\n", prim->start, prim->end, prim->uuid);
+ }
+
+done:
+ g_main_loop_quit(event_loop);
+}
+
+static void primary_by_uuid_cb(GSList *ranges, guint8 status,
+ gpointer user_data)
+{
+ GSList *l;
+
+ if (status != 0) {
+ g_printerr("Discover primary services by UUID failed: %s\n",
+ att_ecode2str(status));
+ goto done;
+ }
+
+ for (l = ranges; l; l = l->next) {
+ struct att_range *range = l->data;
+ g_print("Starting handle: %04x Ending handle: %04x\n",
+ range->start, range->end);
+ }
+
+done:
+ g_main_loop_quit(event_loop);
+}
+
+static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
+{
+ GAttrib *attrib = user_data;
+ uint8_t opdu[ATT_MAX_MTU];
+ uint16_t handle, i, olen = 0;
+
+ handle = att_get_u16(&pdu[1]);
+
+ switch (pdu[0]) {
+ case ATT_OP_HANDLE_NOTIFY:
+ g_print("Notification handle = 0x%04x value: ", handle);
+ break;
+ case ATT_OP_HANDLE_IND:
+ g_print("Indication handle = 0x%04x value: ", handle);
+ break;
+ default:
+ g_print("Invalid opcode\n");
+ return;
+ }
+
+ for (i = 3; i < len; i++)
+ g_print("%02x ", pdu[i]);
+
+ g_print("\n");
+
+ if (pdu[0] == ATT_OP_HANDLE_NOTIFY)
+ return;
+
+ olen = enc_confirmation(opdu, sizeof(opdu));
+
+ if (olen > 0)
+ g_attrib_send(attrib, 0, opdu[0], opdu, olen, NULL, NULL, NULL);
+}
+
+static gboolean listen_start(gpointer user_data)
+{
+ GAttrib *attrib = user_data;
+
+ g_attrib_register(attrib, ATT_OP_HANDLE_NOTIFY, events_handler,
+ attrib, NULL);
+ g_attrib_register(attrib, ATT_OP_HANDLE_IND, events_handler,
+ attrib, NULL);
+
+ return FALSE;
+}
+
+static gboolean primary(gpointer user_data)
+{
+ GAttrib *attrib = user_data;
+
+ if (opt_uuid)
+ gatt_discover_primary(attrib, opt_uuid, primary_by_uuid_cb,
+ NULL);
+ else
+ gatt_discover_primary(attrib, NULL, primary_all_cb, NULL);
+
+ return FALSE;
+}
+
+static void char_discovered_cb(GSList *characteristics, guint8 status,
+ gpointer user_data)
+{
+ GSList *l;
+
+ if (status) {
+ g_printerr("Discover all characteristics failed: %s\n",
+ att_ecode2str(status));
+ goto done;
+ }
+
+ for (l = characteristics; l; l = l->next) {
+ struct att_char *chars = l->data;
+
+ g_print("handle = 0x%04x, char properties = 0x%02x, char value "
+ "handle = 0x%04x, uuid = %s\n", chars->handle,
+ chars->properties, chars->value_handle, chars->uuid);
+ }
+
+done:
+ g_main_loop_quit(event_loop);
+}
+
+static gboolean characteristics(gpointer user_data)
+{
+ GAttrib *attrib = user_data;
+
+ gatt_discover_char(attrib, opt_start, opt_end, opt_uuid,
+ char_discovered_cb, NULL);
+
+ return FALSE;
+}
+
+static void char_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
+ gpointer user_data)
+{
+ uint8_t value[ATT_MAX_MTU];
+ int i, vlen;
+
+ if (status != 0) {
+ g_printerr("Characteristic value/descriptor read failed: %s\n",
+ att_ecode2str(status));
+ goto done;
+ }
+ if (!dec_read_resp(pdu, plen, value, &vlen)) {
+ g_printerr("Protocol error\n");
+ goto done;
+ }
+ g_print("Characteristic value/descriptor: ");
+ for (i = 0; i < vlen; i++)
+ g_print("%02x ", value[i]);
+ g_print("\n");
+
+done:
+ if (opt_listen == FALSE)
+ g_main_loop_quit(event_loop);
+}
+
+static void char_read_by_uuid_cb(guint8 status, const guint8 *pdu,
+ guint16 plen, gpointer user_data)
+{
+ struct characteristic_data *char_data = user_data;
+ struct att_data_list *list;
+ int i;
+
+ if (status == ATT_ECODE_ATTR_NOT_FOUND &&
+ char_data->start != opt_start)
+ goto done;
+
+ if (status != 0) {
+ g_printerr("Read characteristics by UUID failed: %s\n",
+ att_ecode2str(status));
+ goto done;
+ }
+
+ list = dec_read_by_type_resp(pdu, plen);
+ if (list == NULL)
+ goto done;
+
+ for (i = 0; i < list->num; i++) {
+ uint8_t *value = list->data[i];
+ int j;
+
+ char_data->start = att_get_u16(value) + 1;
+
+ g_print("handle: 0x%04x \t value: ", att_get_u16(value));
+ value += 2;
+ for (j = 0; j < list->len - 2; j++, value++)
+ g_print("%02x ", *value);
+ g_print("\n");
+ }
+
+ att_data_list_free(list);
+
+ gatt_read_char_by_uuid(char_data->attrib, char_data->start,
+ char_data->end, opt_uuid,
+ char_read_by_uuid_cb,
+ char_data);
+
+ return;
+done:
+ g_free(char_data);
+ g_main_loop_quit(event_loop);
+}
+
+static gboolean characteristics_read(gpointer user_data)
+{
+ GAttrib *attrib = user_data;
+
+ if (opt_uuid != NULL) {
+ struct characteristic_data *char_data;
+
+ char_data = g_new(struct characteristic_data, 1);
+ char_data->attrib = attrib;
+ char_data->start = opt_start;
+ char_data->end = opt_end;
+
+ gatt_read_char_by_uuid(attrib, opt_start, opt_end, opt_uuid,
+ char_read_by_uuid_cb, char_data);
+
+ return FALSE;
+ }
+
+ if (opt_handle <= 0) {
+ g_printerr("A valid handle is required\n");
+ g_main_loop_quit(event_loop);
+ return FALSE;
+ }
+
+ gatt_read_char(attrib, opt_handle, opt_offset, char_read_cb, attrib);
+
+ return FALSE;
+}
+
+static void mainloop_quit(gpointer user_data)
+{
+ uint8_t *value = user_data;
+
+ g_free(value);
+ g_main_loop_quit(event_loop);
+}
+
+static gboolean characteristics_write(gpointer user_data)
+{
+ GAttrib *attrib = user_data;
+ uint8_t *value;
+ size_t len;
+
+ if (opt_handle <= 0) {
+ g_printerr("A valid handle is required\n");
+ goto error;
+ }
+
+ if (opt_value == NULL || opt_value[0] == '\0') {
+ g_printerr("A value is required\n");
+ goto error;
+ }
+
+ len = gatt_attr_data_from_string(opt_value, &value);
+ if (len == 0) {
+ g_printerr("Invalid value\n");
+ goto error;
+ }
+
+ gatt_write_cmd(attrib, opt_handle, value, len, mainloop_quit, value);
+
+ return FALSE;
+
+error:
+ g_main_loop_quit(event_loop);
+ return FALSE;
+}
+
+static void char_write_req_cb(guint8 status, const guint8 *pdu, guint16 plen,
+ gpointer user_data)
+{
+ if (status != 0) {
+ g_printerr("Characteristic Write Request failed: "
+ "%s\n", att_ecode2str(status));
+ goto done;
+ }
+
+ if (!dec_write_resp(pdu, plen)) {
+ g_printerr("Protocol error\n");
+ goto done;
+ }
+
+ g_print("Characteristic value was written sucessfully\n");
+
+done:
+ if (opt_listen == FALSE)
+ g_main_loop_quit(event_loop);
+}
+
+static gboolean characteristics_write_req(gpointer user_data)
+{
+ GAttrib *attrib = user_data;
+ uint8_t *value;
+ size_t len;
+
+ if (opt_handle <= 0) {
+ g_printerr("A valid handle is required\n");
+ goto error;
+ }
+
+ if (opt_value == NULL || opt_value[0] == '\0') {
+ g_printerr("A value is required\n");
+ goto error;
+ }
+
+ len = gatt_attr_data_from_string(opt_value, &value);
+ if (len == 0) {
+ g_printerr("Invalid value\n");
+ goto error;
+ }
+
+ gatt_write_char(attrib, opt_handle, value, len, char_write_req_cb,
+ NULL);
+
+ return FALSE;
+
+error:
+ g_main_loop_quit(event_loop);
+ return FALSE;
+}
+
+static void char_desc_cb(guint8 status, const guint8 *pdu, guint16 plen,
+ gpointer user_data)
+{
+ struct att_data_list *list;
+ guint8 format;
+ int i;
+
+ if (status != 0) {
+ g_printerr("Discover all characteristic descriptors failed: "
+ "%s\n", att_ecode2str(status));
+ goto done;
+ }
+
+ list = dec_find_info_resp(pdu, plen, &format);
+ if (list == NULL)
+ goto done;
+
+ for (i = 0; i < list->num; i++) {
+ char uuidstr[MAX_LEN_UUID_STR];
+ uint16_t handle;
+ uint8_t *value;
+ bt_uuid_t uuid;
+
+ value = list->data[i];
+ handle = att_get_u16(value);
+
+ if (format == 0x01)
+ uuid = att_get_uuid16(&value[2]);
+ else
+ uuid = att_get_uuid128(&value[2]);
+
+ bt_uuid_to_string(&uuid, uuidstr, MAX_LEN_UUID_STR);
+ g_print("handle = 0x%04x, uuid = %s\n", handle, uuidstr);
+ }
+
+ att_data_list_free(list);
+
+done:
+ if (opt_listen == FALSE)
+ g_main_loop_quit(event_loop);
+}
+
+static gboolean characteristics_desc(gpointer user_data)
+{
+ GAttrib *attrib = user_data;
+
+ gatt_find_info(attrib, opt_start, opt_end, char_desc_cb, NULL);
+
+ return FALSE;
+}
+
+static gboolean parse_uuid(const char *key, const char *value,
+ gpointer user_data, GError **error)
+{
+ if (!value)
+ return FALSE;
+
+ opt_uuid = g_try_malloc(sizeof(bt_uuid_t));
+ if (opt_uuid == NULL)
+ return FALSE;
+
+ if (bt_string_to_uuid(opt_uuid, value) < 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static GOptionEntry primary_char_options[] = {
+ { "start", 's' , 0, G_OPTION_ARG_INT, &opt_start,
+ "Starting handle(optional)", "0x0001" },
+ { "end", 'e' , 0, G_OPTION_ARG_INT, &opt_end,
+ "Ending handle(optional)", "0xffff" },
+ { "uuid", 'u', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
+ parse_uuid, "UUID16 or UUID128(optional)", "0x1801"},
+ { NULL },
+};
+
+static GOptionEntry char_rw_options[] = {
+ { "handle", 'a' , 0, G_OPTION_ARG_INT, &opt_handle,
+ "Read/Write characteristic by handle(required)", "0x0001" },
+ { "value", 'n' , 0, G_OPTION_ARG_STRING, &opt_value,
+ "Write characteristic value (required for write operation)",
+ "0x0001" },
+ { "offset", 'o', 0, G_OPTION_ARG_INT, &opt_offset,
+ "Offset to long read characteristic by handle", "N"},
+ {NULL},
+};
+
+static GOptionEntry gatt_options[] = {
+ { "primary", 0, 0, G_OPTION_ARG_NONE, &opt_primary,
+ "Primary Service Discovery", NULL },
+ { "characteristics", 0, 0, G_OPTION_ARG_NONE, &opt_characteristics,
+ "Characteristics Discovery", NULL },
+ { "char-read", 0, 0, G_OPTION_ARG_NONE, &opt_char_read,
+ "Characteristics Value/Descriptor Read", NULL },
+ { "char-write", 0, 0, G_OPTION_ARG_NONE, &opt_char_write,
+ "Characteristics Value Write Without Response (Write Command)",
+ NULL },
+ { "char-write-req", 0, 0, G_OPTION_ARG_NONE, &opt_char_write_req,
+ "Characteristics Value Write (Write Request)", NULL },
+ { "char-desc", 0, 0, G_OPTION_ARG_NONE, &opt_char_desc,
+ "Characteristics Descriptor Discovery", NULL },
+ { "listen", 0, 0, G_OPTION_ARG_NONE, &opt_listen,
+ "Listen for notifications and indications", NULL },
+ { "interactive", 'I', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_NONE,
+ &opt_interactive, "Use interactive mode", NULL },
+ { NULL },
+};
+
+static GOptionEntry options[] = {
+ { "adapter", 'i', 0, G_OPTION_ARG_STRING, &opt_src,
+ "Specify local adapter interface", "hciX" },
+ { "device", 'b', 0, G_OPTION_ARG_STRING, &opt_dst,
+ "Specify remote Bluetooth address", "MAC" },
+ { "mtu", 'm', 0, G_OPTION_ARG_INT, &opt_mtu,
+ "Specify the MTU size", "MTU" },
+ { "psm", 'p', 0, G_OPTION_ARG_INT, &opt_psm,
+ "Specify the PSM for GATT/ATT over BR/EDR", "PSM" },
+ { "sec-level", 'l', 0, G_OPTION_ARG_STRING, &opt_sec_level,
+ "Set security level. Default: low", "[low | medium | high]"},
+ { NULL },
+};
+
+int main(int argc, char *argv[])
+{
+ GOptionContext *context;
+ GOptionGroup *gatt_group, *params_group, *char_rw_group;
+ GError *gerr = NULL;
+ GAttrib *attrib;
+ GIOChannel *chan;
+ GSourceFunc callback;
+
+ opt_sec_level = g_strdup("low");
+
+ context = g_option_context_new(NULL);
+ g_option_context_add_main_entries(context, options, NULL);
+
+ /* GATT commands */
+ gatt_group = g_option_group_new("gatt", "GATT commands",
+ "Show all GATT commands", NULL, NULL);
+ g_option_context_add_group(context, gatt_group);
+ g_option_group_add_entries(gatt_group, gatt_options);
+
+ /* Primary Services and Characteristics arguments */
+ params_group = g_option_group_new("params",
+ "Primary Services/Characteristics arguments",
+ "Show all Primary Services/Characteristics arguments",
+ NULL, NULL);
+ g_option_context_add_group(context, params_group);
+ g_option_group_add_entries(params_group, primary_char_options);
+
+ /* Characteristics value/descriptor read/write arguments */
+ char_rw_group = g_option_group_new("char-read-write",
+ "Characteristics Value/Descriptor Read/Write arguments",
+ "Show all Characteristics Value/Descriptor Read/Write "
+ "arguments",
+ NULL, NULL);
+ g_option_context_add_group(context, char_rw_group);
+ g_option_group_add_entries(char_rw_group, char_rw_options);
+
+ if (g_option_context_parse(context, &argc, &argv, &gerr) == FALSE) {
+ g_printerr("%s\n", gerr->message);
+ g_error_free(gerr);
+ }
+
+ if (opt_interactive) {
+ interactive(opt_src, opt_dst, opt_psm);
+ goto done;
+ }
+
+ if (opt_primary)
+ callback = primary;
+ else if (opt_characteristics)
+ callback = characteristics;
+ else if (opt_char_read)
+ callback = characteristics_read;
+ else if (opt_char_write)
+ callback = characteristics_write;
+ else if (opt_char_write_req)
+ callback = characteristics_write_req;
+ else if (opt_char_desc)
+ callback = characteristics_desc;
+ else {
+ gchar *help = g_option_context_get_help(context, TRUE, NULL);
+ g_print("%s\n", help);
+ g_free(help);
+ got_error = TRUE;
+ goto done;
+ }
+
+ chan = gatt_connect(opt_src, opt_dst, opt_sec_level,
+ opt_psm, opt_mtu, connect_cb);
+ if (chan == NULL) {
+ got_error = TRUE;
+ goto done;
+ }
+
+ attrib = g_attrib_new(chan);
+ g_io_channel_unref(chan);
+
+ event_loop = g_main_loop_new(NULL, FALSE);
+
+ if (opt_listen)
+ g_idle_add(listen_start, attrib);
+
+ g_idle_add(callback, attrib);
+
+ g_main_loop_run(event_loop);
+
+ g_attrib_unregister_all(attrib);
+
+ g_main_loop_unref(event_loop);
+
+ g_attrib_unref(attrib);
+
+done:
+ g_option_context_free(context);
+ g_free(opt_src);
+ g_free(opt_dst);
+ g_free(opt_uuid);
+ g_free(opt_sec_level);
+
+ if (got_error)
+ exit(EXIT_FAILURE);
+ else
+ exit(EXIT_SUCCESS);
+}
diff --git a/attrib/gatttool.h b/attrib/gatttool.h
new file mode 100644
index 0000000..89ac282
--- /dev/null
+++ b/attrib/gatttool.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ *
+ *
+ * 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
+ *
+ */
+
+int interactive(const gchar *src, const gchar *dst, gboolean le);
+GIOChannel *gatt_connect(const gchar *src, const gchar *dst,
+ const gchar *sec_level, int psm, int mtu,
+ BtIOConnect connect_cb);
+size_t gatt_attr_data_from_string(const char *str, uint8_t **data);
diff --git a/attrib/interactive.c b/attrib/interactive.c
new file mode 100644
index 0000000..3fafb1e
--- /dev/null
+++ b/attrib/interactive.c
@@ -0,0 +1,842 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ *
+ *
+ * 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
+ *
+ */
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdio.h>
+#include <glib.h>
+
+#include <bluetooth/uuid.h>
+
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#include "att.h"
+#include "btio.h"
+#include "gattrib.h"
+#include "gatt.h"
+#include "gatttool.h"
+
+static GIOChannel *iochannel = NULL;
+static GAttrib *attrib = NULL;
+static GMainLoop *event_loop;
+static GString *prompt;
+
+static gchar *opt_src = NULL;
+static gchar *opt_dst = NULL;
+static gchar *opt_sec_level = NULL;
+static int opt_psm = 0;
+static int opt_mtu = 0;
+
+struct characteristic_data {
+ uint16_t orig_start;
+ uint16_t start;
+ uint16_t end;
+ bt_uuid_t uuid;
+};
+
+static void cmd_help(int argcp, char **argvp);
+
+enum state {
+ STATE_DISCONNECTED,
+ STATE_CONNECTING,
+ STATE_CONNECTED
+} conn_state;
+
+static char *get_prompt(void)
+{
+ if (conn_state == STATE_CONNECTING) {
+ g_string_assign(prompt, "Connecting... ");
+ return prompt->str;
+ }
+
+ if (conn_state == STATE_CONNECTED)
+ g_string_assign(prompt, "[CON]");
+ else
+ g_string_assign(prompt, "[ ]");
+
+ if (opt_dst)
+ g_string_append_printf(prompt, "[%17s]", opt_dst);
+ else
+ g_string_append_printf(prompt, "[%17s]", "");
+
+ if (opt_psm)
+ g_string_append(prompt, "[BR]");
+ else
+ g_string_append(prompt, "[LE]");
+
+ g_string_append(prompt, "> ");
+
+ return prompt->str;
+}
+
+
+static void set_state(enum state st)
+{
+ conn_state = st;
+ rl_set_prompt(get_prompt());
+ rl_redisplay();
+}
+
+static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
+{
+ uint8_t opdu[ATT_MAX_MTU];
+ uint16_t handle, i, olen;
+
+ handle = att_get_u16(&pdu[1]);
+
+ printf("\n");
+ switch (pdu[0]) {
+ case ATT_OP_HANDLE_NOTIFY:
+ printf("Notification handle = 0x%04x value: ", handle);
+ break;
+ case ATT_OP_HANDLE_IND:
+ printf("Indication handle = 0x%04x value: ", handle);
+ break;
+ default:
+ printf("Invalid opcode\n");
+ return;
+ }
+
+ for (i = 3; i < len; i++)
+ printf("%02x ", pdu[i]);
+
+ printf("\n");
+ rl_forced_update_display();
+
+ if (pdu[0] == ATT_OP_HANDLE_NOTIFY)
+ return;
+
+ olen = enc_confirmation(opdu, sizeof(opdu));
+
+ if (olen > 0)
+ g_attrib_send(attrib, 0, opdu[0], opdu, olen, NULL, NULL, NULL);
+}
+
+static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
+{
+ if (err) {
+ printf("connect error: %s\n", err->message);
+ set_state(STATE_DISCONNECTED);
+ return;
+ }
+
+ attrib = g_attrib_new(iochannel);
+ g_attrib_register(attrib, ATT_OP_HANDLE_NOTIFY, events_handler,
+ attrib, NULL);
+ g_attrib_register(attrib, ATT_OP_HANDLE_IND, events_handler,
+ attrib, NULL);
+ set_state(STATE_CONNECTED);
+}
+
+static void primary_all_cb(GSList *services, guint8 status, gpointer user_data)
+{
+ GSList *l;
+
+ if (status) {
+ printf("Discover all primary services failed: %s\n",
+ att_ecode2str(status));
+ return;
+ }
+
+ printf("\n");
+ for (l = services; l; l = l->next) {
+ struct att_primary *prim = l->data;
+ printf("attr handle: 0x%04x, end grp handle: 0x%04x "
+ "uuid: %s\n", prim->start, prim->end, prim->uuid);
+ }
+
+ rl_forced_update_display();
+}
+
+static void primary_by_uuid_cb(GSList *ranges, guint8 status,
+ gpointer user_data)
+{
+ GSList *l;
+
+ if (status) {
+ printf("Discover primary services by UUID failed: %s\n",
+ att_ecode2str(status));
+ return;
+ }
+
+ printf("\n");
+ for (l = ranges; l; l = l->next) {
+ struct att_range *range = l->data;
+ g_print("Starting handle: 0x%04x Ending handle: 0x%04x\n",
+ range->start, range->end);
+ }
+
+ rl_forced_update_display();
+}
+
+static void char_cb(GSList *characteristics, guint8 status, gpointer user_data)
+{
+ GSList *l;
+
+ if (status) {
+ printf("Discover all characteristics failed: %s\n",
+ att_ecode2str(status));
+ return;
+ }
+
+ printf("\n");
+ for (l = characteristics; l; l = l->next) {
+ struct att_char *chars = l->data;
+
+ printf("handle: 0x%04x, char properties: 0x%02x, char value "
+ "handle: 0x%04x, uuid: %s\n", chars->handle,
+ chars->properties, chars->value_handle,
+ chars->uuid);
+ }
+
+ rl_forced_update_display();
+}
+
+static void char_desc_cb(guint8 status, const guint8 *pdu, guint16 plen,
+ gpointer user_data)
+{
+ struct att_data_list *list;
+ guint8 format;
+ int i;
+
+ if (status != 0) {
+ printf("Discover all characteristic descriptors failed: "
+ "%s\n", att_ecode2str(status));
+ return;
+ }
+
+ list = dec_find_info_resp(pdu, plen, &format);
+ if (list == NULL)
+ return;
+
+ printf("\n");
+ for (i = 0; i < list->num; i++) {
+ char uuidstr[MAX_LEN_UUID_STR];
+ uint16_t handle;
+ uint8_t *value;
+ bt_uuid_t uuid;
+
+ value = list->data[i];
+ handle = att_get_u16(value);
+
+ if (format == 0x01)
+ uuid = att_get_uuid16(&value[2]);
+ else
+ uuid = att_get_uuid128(&value[2]);
+
+ bt_uuid_to_string(&uuid, uuidstr, MAX_LEN_UUID_STR);
+ printf("handle: 0x%04x, uuid: %s\n", handle, uuidstr);
+ }
+
+ att_data_list_free(list);
+
+ rl_forced_update_display();
+}
+
+static void char_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
+ gpointer user_data)
+{
+ uint8_t value[ATT_MAX_MTU];
+ int i, vlen;
+
+ if (status != 0) {
+ printf("Characteristic value/descriptor read failed: %s\n",
+ att_ecode2str(status));
+ return;
+ }
+
+ if (!dec_read_resp(pdu, plen, value, &vlen)) {
+ printf("Protocol error\n");
+ return;
+ }
+
+ printf("\nCharacteristic value/descriptor: ");
+ for (i = 0; i < vlen; i++)
+ printf("%02x ", value[i]);
+ printf("\n");
+
+ rl_forced_update_display();
+}
+
+static void char_read_by_uuid_cb(guint8 status, const guint8 *pdu,
+ guint16 plen, gpointer user_data)
+{
+ struct characteristic_data *char_data = user_data;
+ struct att_data_list *list;
+ int i;
+
+ if (status == ATT_ECODE_ATTR_NOT_FOUND &&
+ char_data->start != char_data->orig_start)
+ goto done;
+
+ if (status != 0) {
+ printf("Read characteristics by UUID failed: %s\n",
+ att_ecode2str(status));
+ goto done;
+ }
+
+ list = dec_read_by_type_resp(pdu, plen);
+ if (list == NULL)
+ goto done;
+
+ for (i = 0; i < list->num; i++) {
+ uint8_t *value = list->data[i];
+ int j;
+
+ char_data->start = att_get_u16(value) + 1;
+
+ printf("\nhandle: 0x%04x \t value: ", att_get_u16(value));
+ value += 2;
+ for (j = 0; j < list->len - 2; j++, value++)
+ printf("%02x ", *value);
+ printf("\n");
+ }
+
+ att_data_list_free(list);
+
+ gatt_read_char_by_uuid(attrib, char_data->start, char_data->end,
+ &char_data->uuid, char_read_by_uuid_cb,
+ char_data);
+
+ rl_forced_update_display();
+
+ return;
+
+done:
+ g_free(char_data);
+}
+
+static void cmd_exit(int argcp, char **argvp)
+{
+ rl_callback_handler_remove();
+ g_main_loop_quit(event_loop);
+}
+
+static void cmd_connect(int argcp, char **argvp)
+{
+ if (conn_state != STATE_DISCONNECTED)
+ return;
+
+ if (argcp > 1) {
+ g_free(opt_dst);
+ opt_dst = g_strdup(argvp[1]);
+ }
+
+ if (opt_dst == NULL) {
+ printf("Remote Bluetooth address required\n");
+ return;
+ }
+
+ set_state(STATE_CONNECTING);
+ iochannel = gatt_connect(opt_src, opt_dst, opt_sec_level, opt_psm,
+ opt_mtu, connect_cb);
+ if (iochannel == NULL)
+ set_state(STATE_DISCONNECTED);
+}
+
+static void cmd_disconnect(int argcp, char **argvp)
+{
+ if (conn_state == STATE_DISCONNECTED)
+ return;
+
+ g_attrib_unref(attrib);
+ attrib = NULL;
+ opt_mtu = 0;
+
+ g_io_channel_shutdown(iochannel, FALSE, NULL);
+ g_io_channel_unref(iochannel);
+ iochannel = NULL;
+
+ set_state(STATE_DISCONNECTED);
+}
+
+static void cmd_primary(int argcp, char **argvp)
+{
+ bt_uuid_t uuid;
+
+ if (conn_state != STATE_CONNECTED) {
+ printf("Command failed: disconnected\n");
+ return;
+ }
+
+ if (argcp == 1) {
+ gatt_discover_primary(attrib, NULL, primary_all_cb, NULL);
+ return;
+ }
+
+ if (bt_string_to_uuid(&uuid, argvp[1]) < 0) {
+ printf("Invalid UUID\n");
+ return;
+ }
+
+ gatt_discover_primary(attrib, &uuid, primary_by_uuid_cb, NULL);
+}
+
+static int strtohandle(const char *src)
+{
+ char *e;
+ int dst;
+
+ errno = 0;
+ dst = strtoll(src, &e, 16);
+ if (errno != 0 || *e != '\0')
+ return -EINVAL;
+
+ return dst;
+}
+
+static void cmd_char(int argcp, char **argvp)
+{
+ int start = 0x0001;
+ int end = 0xffff;
+
+ if (conn_state != STATE_CONNECTED) {
+ printf("Command failed: disconnected\n");
+ return;
+ }
+
+ if (argcp > 1) {
+ start = strtohandle(argvp[1]);
+ if (start < 0) {
+ printf("Invalid start handle: %s\n", argvp[1]);
+ return;
+ }
+ }
+
+ if (argcp > 2) {
+ end = strtohandle(argvp[2]);
+ if (end < 0) {
+ printf("Invalid end handle: %s\n", argvp[2]);
+ return;
+ }
+ }
+
+ if (argcp > 3) {
+ bt_uuid_t uuid;
+
+ if (bt_string_to_uuid(&uuid, argvp[3]) < 0) {
+ printf("Invalid UUID\n");
+ return;
+ }
+
+ gatt_discover_char(attrib, start, end, &uuid, char_cb, NULL);
+ return;
+ }
+
+ gatt_discover_char(attrib, start, end, NULL, char_cb, NULL);
+}
+
+static void cmd_char_desc(int argcp, char **argvp)
+{
+ int start = 0x0001;
+ int end = 0xffff;
+
+ if (conn_state != STATE_CONNECTED) {
+ printf("Command failed: disconnected\n");
+ return;
+ }
+
+ if (argcp > 1) {
+ start = strtohandle(argvp[1]);
+ if (start < 0) {
+ printf("Invalid start handle: %s\n", argvp[1]);
+ return;
+ }
+ }
+
+ if (argcp > 2) {
+ end = strtohandle(argvp[2]);
+ if (end < 0) {
+ printf("Invalid end handle: %s\n", argvp[2]);
+ return;
+ }
+ }
+
+ gatt_find_info(attrib, start, end, char_desc_cb, NULL);
+}
+
+static void cmd_read_hnd(int argcp, char **argvp)
+{
+ int handle;
+ int offset = 0;
+
+ if (conn_state != STATE_CONNECTED) {
+ printf("Command failed: disconnected\n");
+ return;
+ }
+
+ if (argcp < 2) {
+ printf("Missing argument: handle\n");
+ return;
+ }
+
+ handle = strtohandle(argvp[1]);
+ if (handle < 0) {
+ printf("Invalid handle: %s\n", argvp[1]);
+ return;
+ }
+
+ if (argcp > 2) {
+ char *e;
+
+ errno = 0;
+ offset = strtol(argvp[2], &e, 0);
+ if (errno != 0 || *e != '\0') {
+ printf("Invalid offset: %s\n", argvp[2]);
+ return;
+ }
+ }
+
+ gatt_read_char(attrib, handle, offset, char_read_cb, attrib);
+}
+
+static void cmd_read_uuid(int argcp, char **argvp)
+{
+ struct characteristic_data *char_data;
+ int start = 0x0001;
+ int end = 0xffff;
+ bt_uuid_t uuid;
+
+ if (conn_state != STATE_CONNECTED) {
+ printf("Command failed: disconnected\n");
+ return;
+ }
+
+ if (argcp < 2) {
+ printf("Missing argument: UUID\n");
+ return;
+ }
+
+ if (bt_string_to_uuid(&uuid, argvp[1]) < 0) {
+ printf("Invalid UUID\n");
+ return;
+ }
+
+ if (argcp > 2) {
+ start = strtohandle(argvp[2]);
+ if (start < 0) {
+ printf("Invalid start handle: %s\n", argvp[1]);
+ return;
+ }
+ }
+
+ if (argcp > 3) {
+ end = strtohandle(argvp[3]);
+ if (end < 0) {
+ printf("Invalid end handle: %s\n", argvp[2]);
+ return;
+ }
+ }
+
+ char_data = g_new(struct characteristic_data, 1);
+ char_data->orig_start = start;
+ char_data->start = start;
+ char_data->end = end;
+ char_data->uuid = uuid;
+
+ gatt_read_char_by_uuid(attrib, start, end, &char_data->uuid,
+ char_read_by_uuid_cb, char_data);
+}
+
+static void char_write_req_cb(guint8 status, const guint8 *pdu, guint16 plen,
+ gpointer user_data)
+{
+ if (status != 0) {
+ printf("Characteristic Write Request failed: "
+ "%s\n", att_ecode2str(status));
+ return;
+ }
+
+ if (!dec_write_resp(pdu, plen)) {
+ printf("Protocol error\n");
+ return;
+ }
+
+ printf("Characteristic value was written successfully\n");
+}
+
+static void cmd_char_write(int argcp, char **argvp)
+{
+ uint8_t *value;
+ size_t plen;
+ int handle;
+
+ if (conn_state != STATE_CONNECTED) {
+ printf("Command failed: disconnected\n");
+ return;
+ }
+
+ if (argcp < 3) {
+ printf("Usage: %s <handle> <new value>\n", argvp[0]);
+ return;
+ }
+
+ handle = strtoll(argvp[1], NULL, 16);
+ if (errno != 0 || handle <= 0) {
+ printf("A valid handle is required\n");
+ return;
+ }
+
+ plen = gatt_attr_data_from_string(argvp[2], &value);
+ if (plen == 0) {
+ g_printerr("Invalid value\n");
+ return;
+ }
+
+ if (g_strcmp0("char-write-req", argvp[0]) == 0)
+ gatt_write_char(attrib, handle, value, plen,
+ char_write_req_cb, NULL);
+ else
+ gatt_write_char(attrib, handle, value, plen, NULL, NULL);
+
+ g_free(value);
+}
+
+static void cmd_sec_level(int argcp, char **argvp)
+{
+ GError *gerr = NULL;
+ BtIOSecLevel sec_level;
+
+ if (argcp < 2) {
+ printf("sec-level: %s\n", opt_sec_level);
+ return;
+ }
+
+ if (strcasecmp(argvp[1], "medium") == 0)
+ sec_level = BT_IO_SEC_MEDIUM;
+ else if (strcasecmp(argvp[1], "high") == 0)
+ sec_level = BT_IO_SEC_HIGH;
+ else if (strcasecmp(argvp[1], "low") == 0)
+ sec_level = BT_IO_SEC_LOW;
+ else {
+ printf("Allowed values: low | medium | high\n");
+ return;
+ }
+
+ g_free(opt_sec_level);
+ opt_sec_level = g_strdup(argvp[1]);
+
+ if (conn_state != STATE_CONNECTED)
+ return;
+
+ if (opt_psm) {
+ printf("It must be reconnected to this change take effect\n");
+ return;
+ }
+
+ bt_io_set(iochannel, BT_IO_L2CAP, &gerr,
+ BT_IO_OPT_SEC_LEVEL, sec_level,
+ BT_IO_OPT_INVALID);
+
+ if (gerr) {
+ printf("Error: %s\n", gerr->message);
+ g_error_free(gerr);
+ }
+}
+
+static void exchange_mtu_cb(guint8 status, const guint8 *pdu, guint16 plen,
+ gpointer user_data)
+{
+ uint16_t mtu;
+
+ if (status != 0) {
+ printf("Exchange MTU Request failed: %s\n",
+ att_ecode2str(status));
+ return;
+ }
+
+ if (!dec_mtu_resp(pdu, plen, &mtu)) {
+ printf("Protocol error\n");
+ return;
+ }
+
+ mtu = MIN(mtu, opt_mtu);
+ /* Set new value for MTU in client */
+ if (g_attrib_set_mtu(attrib, mtu))
+ printf("MTU was exchanged successfully: %d\n", mtu);
+ else
+ printf("Error exchanging MTU\n");
+}
+
+static void cmd_mtu(int argcp, char **argvp)
+{
+ if (conn_state != STATE_CONNECTED) {
+ printf("Command failed: not connected.\n");
+ return;
+ }
+
+ if (opt_psm) {
+ printf("Command failed: operation is only available for LE"
+ " transport.\n");
+ return;
+ }
+
+ if (argcp < 2) {
+ printf("Usage: mtu <value>\n");
+ return;
+ }
+
+ if (opt_mtu) {
+ printf("Command failed: MTU exchange can only occur once per"
+ " connection.\n");
+ return;
+ }
+
+ errno = 0;
+ opt_mtu = strtoll(argvp[1], NULL, 0);
+ if (errno != 0 || opt_mtu < ATT_DEFAULT_LE_MTU) {
+ printf("Invalid value. Minimum MTU size is %d\n",
+ ATT_DEFAULT_LE_MTU);
+ return;
+ }
+
+ gatt_exchange_mtu(attrib, opt_mtu, exchange_mtu_cb, NULL);
+}
+
+static struct {
+ const char *cmd;
+ void (*func)(int argcp, char **argvp);
+ const char *params;
+ const char *desc;
+} commands[] = {
+ { "help", cmd_help, "",
+ "Show this help"},
+ { "exit", cmd_exit, "",
+ "Exit interactive mode" },
+ { "connect", cmd_connect, "[address]",
+ "Connect to a remote device" },
+ { "disconnect", cmd_disconnect, "",
+ "Disconnect from a remote device" },
+ { "primary", cmd_primary, "[UUID]",
+ "Primary Service Discovery" },
+ { "characteristics", cmd_char, "[start hnd [end hnd [UUID]]]",
+ "Characteristics Discovery" },
+ { "char-desc", cmd_char_desc, "[start hnd] [end hnd]",
+ "Characteristics Descriptor Discovery" },
+ { "char-read-hnd", cmd_read_hnd, "<handle> [offset]",
+ "Characteristics Value/Descriptor Read by handle" },
+ { "char-read-uuid", cmd_read_uuid, "<UUID> [start hnd] [end hnd]",
+ "Characteristics Value/Descriptor Read by UUID" },
+ { "char-write-req", cmd_char_write, "<handle> <new value>",
+ "Characteristic Value Write (Write Request)" },
+ { "char-write-cmd", cmd_char_write, "<handle> <new value>",
+ "Characteristic Value Write (No response)" },
+ { "sec-level", cmd_sec_level, "[low | medium | high]",
+ "Set security level. Default: low" },
+ { "mtu", cmd_mtu, "<value>",
+ "Exchange MTU for GATT/ATT" },
+ { NULL, NULL, NULL}
+};
+
+static void cmd_help(int argcp, char **argvp)
+{
+ int i;
+
+ for (i = 0; commands[i].cmd; i++)
+ printf("%-15s %-30s %s\n", commands[i].cmd,
+ commands[i].params, commands[i].desc);
+}
+
+static void parse_line(char *line_read)
+{
+ gchar **argvp;
+ int argcp;
+ int i;
+
+ if (line_read == NULL) {
+ printf("\n");
+ cmd_exit(0, NULL);
+ return;
+ }
+
+ line_read = g_strstrip(line_read);
+
+ if (*line_read == '\0')
+ return;
+
+ add_history(line_read);
+
+ g_shell_parse_argv(line_read, &argcp, &argvp, NULL);
+
+ for (i = 0; commands[i].cmd; i++)
+ if (strcasecmp(commands[i].cmd, argvp[0]) == 0)
+ break;
+
+ if (commands[i].cmd)
+ commands[i].func(argcp, argvp);
+ else
+ printf("%s: command not found\n", argvp[0]);
+
+ g_strfreev(argvp);
+}
+
+static gboolean prompt_read(GIOChannel *chan, GIOCondition cond,
+ gpointer user_data)
+{
+ if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
+ g_io_channel_unref(chan);
+ return FALSE;
+ }
+
+ rl_callback_read_char();
+
+ return TRUE;
+}
+
+int interactive(const gchar *src, const gchar *dst, int psm)
+{
+ GIOChannel *pchan;
+ gint events;
+
+ opt_sec_level = g_strdup("low");
+
+ opt_src = g_strdup(src);
+ opt_dst = g_strdup(dst);
+ opt_psm = psm;
+
+ prompt = g_string_new(NULL);
+
+ event_loop = g_main_loop_new(NULL, FALSE);
+
+ pchan = g_io_channel_unix_new(fileno(stdin));
+ g_io_channel_set_close_on_unref(pchan, TRUE);
+ events = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+ g_io_add_watch(pchan, events, prompt_read, NULL);
+
+ rl_callback_handler_install(get_prompt(), parse_line);
+
+ g_main_loop_run(event_loop);
+
+ rl_callback_handler_remove();
+ cmd_disconnect(0, NULL);
+ g_io_channel_unref(pchan);
+ g_main_loop_unref(event_loop);
+ g_string_free(prompt, TRUE);
+
+ g_free(opt_src);
+ g_free(opt_dst);
+ g_free(opt_sec_level);
+
+ return 0;
+}
diff --git a/attrib/main.c b/attrib/main.c
new file mode 100644
index 0000000..6c946be
--- /dev/null
+++ b/attrib/main.c
@@ -0,0 +1,60 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2010 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 <errno.h>
+
+#include <gdbus.h>
+
+#include "plugin.h"
+#include "manager.h"
+
+static DBusConnection *connection;
+
+static int attrib_init(void)
+{
+ connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+ if (connection == NULL)
+ return -EIO;
+
+ if (attrib_manager_init(connection) < 0) {
+ dbus_connection_unref(connection);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void attrib_exit(void)
+{
+ attrib_manager_exit();
+
+ dbus_connection_unref(connection);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(attrib, VERSION,
+ BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, attrib_init, attrib_exit)
diff --git a/attrib/manager.c b/attrib/manager.c
new file mode 100644
index 0000000..a5a7de4
--- /dev/null
+++ b/attrib/manager.c
@@ -0,0 +1,105 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2010 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 <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include "../src/adapter.h"
+#include "../src/device.h"
+#include "hcid.h"
+
+#include "manager.h"
+#include "client.h"
+#include "example.h"
+
+#define GATT_UUID "00001801-0000-1000-8000-00805f9b34fb"
+
+static DBusConnection *connection;
+
+static int client_probe(struct btd_device *device, GSList *uuids)
+{
+ const sdp_record_t *rec;
+ int psm = -1;
+
+ rec = btd_device_get_record(device, GATT_UUID);
+ if (rec) {
+ sdp_list_t *list;
+ if (sdp_get_access_protos(rec, &list) < 0)
+ return -1;
+
+ psm = sdp_get_proto_port(list, L2CAP_UUID);
+
+ sdp_list_foreach(list, (sdp_list_func_t) sdp_list_free, NULL);
+ sdp_list_free(list, NULL);
+
+ if (psm < 0)
+ return -1;
+ }
+
+ return attrib_client_register(device, psm);
+}
+
+static void client_remove(struct btd_device *device)
+{
+ attrib_client_unregister(device);
+}
+
+static struct btd_device_driver client_driver = {
+ .name = "gatt-client",
+ .uuids = BTD_UUIDS(GATT_UUID),
+ .probe = client_probe,
+ .remove = client_remove,
+};
+
+int attrib_manager_init(DBusConnection *conn)
+{
+ connection = dbus_connection_ref(conn);
+
+ attrib_client_init(connection);
+
+ btd_register_device_driver(&client_driver);
+
+
+ if (main_opts.attrib_server)
+ return server_example_init();
+
+ return 0;
+}
+
+void attrib_manager_exit(void)
+{
+ btd_unregister_device_driver(&client_driver);
+
+ if (main_opts.attrib_server)
+ server_example_exit();
+
+ attrib_client_exit();
+
+ dbus_connection_unref(connection);
+}
diff --git a/attrib/manager.h b/attrib/manager.h
new file mode 100644
index 0000000..fabf342
--- /dev/null
+++ b/attrib/manager.h
@@ -0,0 +1,26 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2010 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
+ *
+ */
+
+int attrib_manager_init(DBusConnection *conn);
+void attrib_manager_exit(void);
diff --git a/attrib/utils.c b/attrib/utils.c
new file mode 100644
index 0000000..5f4444a
--- /dev/null
+++ b/attrib/utils.c
@@ -0,0 +1,126 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ *
+ *
+ * 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
+ *
+ */
+
+#include <stdlib.h>
+#include <glib.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/uuid.h>
+#include <bluetooth/sdp.h>
+
+#include "gattrib.h"
+#include "gatt.h"
+#include "btio.h"
+#include "gatttool.h"
+
+/* Minimum MTU for ATT connections */
+#define ATT_MIN_MTU_LE 23
+#define ATT_MIN_MTU_L2CAP 48
+
+GIOChannel *gatt_connect(const gchar *src, const gchar *dst,
+ const gchar *sec_level, int psm, int mtu,
+ BtIOConnect connect_cb)
+{
+ GIOChannel *chan;
+ bdaddr_t sba, dba;
+ GError *err = NULL;
+ BtIOSecLevel sec;
+ int minimum_mtu;
+
+ /* This check is required because currently setsockopt() returns no
+ * errors for MTU values smaller than the allowed minimum. */
+ minimum_mtu = psm ? ATT_MIN_MTU_L2CAP : ATT_MIN_MTU_LE;
+ if (mtu != 0 && mtu < minimum_mtu) {
+ g_printerr("MTU cannot be smaller than %d\n", minimum_mtu);
+ return NULL;
+ }
+
+ /* Remote device */
+ if (dst == NULL) {
+ g_printerr("Remote Bluetooth address required\n");
+ return NULL;
+ }
+ str2ba(dst, &dba);
+
+ /* Local adapter */
+ if (src != NULL) {
+ if (!strncmp(src, "hci", 3))
+ hci_devba(atoi(src + 3), &sba);
+ else
+ str2ba(src, &sba);
+ } else
+ bacpy(&sba, BDADDR_ANY);
+
+ if (strcmp(sec_level, "medium") == 0)
+ sec = BT_IO_SEC_MEDIUM;
+ else if (strcmp(sec_level, "high") == 0)
+ sec = BT_IO_SEC_HIGH;
+ else
+ sec = BT_IO_SEC_LOW;
+
+ if (psm == 0)
+ chan = bt_io_connect(BT_IO_L2CAP, connect_cb, NULL, NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &sba,
+ BT_IO_OPT_DEST_BDADDR, &dba,
+ BT_IO_OPT_CID, GATT_CID,
+ BT_IO_OPT_OMTU, mtu,
+ BT_IO_OPT_SEC_LEVEL, sec,
+ BT_IO_OPT_INVALID);
+ else
+ chan = bt_io_connect(BT_IO_L2CAP, connect_cb, NULL, NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &sba,
+ BT_IO_OPT_DEST_BDADDR, &dba,
+ BT_IO_OPT_PSM, psm,
+ BT_IO_OPT_OMTU, mtu,
+ BT_IO_OPT_SEC_LEVEL, sec,
+ BT_IO_OPT_INVALID);
+
+ if (err) {
+ g_printerr("%s\n", err->message);
+ g_error_free(err);
+ return NULL;
+ }
+
+ return chan;
+}
+
+size_t gatt_attr_data_from_string(const char *str, uint8_t **data)
+{
+ char tmp[3];
+ size_t size, i;
+
+ size = strlen(str) / 2;
+ *data = g_try_malloc0(size);
+ if (*data == NULL)
+ return 0;
+
+ tmp[2] = '\0';
+ for (i = 0; i < size; i++) {
+ memcpy(tmp, str + (i * 2), 2);
+ (*data)[i] = (uint8_t) strtol(tmp, NULL, 16);
+ }
+
+ return size;
+}
diff --git a/audio/a2dp-codecs.h b/audio/a2dp-codecs.h
new file mode 100644
index 0000000..e44634e
--- /dev/null
+++ b/audio/a2dp-codecs.h
@@ -0,0 +1,116 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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
+ *
+ */
+
+#define A2DP_CODEC_SBC 0x00
+#define A2DP_CODEC_MPEG12 0x01
+#define A2DP_CODEC_MPEG24 0x02
+#define A2DP_CODEC_ATRAC 0x03
+
+#define SBC_SAMPLING_FREQ_16000 (1 << 3)
+#define SBC_SAMPLING_FREQ_32000 (1 << 2)
+#define SBC_SAMPLING_FREQ_44100 (1 << 1)
+#define SBC_SAMPLING_FREQ_48000 1
+
+#define SBC_CHANNEL_MODE_MONO (1 << 3)
+#define SBC_CHANNEL_MODE_DUAL_CHANNEL (1 << 2)
+#define SBC_CHANNEL_MODE_STEREO (1 << 1)
+#define SBC_CHANNEL_MODE_JOINT_STEREO 1
+
+#define SBC_BLOCK_LENGTH_4 (1 << 3)
+#define SBC_BLOCK_LENGTH_8 (1 << 2)
+#define SBC_BLOCK_LENGTH_12 (1 << 1)
+#define SBC_BLOCK_LENGTH_16 1
+
+#define SBC_SUBBANDS_4 (1 << 1)
+#define SBC_SUBBANDS_8 1
+
+#define SBC_ALLOCATION_SNR (1 << 1)
+#define SBC_ALLOCATION_LOUDNESS 1
+
+#define MPEG_CHANNEL_MODE_MONO (1 << 3)
+#define MPEG_CHANNEL_MODE_DUAL_CHANNEL (1 << 2)
+#define MPEG_CHANNEL_MODE_STEREO (1 << 1)
+#define MPEG_CHANNEL_MODE_JOINT_STEREO 1
+
+#define MPEG_LAYER_MP1 (1 << 2)
+#define MPEG_LAYER_MP2 (1 << 1)
+#define MPEG_LAYER_MP3 1
+
+#define MPEG_SAMPLING_FREQ_16000 (1 << 5)
+#define MPEG_SAMPLING_FREQ_22050 (1 << 4)
+#define MPEG_SAMPLING_FREQ_24000 (1 << 3)
+#define MPEG_SAMPLING_FREQ_32000 (1 << 2)
+#define MPEG_SAMPLING_FREQ_44100 (1 << 1)
+#define MPEG_SAMPLING_FREQ_48000 1
+
+#define MAX_BITPOOL 64
+#define MIN_BITPOOL 2
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+typedef struct {
+ uint8_t channel_mode:4;
+ uint8_t frequency:4;
+ uint8_t allocation_method:2;
+ uint8_t subbands:2;
+ uint8_t block_length:4;
+ uint8_t min_bitpool;
+ uint8_t max_bitpool;
+} __attribute__ ((packed)) a2dp_sbc_t;
+
+typedef struct {
+ uint8_t channel_mode:4;
+ uint8_t crc:1;
+ uint8_t layer:3;
+ uint8_t frequency:6;
+ uint8_t mpf:1;
+ uint8_t rfa:1;
+ uint16_t bitrate;
+} __attribute__ ((packed)) a2dp_mpeg_t;
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+typedef struct {
+ uint8_t frequency:4;
+ uint8_t channel_mode:4;
+ uint8_t block_length:4;
+ uint8_t subbands:2;
+ uint8_t allocation_method:2;
+ uint8_t min_bitpool;
+ uint8_t max_bitpool;
+} __attribute__ ((packed)) a2dp_sbc_t;
+
+typedef struct {
+ uint8_t layer:3;
+ uint8_t crc:1;
+ uint8_t channel_mode:4;
+ uint8_t rfa:1;
+ uint8_t mpf:1;
+ uint8_t frequency:6;
+ uint16_t bitrate;
+} __attribute__ ((packed)) a2dp_mpeg_t;
+
+#else
+#error "Unknown byte order"
+#endif
diff --git a/audio/a2dp.c b/audio/a2dp.c
new file mode 100644
index 0000000..9d9c61d
--- /dev/null
+++ b/audio/a2dp.c
@@ -0,0 +1,2361 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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 <stdlib.h>
+#include <errno.h>
+
+#include <dbus/dbus.h>
+#include <glib.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include "log.h"
+#include "device.h"
+#include "manager.h"
+#include "avdtp.h"
+#include "sink.h"
+#include "source.h"
+#include "unix.h"
+#include "media.h"
+#include "transport.h"
+#include "a2dp.h"
+#include "sdpd.h"
+
+/* The duration that streams without users are allowed to stay in
+ * STREAMING state. */
+#define SUSPEND_TIMEOUT 5
+#define RECONFIGURE_TIMEOUT 500
+
+#ifndef MIN
+# define MIN(x, y) ((x) < (y) ? (x) : (y))
+#endif
+
+#ifndef MAX
+# define MAX(x, y) ((x) > (y) ? (x) : (y))
+#endif
+
+struct a2dp_sep {
+ struct a2dp_server *server;
+ struct media_endpoint *endpoint;
+ uint8_t type;
+ uint8_t codec;
+ struct avdtp_local_sep *lsep;
+ struct avdtp *session;
+ struct avdtp_stream *stream;
+ guint suspend_timer;
+ gboolean delay_reporting;
+ gboolean locked;
+ gboolean suspending;
+ gboolean starting;
+};
+
+struct a2dp_setup_cb {
+ struct a2dp_setup *setup;
+ a2dp_select_cb_t select_cb;
+ a2dp_config_cb_t config_cb;
+ a2dp_stream_cb_t resume_cb;
+ a2dp_stream_cb_t suspend_cb;
+ void *user_data;
+ unsigned int id;
+};
+
+struct a2dp_setup {
+ struct audio_device *dev;
+ struct avdtp *session;
+ struct a2dp_sep *sep;
+ struct avdtp_remote_sep *rsep;
+ struct avdtp_stream *stream;
+ struct avdtp_error *err;
+ avdtp_set_configuration_cb setconf_cb;
+ GSList *caps;
+ gboolean reconfigure;
+ gboolean start;
+ GSList *cb;
+ int ref;
+};
+
+static DBusConnection *connection = NULL;
+
+struct a2dp_server {
+ bdaddr_t src;
+ GSList *sinks;
+ GSList *sources;
+ uint32_t source_record_id;
+ uint32_t sink_record_id;
+ uint16_t version;
+ gboolean sink_enabled;
+ gboolean source_enabled;
+};
+
+static GSList *servers = NULL;
+static GSList *setups = NULL;
+static unsigned int cb_id = 0;
+
+static struct a2dp_setup *setup_ref(struct a2dp_setup *setup)
+{
+ setup->ref++;
+
+ DBG("%p: ref=%d", setup, setup->ref);
+
+ return setup;
+}
+
+static struct audio_device *a2dp_get_dev(struct avdtp *session)
+{
+ bdaddr_t src, dst;
+
+ avdtp_get_peers(session, &src, &dst);
+
+ return manager_find_device(NULL, &src, &dst, NULL, FALSE);
+}
+
+static struct a2dp_setup *setup_new(struct avdtp *session)
+{
+ struct audio_device *dev;
+ struct a2dp_setup *setup;
+
+ dev = a2dp_get_dev(session);
+ if (!dev) {
+ error("Unable to create setup");
+ return NULL;
+ }
+
+ setup = g_new0(struct a2dp_setup, 1);
+ setup->session = avdtp_ref(session);
+ setup->dev = a2dp_get_dev(session);
+ setups = g_slist_append(setups, setup);
+
+ return setup;
+}
+
+static void setup_free(struct a2dp_setup *s)
+{
+ DBG("%p", s);
+
+ setups = g_slist_remove(setups, s);
+ if (s->session)
+ avdtp_unref(s->session);
+ g_slist_foreach(s->cb, (GFunc) g_free, NULL);
+ g_slist_free(s->cb);
+ g_slist_foreach(s->caps, (GFunc) g_free, NULL);
+ g_slist_free(s->caps);
+ g_free(s);
+}
+
+static void setup_unref(struct a2dp_setup *setup)
+{
+ setup->ref--;
+
+ DBG("%p: ref=%d", setup, setup->ref);
+
+ if (setup->ref > 0)
+ return;
+
+ setup_free(setup);
+}
+
+static struct a2dp_setup_cb *setup_cb_new(struct a2dp_setup *setup)
+{
+ struct a2dp_setup_cb *cb;
+
+ cb = g_new0(struct a2dp_setup_cb, 1);
+ cb->setup = setup;
+ cb->id = ++cb_id;
+
+ setup->cb = g_slist_append(setup->cb, cb);
+ return cb;
+}
+
+static void setup_cb_free(struct a2dp_setup_cb *cb)
+{
+ struct a2dp_setup *setup = cb->setup;
+
+ setup->cb = g_slist_remove(setup->cb, cb);
+ setup_unref(cb->setup);
+ g_free(cb);
+}
+
+static gboolean finalize_config(struct a2dp_setup *s)
+{
+ GSList *l;
+ struct avdtp_stream *stream = s->err ? NULL : s->stream;
+
+ for (l = s->cb; l != NULL; ) {
+ struct a2dp_setup_cb *cb = l->data;
+
+ l = l->next;
+
+ if (!cb->config_cb)
+ continue;
+
+ cb->config_cb(s->session, s->sep, stream, s->err,
+ cb->user_data);
+ setup_cb_free(cb);
+ }
+
+ return FALSE;
+}
+
+static gboolean finalize_config_errno(struct a2dp_setup *s, int err)
+{
+ struct avdtp_error avdtp_err;
+
+ avdtp_error_init(&avdtp_err, AVDTP_ERRNO, -err);
+ s->err = err ? &avdtp_err : NULL;
+
+ return finalize_config(s);
+}
+
+static gboolean finalize_resume(struct a2dp_setup *s)
+{
+ GSList *l;
+
+ for (l = s->cb; l != NULL; ) {
+ struct a2dp_setup_cb *cb = l->data;
+
+ l = l->next;
+
+ if (!cb->resume_cb)
+ continue;
+
+ cb->resume_cb(s->session, s->err, cb->user_data);
+ setup_cb_free(cb);
+ }
+
+ return FALSE;
+}
+
+static gboolean finalize_resume_errno(struct a2dp_setup *s, int err)
+{
+ struct avdtp_error avdtp_err;
+
+ avdtp_error_init(&avdtp_err, AVDTP_ERRNO, -err);
+ s->err = err ? &avdtp_err : NULL;
+
+ return finalize_resume(s);
+}
+
+static gboolean finalize_suspend(struct a2dp_setup *s)
+{
+ GSList *l;
+
+ for (l = s->cb; l != NULL; ) {
+ struct a2dp_setup_cb *cb = l->data;
+
+ l = l->next;
+
+ if (!cb->suspend_cb)
+ continue;
+
+ cb->suspend_cb(s->session, s->err, cb->user_data);
+ setup_cb_free(cb);
+ }
+
+ return FALSE;
+}
+
+static gboolean finalize_suspend_errno(struct a2dp_setup *s, int err)
+{
+ struct avdtp_error avdtp_err;
+
+ avdtp_error_init(&avdtp_err, AVDTP_ERRNO, -err);
+ s->err = err ? &avdtp_err : NULL;
+
+ return finalize_suspend(s);
+}
+
+static gboolean finalize_select(struct a2dp_setup *s)
+{
+ GSList *l;
+
+ for (l = s->cb; l != NULL; ) {
+ struct a2dp_setup_cb *cb = l->data;
+
+ l = l->next;
+
+ if (!cb->select_cb)
+ continue;
+
+ cb->select_cb(s->session, s->sep, s->caps, cb->user_data);
+ setup_cb_free(cb);
+ }
+
+ return FALSE;
+}
+
+static struct a2dp_setup *find_setup_by_session(struct avdtp *session)
+{
+ GSList *l;
+
+ for (l = setups; l != NULL; l = l->next) {
+ struct a2dp_setup *setup = l->data;
+
+ if (setup->session == session)
+ return setup;
+ }
+
+ return NULL;
+}
+
+static struct a2dp_setup *a2dp_setup_get(struct avdtp *session)
+{
+ struct a2dp_setup *setup;
+
+ setup = find_setup_by_session(session);
+ if (!setup) {
+ setup = setup_new(session);
+ if (!setup)
+ return NULL;
+ }
+
+ return setup_ref(setup);
+}
+
+static struct a2dp_setup *find_setup_by_dev(struct audio_device *dev)
+{
+ GSList *l;
+
+ for (l = setups; l != NULL; l = l->next) {
+ struct a2dp_setup *setup = l->data;
+
+ if (setup->dev == dev)
+ return setup;
+ }
+
+ return NULL;
+}
+
+static void stream_state_changed(struct avdtp_stream *stream,
+ avdtp_state_t old_state,
+ avdtp_state_t new_state,
+ struct avdtp_error *err,
+ void *user_data)
+{
+ struct a2dp_sep *sep = user_data;
+
+ if (new_state != AVDTP_STATE_IDLE)
+ return;
+
+ if (sep->suspend_timer) {
+ g_source_remove(sep->suspend_timer);
+ sep->suspend_timer = 0;
+ }
+
+ if (sep->session) {
+ avdtp_unref(sep->session);
+ sep->session = NULL;
+ }
+
+ if (sep->endpoint)
+ media_endpoint_clear_configuration(sep->endpoint);
+
+ sep->stream = NULL;
+
+}
+
+static gboolean auto_config(gpointer data)
+{
+ struct a2dp_setup *setup = data;
+ struct avdtp_error *err = NULL;
+
+ /* Check if configuration was aborted */
+ if (setup->sep->stream == NULL)
+ return FALSE;
+
+ if (setup->err != NULL) {
+ err = setup->err;
+ goto done;
+ }
+
+ avdtp_stream_add_cb(setup->session, setup->stream,
+ stream_state_changed, setup->sep);
+
+ if (setup->sep->type == AVDTP_SEP_TYPE_SOURCE)
+ sink_new_stream(setup->dev, setup->session, setup->stream);
+ else
+ source_new_stream(setup->dev, setup->session, setup->stream);
+
+done:
+ if (setup->setconf_cb)
+ setup->setconf_cb(setup->session, setup->stream, setup->err);
+
+ finalize_config(setup);
+
+ if (err)
+ g_free(err);
+
+ setup_unref(setup);
+
+ return FALSE;
+}
+
+static gboolean sbc_setconf_ind(struct avdtp *session,
+ struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream,
+ GSList *caps,
+ avdtp_set_configuration_cb cb,
+ void *user_data)
+{
+ struct a2dp_sep *a2dp_sep = user_data;
+ struct a2dp_setup *setup;
+
+ if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+ DBG("Sink %p: Set_Configuration_Ind", sep);
+ else
+ DBG("Source %p: Set_Configuration_Ind", sep);
+
+ setup = a2dp_setup_get(session);
+ if (!setup)
+ return FALSE;
+
+ a2dp_sep->stream = stream;
+ setup->sep = a2dp_sep;
+ setup->stream = stream;
+ setup->setconf_cb = cb;
+
+ /* Check valid settings */
+ for (; caps != NULL; caps = g_slist_next(caps)) {
+ struct avdtp_service_capability *cap = caps->data;
+ struct avdtp_media_codec_capability *codec_cap;
+ struct sbc_codec_cap *sbc_cap;
+
+ if (cap->category == AVDTP_DELAY_REPORTING &&
+ !a2dp_sep->delay_reporting) {
+ setup->err = g_new(struct avdtp_error, 1);
+ avdtp_error_init(setup->err, AVDTP_DELAY_REPORTING,
+ AVDTP_UNSUPPORTED_CONFIGURATION);
+ goto done;
+ }
+
+ if (cap->category != AVDTP_MEDIA_CODEC)
+ continue;
+
+ if (cap->length < sizeof(struct sbc_codec_cap))
+ continue;
+
+ codec_cap = (void *) cap->data;
+
+ if (codec_cap->media_codec_type != A2DP_CODEC_SBC)
+ continue;
+
+ sbc_cap = (void *) codec_cap;
+
+ if (sbc_cap->min_bitpool < MIN_BITPOOL ||
+ sbc_cap->max_bitpool > MAX_BITPOOL) {
+ setup->err = g_new(struct avdtp_error, 1);
+ avdtp_error_init(setup->err, AVDTP_MEDIA_CODEC,
+ AVDTP_UNSUPPORTED_CONFIGURATION);
+ goto done;
+ }
+ }
+
+done:
+ g_idle_add(auto_config, setup);
+ return TRUE;
+}
+
+static gboolean sbc_getcap_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+ gboolean get_all, GSList **caps, uint8_t *err,
+ void *user_data)
+{
+ struct a2dp_sep *a2dp_sep = user_data;
+ struct avdtp_service_capability *media_transport, *media_codec;
+ struct sbc_codec_cap sbc_cap;
+
+ if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+ DBG("Sink %p: Get_Capability_Ind", sep);
+ else
+ DBG("Source %p: Get_Capability_Ind", sep);
+
+ *caps = NULL;
+
+ media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
+ NULL, 0);
+
+ *caps = g_slist_append(*caps, media_transport);
+
+ memset(&sbc_cap, 0, sizeof(struct sbc_codec_cap));
+
+ sbc_cap.cap.media_type = AVDTP_MEDIA_TYPE_AUDIO;
+ sbc_cap.cap.media_codec_type = A2DP_CODEC_SBC;
+
+ sbc_cap.frequency = ( SBC_SAMPLING_FREQ_48000 |
+ SBC_SAMPLING_FREQ_44100 |
+ SBC_SAMPLING_FREQ_32000 |
+ SBC_SAMPLING_FREQ_16000 );
+
+ sbc_cap.channel_mode = ( SBC_CHANNEL_MODE_JOINT_STEREO |
+ SBC_CHANNEL_MODE_STEREO |
+ SBC_CHANNEL_MODE_DUAL_CHANNEL |
+ SBC_CHANNEL_MODE_MONO );
+
+ sbc_cap.block_length = ( SBC_BLOCK_LENGTH_16 |
+ SBC_BLOCK_LENGTH_12 |
+ SBC_BLOCK_LENGTH_8 |
+ SBC_BLOCK_LENGTH_4 );
+
+ sbc_cap.subbands = ( SBC_SUBBANDS_8 | SBC_SUBBANDS_4 );
+
+ sbc_cap.allocation_method = ( SBC_ALLOCATION_LOUDNESS |
+ SBC_ALLOCATION_SNR );
+
+ sbc_cap.min_bitpool = MIN_BITPOOL;
+ sbc_cap.max_bitpool = MAX_BITPOOL;
+
+ media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap,
+ sizeof(sbc_cap));
+
+ *caps = g_slist_append(*caps, media_codec);
+
+ if (get_all) {
+ struct avdtp_service_capability *delay_reporting;
+ delay_reporting = avdtp_service_cap_new(AVDTP_DELAY_REPORTING,
+ NULL, 0);
+ *caps = g_slist_append(*caps, delay_reporting);
+ }
+
+ return TRUE;
+}
+
+static gboolean mpeg_setconf_ind(struct avdtp *session,
+ struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream,
+ GSList *caps,
+ avdtp_set_configuration_cb cb,
+ void *user_data)
+{
+ struct a2dp_sep *a2dp_sep = user_data;
+ struct a2dp_setup *setup;
+
+ if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+ DBG("Sink %p: Set_Configuration_Ind", sep);
+ else
+ DBG("Source %p: Set_Configuration_Ind", sep);
+
+ setup = a2dp_setup_get(session);
+ if (!setup)
+ return FALSE;
+
+ a2dp_sep->stream = stream;
+ setup->sep = a2dp_sep;
+ setup->stream = stream;
+ setup->setconf_cb = cb;
+
+ for (; caps != NULL; caps = g_slist_next(caps)) {
+ struct avdtp_service_capability *cap = caps->data;
+
+ if (cap->category == AVDTP_DELAY_REPORTING &&
+ !a2dp_sep->delay_reporting) {
+ setup->err = g_new(struct avdtp_error, 1);
+ avdtp_error_init(setup->err, AVDTP_DELAY_REPORTING,
+ AVDTP_UNSUPPORTED_CONFIGURATION);
+ goto done;
+ }
+ }
+
+done:
+ g_idle_add(auto_config, setup);
+ return TRUE;
+}
+
+static gboolean mpeg_getcap_ind(struct avdtp *session,
+ struct avdtp_local_sep *sep,
+ gboolean get_all,
+ GSList **caps, uint8_t *err, void *user_data)
+{
+ struct a2dp_sep *a2dp_sep = user_data;
+ struct avdtp_service_capability *media_transport, *media_codec;
+ struct mpeg_codec_cap mpeg_cap;
+
+ if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+ DBG("Sink %p: Get_Capability_Ind", sep);
+ else
+ DBG("Source %p: Get_Capability_Ind", sep);
+
+ *caps = NULL;
+
+ media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
+ NULL, 0);
+
+ *caps = g_slist_append(*caps, media_transport);
+
+ memset(&mpeg_cap, 0, sizeof(struct mpeg_codec_cap));
+
+ mpeg_cap.cap.media_type = AVDTP_MEDIA_TYPE_AUDIO;
+ mpeg_cap.cap.media_codec_type = A2DP_CODEC_MPEG12;
+
+ mpeg_cap.frequency = ( MPEG_SAMPLING_FREQ_48000 |
+ MPEG_SAMPLING_FREQ_44100 |
+ MPEG_SAMPLING_FREQ_32000 |
+ MPEG_SAMPLING_FREQ_24000 |
+ MPEG_SAMPLING_FREQ_22050 |
+ MPEG_SAMPLING_FREQ_16000 );
+
+ mpeg_cap.channel_mode = ( MPEG_CHANNEL_MODE_JOINT_STEREO |
+ MPEG_CHANNEL_MODE_STEREO |
+ MPEG_CHANNEL_MODE_DUAL_CHANNEL |
+ MPEG_CHANNEL_MODE_MONO );
+
+ mpeg_cap.layer = ( MPEG_LAYER_MP3 | MPEG_LAYER_MP2 | MPEG_LAYER_MP1 );
+
+ mpeg_cap.bitrate = 0xFFFF;
+
+ media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &mpeg_cap,
+ sizeof(mpeg_cap));
+
+ *caps = g_slist_append(*caps, media_codec);
+
+ if (get_all) {
+ struct avdtp_service_capability *delay_reporting;
+ delay_reporting = avdtp_service_cap_new(AVDTP_DELAY_REPORTING,
+ NULL, 0);
+ *caps = g_slist_append(*caps, delay_reporting);
+ }
+
+ return TRUE;
+}
+
+static void endpoint_setconf_cb(struct media_endpoint *endpoint, void *ret,
+ int size, void *user_data)
+{
+ struct a2dp_setup *setup = user_data;
+
+ if (ret == NULL) {
+ setup->err = g_new(struct avdtp_error, 1);
+ avdtp_error_init(setup->err, AVDTP_MEDIA_CODEC,
+ AVDTP_UNSUPPORTED_CONFIGURATION);
+ }
+
+ auto_config(setup);
+}
+
+static gboolean endpoint_setconf_ind(struct avdtp *session,
+ struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream,
+ GSList *caps,
+ avdtp_set_configuration_cb cb,
+ void *user_data)
+{
+ struct a2dp_sep *a2dp_sep = user_data;
+ struct a2dp_setup *setup;
+
+ if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+ DBG("Sink %p: Set_Configuration_Ind", sep);
+ else
+ DBG("Source %p: Set_Configuration_Ind", sep);
+
+ setup = a2dp_setup_get(session);
+ if (!session)
+ return FALSE;
+
+ a2dp_sep->stream = stream;
+ setup->sep = a2dp_sep;
+ setup->stream = stream;
+ setup->setconf_cb = cb;
+
+ for (; caps != NULL; caps = g_slist_next(caps)) {
+ struct avdtp_service_capability *cap = caps->data;
+ struct avdtp_media_codec_capability *codec;
+ gboolean ret;
+
+ if (cap->category == AVDTP_DELAY_REPORTING &&
+ !a2dp_sep->delay_reporting) {
+ setup->err = g_new(struct avdtp_error, 1);
+ avdtp_error_init(setup->err, AVDTP_DELAY_REPORTING,
+ AVDTP_UNSUPPORTED_CONFIGURATION);
+ goto done;
+ }
+
+ if (cap->category != AVDTP_MEDIA_CODEC)
+ continue;
+
+ codec = (struct avdtp_media_codec_capability *) cap->data;
+
+ if (codec->media_codec_type != a2dp_sep->codec) {
+ setup->err = g_new(struct avdtp_error, 1);
+ avdtp_error_init(setup->err, AVDTP_MEDIA_CODEC,
+ AVDTP_UNSUPPORTED_CONFIGURATION);
+ goto done;
+ }
+
+ ret = media_endpoint_set_configuration(a2dp_sep->endpoint,
+ setup->dev, codec->data,
+ cap->length - sizeof(*codec),
+ endpoint_setconf_cb, setup);
+ if (ret)
+ return TRUE;
+
+ avdtp_error_init(setup->err, AVDTP_MEDIA_CODEC,
+ AVDTP_UNSUPPORTED_CONFIGURATION);
+ break;
+ }
+
+done:
+ g_idle_add(auto_config, setup);
+ return TRUE;
+}
+
+static gboolean endpoint_getcap_ind(struct avdtp *session,
+ struct avdtp_local_sep *sep,
+ gboolean get_all, GSList **caps,
+ uint8_t *err, void *user_data)
+{
+ struct a2dp_sep *a2dp_sep = user_data;
+ struct avdtp_service_capability *media_transport, *media_codec;
+ struct avdtp_media_codec_capability *codec_caps;
+ uint8_t *capabilities;
+ size_t length;
+
+ if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+ DBG("Sink %p: Get_Capability_Ind", sep);
+ else
+ DBG("Source %p: Get_Capability_Ind", sep);
+
+ *caps = NULL;
+
+ media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
+ NULL, 0);
+
+ *caps = g_slist_append(*caps, media_transport);
+
+ length = media_endpoint_get_capabilities(a2dp_sep->endpoint,
+ &capabilities);
+
+ codec_caps = g_malloc0(sizeof(*codec_caps) + length);
+ codec_caps->media_type = AVDTP_MEDIA_TYPE_AUDIO;
+ codec_caps->media_codec_type = a2dp_sep->codec;
+ memcpy(codec_caps->data, capabilities, length);
+
+ media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, codec_caps,
+ sizeof(*codec_caps) + length);
+
+ *caps = g_slist_append(*caps, media_codec);
+ g_free(codec_caps);
+
+ if (get_all) {
+ struct avdtp_service_capability *delay_reporting;
+ delay_reporting = avdtp_service_cap_new(AVDTP_DELAY_REPORTING,
+ NULL, 0);
+ *caps = g_slist_append(*caps, delay_reporting);
+ }
+
+ return TRUE;
+}
+
+static void endpoint_open_cb(struct media_endpoint *endpoint, void *ret,
+ int size, void *user_data)
+{
+ struct a2dp_setup *setup = user_data;
+ int err;
+
+ if (ret == NULL) {
+ setup->stream = NULL;
+ finalize_config_errno(setup, -EPERM);
+ return;
+ }
+
+ err = avdtp_open(setup->session, setup->stream);
+ if (err == 0)
+ return;
+
+ error("Error on avdtp_open %s (%d)", strerror(-err), -err);
+ setup->stream = NULL;
+ finalize_config_errno(setup, err);
+}
+
+static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream,
+ struct avdtp_error *err, void *user_data)
+{
+ struct a2dp_sep *a2dp_sep = user_data;
+ struct a2dp_setup *setup;
+ struct audio_device *dev;
+ int ret;
+
+ if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+ DBG("Sink %p: Set_Configuration_Cfm", sep);
+ else
+ DBG("Source %p: Set_Configuration_Cfm", sep);
+
+ setup = find_setup_by_session(session);
+
+ if (err) {
+ if (setup) {
+ setup->err = err;
+ finalize_config(setup);
+ }
+ return;
+ }
+
+ avdtp_stream_add_cb(session, stream, stream_state_changed, a2dp_sep);
+ a2dp_sep->stream = stream;
+
+ if (!setup)
+ return;
+
+ dev = a2dp_get_dev(session);
+
+ /* Notify D-Bus interface of the new stream */
+ if (a2dp_sep->type == AVDTP_SEP_TYPE_SOURCE)
+ sink_new_stream(dev, session, setup->stream);
+ else
+ source_new_stream(dev, session, setup->stream);
+
+ /* Notify Endpoint */
+ if (a2dp_sep->endpoint) {
+ struct avdtp_service_capability *service;
+ struct avdtp_media_codec_capability *codec;
+
+ service = avdtp_stream_get_codec(stream);
+ codec = (struct avdtp_media_codec_capability *) service->data;
+
+ if (media_endpoint_set_configuration(a2dp_sep->endpoint, dev,
+ codec->data, service->length -
+ sizeof(*codec),
+ endpoint_open_cb, setup) ==
+ TRUE)
+ return;
+
+ setup->stream = NULL;
+ finalize_config_errno(setup, -EPERM);
+ return;
+ }
+
+ ret = avdtp_open(session, stream);
+ if (ret < 0) {
+ error("Error on avdtp_open %s (%d)", strerror(-ret), -ret);
+ setup->stream = NULL;
+ finalize_config_errno(setup, ret);
+ }
+}
+
+static gboolean getconf_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+ uint8_t *err, void *user_data)
+{
+ struct a2dp_sep *a2dp_sep = user_data;
+
+ if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+ DBG("Sink %p: Get_Configuration_Ind", sep);
+ else
+ DBG("Source %p: Get_Configuration_Ind", sep);
+ return TRUE;
+}
+
+static void getconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream, struct avdtp_error *err,
+ void *user_data)
+{
+ struct a2dp_sep *a2dp_sep = user_data;
+
+ if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+ DBG("Sink %p: Set_Configuration_Cfm", sep);
+ else
+ DBG("Source %p: Set_Configuration_Cfm", sep);
+}
+
+static gboolean open_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream, uint8_t *err,
+ void *user_data)
+{
+ struct a2dp_sep *a2dp_sep = user_data;
+
+ if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+ DBG("Sink %p: Open_Ind", sep);
+ else
+ DBG("Source %p: Open_Ind", sep);
+ return TRUE;
+}
+
+static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream, struct avdtp_error *err,
+ void *user_data)
+{
+ struct a2dp_sep *a2dp_sep = user_data;
+ struct a2dp_setup *setup;
+
+ if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+ DBG("Sink %p: Open_Cfm", sep);
+ else
+ DBG("Source %p: Open_Cfm", sep);
+
+ setup = find_setup_by_session(session);
+ if (!setup)
+ return;
+
+ if (setup->reconfigure)
+ setup->reconfigure = FALSE;
+
+ if (err) {
+ setup->stream = NULL;
+ setup->err = err;
+ }
+
+ finalize_config(setup);
+}
+
+static gboolean suspend_timeout(struct a2dp_sep *sep)
+{
+ if (avdtp_suspend(sep->session, sep->stream) == 0)
+ sep->suspending = TRUE;
+
+ sep->suspend_timer = 0;
+
+ avdtp_unref(sep->session);
+ sep->session = NULL;
+
+ return FALSE;
+}
+
+static gboolean start_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream, uint8_t *err,
+ void *user_data)
+{
+ struct a2dp_sep *a2dp_sep = user_data;
+ struct a2dp_setup *setup;
+
+ if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+ DBG("Sink %p: Start_Ind", sep);
+ else
+ DBG("Source %p: Start_Ind", sep);
+
+ setup = find_setup_by_session(session);
+ if (setup)
+ finalize_resume(setup);
+
+ if (!a2dp_sep->locked) {
+ a2dp_sep->session = avdtp_ref(session);
+ a2dp_sep->suspend_timer = g_timeout_add_seconds(SUSPEND_TIMEOUT,
+ (GSourceFunc) suspend_timeout,
+ a2dp_sep);
+ }
+
+ return TRUE;
+}
+
+static void start_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream, struct avdtp_error *err,
+ void *user_data)
+{
+ struct a2dp_sep *a2dp_sep = user_data;
+ struct a2dp_setup *setup;
+
+ if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+ DBG("Sink %p: Start_Cfm", sep);
+ else
+ DBG("Source %p: Start_Cfm", sep);
+
+ setup = find_setup_by_session(session);
+ if (!setup)
+ return;
+
+ if (err) {
+ setup->stream = NULL;
+ setup->err = err;
+ }
+
+ finalize_resume(setup);
+}
+
+static gboolean suspend_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream, uint8_t *err,
+ void *user_data)
+{
+ struct a2dp_sep *a2dp_sep = user_data;
+
+ if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+ DBG("Sink %p: Suspend_Ind", sep);
+ else
+ DBG("Source %p: Suspend_Ind", sep);
+
+ if (a2dp_sep->suspend_timer) {
+ g_source_remove(a2dp_sep->suspend_timer);
+ a2dp_sep->suspend_timer = 0;
+ avdtp_unref(a2dp_sep->session);
+ a2dp_sep->session = NULL;
+ }
+
+ return TRUE;
+}
+
+static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream, struct avdtp_error *err,
+ void *user_data)
+{
+ struct a2dp_sep *a2dp_sep = user_data;
+ struct a2dp_setup *setup;
+ gboolean start;
+
+ if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+ DBG("Sink %p: Suspend_Cfm", sep);
+ else
+ DBG("Source %p: Suspend_Cfm", sep);
+
+ a2dp_sep->suspending = FALSE;
+
+ setup = find_setup_by_session(session);
+ if (!setup)
+ return;
+
+ start = setup->start;
+ setup->start = FALSE;
+
+ if (err) {
+ setup->stream = NULL;
+ setup->err = err;
+ finalize_suspend(setup);
+ }
+ else
+ finalize_suspend_errno(setup, 0);
+
+ if (!start)
+ return;
+
+ if (err) {
+ setup->err = err;
+ finalize_suspend(setup);
+ } else if (avdtp_start(session, a2dp_sep->stream) < 0) {
+ struct avdtp_error start_err;
+ error("avdtp_start failed");
+ avdtp_error_init(&start_err, AVDTP_ERRNO, EIO);
+ setup->err = err;
+ finalize_suspend(setup);
+ }
+}
+
+static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream, uint8_t *err,
+ void *user_data)
+{
+ struct a2dp_sep *a2dp_sep = user_data;
+ struct a2dp_setup *setup;
+
+ if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+ DBG("Sink %p: Close_Ind", sep);
+ else
+ DBG("Source %p: Close_Ind", sep);
+
+ setup = find_setup_by_session(session);
+ if (!setup)
+ return TRUE;
+
+ finalize_suspend_errno(setup, -ECONNRESET);
+ finalize_resume_errno(setup, -ECONNRESET);
+
+ return TRUE;
+}
+
+static gboolean a2dp_reconfigure(gpointer data)
+{
+ struct a2dp_setup *setup = data;
+ struct a2dp_sep *sep = setup->sep;
+ int posix_err;
+ struct avdtp_media_codec_capability *rsep_codec;
+ struct avdtp_service_capability *cap;
+
+ if (setup->rsep) {
+ cap = avdtp_get_codec(setup->rsep);
+ rsep_codec = (struct avdtp_media_codec_capability *) cap->data;
+ }
+
+ if (!setup->rsep || sep->codec != rsep_codec->media_codec_type)
+ setup->rsep = avdtp_find_remote_sep(setup->session, sep->lsep);
+
+ posix_err = avdtp_set_configuration(setup->session, setup->rsep,
+ sep->lsep,
+ setup->caps,
+ &setup->stream);
+ if (posix_err < 0) {
+ error("avdtp_set_configuration: %s", strerror(-posix_err));
+ goto failed;
+ }
+
+ return FALSE;
+
+failed:
+ finalize_config_errno(setup, posix_err);
+ return FALSE;
+}
+
+static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream, struct avdtp_error *err,
+ void *user_data)
+{
+ struct a2dp_sep *a2dp_sep = user_data;
+ struct a2dp_setup *setup;
+
+ if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+ DBG("Sink %p: Close_Cfm", sep);
+ else
+ DBG("Source %p: Close_Cfm", sep);
+
+ setup = find_setup_by_session(session);
+ if (!setup)
+ return;
+
+ if (err) {
+ setup->stream = NULL;
+ setup->err = err;
+ finalize_config(setup);
+ return;
+ }
+
+ if (!setup->rsep)
+ setup->rsep = avdtp_stream_get_remote_sep(stream);
+
+ if (setup->reconfigure)
+ g_timeout_add(RECONFIGURE_TIMEOUT, a2dp_reconfigure, setup);
+}
+
+static gboolean abort_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream, uint8_t *err,
+ void *user_data)
+{
+ struct a2dp_sep *a2dp_sep = user_data;
+
+ if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+ DBG("Sink %p: Abort_Ind", sep);
+ else
+ DBG("Source %p: Abort_Ind", sep);
+
+ a2dp_sep->stream = NULL;
+
+ return TRUE;
+}
+
+static void abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream, struct avdtp_error *err,
+ void *user_data)
+{
+ struct a2dp_sep *a2dp_sep = user_data;
+ struct a2dp_setup *setup;
+
+ if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+ DBG("Sink %p: Abort_Cfm", sep);
+ else
+ DBG("Source %p: Abort_Cfm", sep);
+
+ setup = find_setup_by_session(session);
+ if (!setup)
+ return;
+
+ setup_unref(setup);
+}
+
+static gboolean reconf_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+ uint8_t *err, void *user_data)
+{
+ struct a2dp_sep *a2dp_sep = user_data;
+
+ if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+ DBG("Sink %p: ReConfigure_Ind", sep);
+ else
+ DBG("Source %p: ReConfigure_Ind", sep);
+
+ return TRUE;
+}
+
+static gboolean delayreport_ind(struct avdtp *session,
+ struct avdtp_local_sep *sep,
+ uint8_t rseid, uint16_t delay,
+ uint8_t *err, void *user_data)
+{
+ struct a2dp_sep *a2dp_sep = user_data;
+ struct audio_device *dev = a2dp_get_dev(session);
+
+ if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+ DBG("Sink %p: DelayReport_Ind", sep);
+ else
+ DBG("Source %p: DelayReport_Ind", sep);
+
+ unix_delay_report(dev, rseid, delay);
+
+ return TRUE;
+}
+
+static gboolean endpoint_delayreport_ind(struct avdtp *session,
+ struct avdtp_local_sep *sep,
+ uint8_t rseid, uint16_t delay,
+ uint8_t *err, void *user_data)
+{
+ struct a2dp_sep *a2dp_sep = user_data;
+ struct media_transport *transport;
+
+ if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+ DBG("Sink %p: DelayReport_Ind", sep);
+ else
+ DBG("Source %p: DelayReport_Ind", sep);
+
+ transport = media_endpoint_get_transport(a2dp_sep->endpoint);
+ if (transport == NULL)
+ return FALSE;
+
+ media_transport_update_delay(transport, delay);
+
+ return TRUE;
+}
+
+static void reconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream, struct avdtp_error *err,
+ void *user_data)
+{
+ struct a2dp_sep *a2dp_sep = user_data;
+ struct a2dp_setup *setup;
+
+ if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+ DBG("Sink %p: ReConfigure_Cfm", sep);
+ else
+ DBG("Source %p: ReConfigure_Cfm", sep);
+
+ setup = find_setup_by_session(session);
+ if (!setup)
+ return;
+
+ if (err) {
+ setup->stream = NULL;
+ setup->err = err;
+ }
+
+ finalize_config(setup);
+}
+
+static void delay_report_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream,
+ struct avdtp_error *err, void *user_data)
+{
+ struct a2dp_sep *a2dp_sep = user_data;
+
+ if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+ DBG("Sink %p: DelayReport_Cfm", sep);
+ else
+ DBG("Source %p: DelayReport_Cfm", sep);
+}
+
+static struct avdtp_sep_cfm cfm = {
+ .set_configuration = setconf_cfm,
+ .get_configuration = getconf_cfm,
+ .open = open_cfm,
+ .start = start_cfm,
+ .suspend = suspend_cfm,
+ .close = close_cfm,
+ .abort = abort_cfm,
+ .reconfigure = reconf_cfm,
+ .delay_report = delay_report_cfm,
+};
+
+static struct avdtp_sep_ind sbc_ind = {
+ .get_capability = sbc_getcap_ind,
+ .set_configuration = sbc_setconf_ind,
+ .get_configuration = getconf_ind,
+ .open = open_ind,
+ .start = start_ind,
+ .suspend = suspend_ind,
+ .close = close_ind,
+ .abort = abort_ind,
+ .reconfigure = reconf_ind,
+ .delayreport = delayreport_ind,
+};
+
+static struct avdtp_sep_ind mpeg_ind = {
+ .get_capability = mpeg_getcap_ind,
+ .set_configuration = mpeg_setconf_ind,
+ .get_configuration = getconf_ind,
+ .open = open_ind,
+ .start = start_ind,
+ .suspend = suspend_ind,
+ .close = close_ind,
+ .abort = abort_ind,
+ .reconfigure = reconf_ind,
+ .delayreport = delayreport_ind,
+};
+
+static struct avdtp_sep_ind endpoint_ind = {
+ .get_capability = endpoint_getcap_ind,
+ .set_configuration = endpoint_setconf_ind,
+ .get_configuration = getconf_ind,
+ .open = open_ind,
+ .start = start_ind,
+ .suspend = suspend_ind,
+ .close = close_ind,
+ .abort = abort_ind,
+ .reconfigure = reconf_ind,
+ .delayreport = endpoint_delayreport_ind,
+};
+
+static sdp_record_t *a2dp_record(uint8_t type, uint16_t avdtp_ver)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, l2cap_uuid, avdtp_uuid, a2dp_uuid;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t *record;
+ sdp_data_t *psm, *version, *features;
+ uint16_t lp = AVDTP_UUID;
+ uint16_t a2dp_ver = 0x0102, feat = 0x000f;
+
+ record = sdp_record_alloc();
+ if (!record)
+ return NULL;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(record, root);
+
+ if (type == AVDTP_SEP_TYPE_SOURCE)
+ sdp_uuid16_create(&a2dp_uuid, AUDIO_SOURCE_SVCLASS_ID);
+ else
+ sdp_uuid16_create(&a2dp_uuid, AUDIO_SINK_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &a2dp_uuid);
+ sdp_set_service_classes(record, svclass_id);
+
+ sdp_uuid16_create(&profile[0].uuid, ADVANCED_AUDIO_PROFILE_ID);
+ profile[0].version = a2dp_ver;
+ pfseq = sdp_list_append(0, &profile[0]);
+ sdp_set_profile_descs(record, pfseq);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap_uuid);
+ psm = sdp_data_alloc(SDP_UINT16, &lp);
+ proto[0] = sdp_list_append(proto[0], psm);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&avdtp_uuid, AVDTP_UUID);
+ proto[1] = sdp_list_append(0, &avdtp_uuid);
+ version = sdp_data_alloc(SDP_UINT16, &avdtp_ver);
+ proto[1] = sdp_list_append(proto[1], version);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(record, aproto);
+
+ features = sdp_data_alloc(SDP_UINT16, &feat);
+ sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+ if (type == AVDTP_SEP_TYPE_SOURCE)
+ sdp_set_info_attr(record, "Audio Source", 0, 0);
+ else
+ sdp_set_info_attr(record, "Audio Sink", 0, 0);
+
+ free(psm);
+ free(version);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(pfseq, 0);
+ sdp_list_free(aproto, 0);
+ sdp_list_free(root, 0);
+ sdp_list_free(svclass_id, 0);
+
+ return record;
+}
+
+static struct a2dp_server *find_server(GSList *list, const bdaddr_t *src)
+{
+ GSList *l;
+
+ for (l = list; l; l = l->next) {
+ struct a2dp_server *server = l->data;
+
+ if (bacmp(&server->src, src) == 0)
+ return server;
+ }
+
+ return NULL;
+}
+
+int a2dp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config)
+{
+ int sbc_srcs = 1, sbc_sinks = 1;
+ int mpeg12_srcs = 0, mpeg12_sinks = 0;
+ gboolean source = TRUE, sink = FALSE, socket = TRUE;
+ gboolean delay_reporting = FALSE;
+ char *str;
+ GError *err = NULL;
+ int i;
+ struct a2dp_server *server;
+
+ if (!config)
+ goto proceed;
+
+ str = g_key_file_get_string(config, "General", "Enable", &err);
+
+ if (err) {
+ DBG("audio.conf: %s", err->message);
+ g_clear_error(&err);
+ } else {
+ if (strstr(str, "Sink"))
+ source = TRUE;
+ if (strstr(str, "Source"))
+ sink = TRUE;
+ g_free(str);
+ }
+
+ str = g_key_file_get_string(config, "General", "Disable", &err);
+
+ if (err) {
+ DBG("audio.conf: %s", err->message);
+ g_clear_error(&err);
+ } else {
+ if (strstr(str, "Sink"))
+ source = FALSE;
+ if (strstr(str, "Source"))
+ sink = FALSE;
+ if (strstr(str, "Socket"))
+ socket = FALSE;
+ g_free(str);
+ }
+
+ /* Don't register any local sep if Socket is disabled */
+ if (socket == FALSE) {
+ sbc_srcs = 0;
+ sbc_sinks = 0;
+ mpeg12_srcs = 0;
+ mpeg12_sinks = 0;
+ goto proceed;
+ }
+
+ str = g_key_file_get_string(config, "A2DP", "SBCSources", &err);
+ if (err) {
+ DBG("audio.conf: %s", err->message);
+ g_clear_error(&err);
+ } else {
+ sbc_srcs = atoi(str);
+ g_free(str);
+ }
+
+ str = g_key_file_get_string(config, "A2DP", "MPEG12Sources", &err);
+ if (err) {
+ DBG("audio.conf: %s", err->message);
+ g_clear_error(&err);
+ } else {
+ mpeg12_srcs = atoi(str);
+ g_free(str);
+ }
+
+ str = g_key_file_get_string(config, "A2DP", "SBCSinks", &err);
+ if (err) {
+ DBG("audio.conf: %s", err->message);
+ g_clear_error(&err);
+ } else {
+ sbc_sinks = atoi(str);
+ g_free(str);
+ }
+
+ str = g_key_file_get_string(config, "A2DP", "MPEG12Sinks", &err);
+ if (err) {
+ DBG("audio.conf: %s", err->message);
+ g_clear_error(&err);
+ } else {
+ mpeg12_sinks = atoi(str);
+ g_free(str);
+ }
+
+proceed:
+ if (!connection)
+ connection = dbus_connection_ref(conn);
+
+ server = find_server(servers, src);
+ if (!server) {
+ int av_err;
+
+ server = g_new0(struct a2dp_server, 1);
+ if (!server)
+ return -ENOMEM;
+
+ av_err = avdtp_init(src, config, &server->version);
+ if (av_err < 0) {
+ g_free(server);
+ return av_err;
+ }
+
+ bacpy(&server->src, src);
+ servers = g_slist_append(servers, server);
+ }
+
+ if (config)
+ delay_reporting = g_key_file_get_boolean(config, "A2DP",
+ "DelayReporting", NULL);
+
+ if (delay_reporting)
+ server->version = 0x0103;
+ else
+ server->version = 0x0102;
+
+ server->source_enabled = source;
+ if (source) {
+ for (i = 0; i < sbc_srcs; i++)
+ a2dp_add_sep(src, AVDTP_SEP_TYPE_SOURCE,
+ A2DP_CODEC_SBC, delay_reporting, NULL, NULL);
+
+ for (i = 0; i < mpeg12_srcs; i++)
+ a2dp_add_sep(src, AVDTP_SEP_TYPE_SOURCE,
+ A2DP_CODEC_MPEG12, delay_reporting,
+ NULL, NULL);
+ }
+ server->sink_enabled = sink;
+ if (sink) {
+ for (i = 0; i < sbc_sinks; i++)
+ a2dp_add_sep(src, AVDTP_SEP_TYPE_SINK,
+ A2DP_CODEC_SBC, delay_reporting, NULL, NULL);
+
+ for (i = 0; i < mpeg12_sinks; i++)
+ a2dp_add_sep(src, AVDTP_SEP_TYPE_SINK,
+ A2DP_CODEC_MPEG12, delay_reporting,
+ NULL, NULL);
+ }
+
+ return 0;
+}
+
+static void a2dp_unregister_sep(struct a2dp_sep *sep)
+{
+ if (sep->endpoint) {
+ media_endpoint_release(sep->endpoint);
+ sep->endpoint = NULL;
+ }
+
+ avdtp_unregister_sep(sep->lsep);
+ g_free(sep);
+}
+
+void a2dp_unregister(const bdaddr_t *src)
+{
+ struct a2dp_server *server;
+
+ server = find_server(servers, src);
+ if (!server)
+ return;
+
+ g_slist_foreach(server->sinks, (GFunc) a2dp_remove_sep, NULL);
+ g_slist_free(server->sinks);
+
+ g_slist_foreach(server->sources, (GFunc) a2dp_remove_sep, NULL);
+ g_slist_free(server->sources);
+
+ avdtp_exit(src);
+
+ servers = g_slist_remove(servers, server);
+ g_free(server);
+
+ if (servers)
+ return;
+
+ dbus_connection_unref(connection);
+ connection = NULL;
+}
+
+struct a2dp_sep *a2dp_add_sep(const bdaddr_t *src, uint8_t type,
+ uint8_t codec, gboolean delay_reporting,
+ struct media_endpoint *endpoint, int *err)
+{
+ struct a2dp_server *server;
+ struct a2dp_sep *sep;
+ GSList **l;
+ uint32_t *record_id;
+ sdp_record_t *record;
+ struct avdtp_sep_ind *ind;
+
+ server = find_server(servers, src);
+ if (server == NULL) {
+ if (err)
+ *err = -EINVAL;
+ return NULL;
+ }
+
+ if (type == AVDTP_SEP_TYPE_SINK && !server->sink_enabled) {
+ if (err)
+ *err = -EPROTONOSUPPORT;
+ return NULL;
+ }
+
+ if (type == AVDTP_SEP_TYPE_SOURCE && !server->source_enabled) {
+ if (err)
+ *err = -EPROTONOSUPPORT;
+ return NULL;
+ }
+
+ sep = g_new0(struct a2dp_sep, 1);
+
+ if (endpoint) {
+ ind = &endpoint_ind;
+ goto proceed;
+ }
+
+ ind = (codec == A2DP_CODEC_MPEG12) ? &mpeg_ind : &sbc_ind;
+
+proceed:
+ sep->lsep = avdtp_register_sep(&server->src, type,
+ AVDTP_MEDIA_TYPE_AUDIO, codec,
+ delay_reporting, ind, &cfm, sep);
+ if (sep->lsep == NULL) {
+ g_free(sep);
+ if (err)
+ *err = -EINVAL;
+ return NULL;
+ }
+
+ sep->server = server;
+ sep->endpoint = endpoint;
+ sep->codec = codec;
+ sep->type = type;
+ sep->delay_reporting = delay_reporting;
+
+ if (type == AVDTP_SEP_TYPE_SOURCE) {
+ l = &server->sources;
+ record_id = &server->source_record_id;
+ } else {
+ l = &server->sinks;
+ record_id = &server->sink_record_id;
+ }
+
+ if (*record_id != 0)
+ goto add;
+
+ record = a2dp_record(type, server->version);
+ if (!record) {
+ error("Unable to allocate new service record");
+ avdtp_unregister_sep(sep->lsep);
+ g_free(sep);
+ if (err)
+ *err = -EINVAL;
+ return NULL;
+ }
+
+ if (add_record_to_server(&server->src, record) < 0) {
+ error("Unable to register A2DP service record");\
+ sdp_record_free(record);
+ avdtp_unregister_sep(sep->lsep);
+ g_free(sep);
+ if (err)
+ *err = -EINVAL;
+ return NULL;
+ }
+ *record_id = record->handle;
+
+add:
+ *l = g_slist_append(*l, sep);
+
+ if (err)
+ *err = 0;
+ return sep;
+}
+
+void a2dp_remove_sep(struct a2dp_sep *sep)
+{
+ struct a2dp_server *server = sep->server;
+
+ if (sep->type == AVDTP_SEP_TYPE_SOURCE) {
+ if (g_slist_find(server->sources, sep) == NULL)
+ return;
+ server->sources = g_slist_remove(server->sources, sep);
+ if (server->sources == NULL && server->source_record_id) {
+ remove_record_from_server(server->source_record_id);
+ server->source_record_id = 0;
+ }
+ } else {
+ if (g_slist_find(server->sinks, sep) == NULL)
+ return;
+ server->sinks = g_slist_remove(server->sinks, sep);
+ if (server->sinks == NULL && server->sink_record_id) {
+ remove_record_from_server(server->sink_record_id);
+ server->sink_record_id = 0;
+ }
+ }
+
+ a2dp_unregister_sep(sep);
+}
+
+struct a2dp_sep *a2dp_get(struct avdtp *session,
+ struct avdtp_remote_sep *rsep)
+{
+ GSList *l;
+ struct a2dp_server *server;
+ struct avdtp_service_capability *cap;
+ struct avdtp_media_codec_capability *codec_cap = NULL;
+ bdaddr_t src;
+
+ avdtp_get_peers(session, &src, NULL);
+ server = find_server(servers, &src);
+ if (!server)
+ return NULL;
+
+ cap = avdtp_get_codec(rsep);
+ codec_cap = (void *) cap->data;
+
+ if (avdtp_get_type(rsep) == AVDTP_SEP_TYPE_SINK)
+ l = server->sources;
+ else
+ l = server->sinks;
+
+ for (; l != NULL; l = l->next) {
+ struct a2dp_sep *sep = l->data;
+
+ if (sep->locked)
+ continue;
+
+ if (sep->codec != codec_cap->media_codec_type)
+ continue;
+
+ if (!sep->stream || avdtp_has_stream(session, sep->stream))
+ return sep;
+ }
+
+ return NULL;
+}
+
+static uint8_t default_bitpool(uint8_t freq, uint8_t mode)
+{
+ switch (freq) {
+ case SBC_SAMPLING_FREQ_16000:
+ case SBC_SAMPLING_FREQ_32000:
+ return 53;
+ case SBC_SAMPLING_FREQ_44100:
+ switch (mode) {
+ case SBC_CHANNEL_MODE_MONO:
+ case SBC_CHANNEL_MODE_DUAL_CHANNEL:
+ return 31;
+ case SBC_CHANNEL_MODE_STEREO:
+ case SBC_CHANNEL_MODE_JOINT_STEREO:
+ return 53;
+ default:
+ error("Invalid channel mode %u", mode);
+ return 53;
+ }
+ case SBC_SAMPLING_FREQ_48000:
+ switch (mode) {
+ case SBC_CHANNEL_MODE_MONO:
+ case SBC_CHANNEL_MODE_DUAL_CHANNEL:
+ return 29;
+ case SBC_CHANNEL_MODE_STEREO:
+ case SBC_CHANNEL_MODE_JOINT_STEREO:
+ return 51;
+ default:
+ error("Invalid channel mode %u", mode);
+ return 51;
+ }
+ default:
+ error("Invalid sampling freq %u", freq);
+ return 53;
+ }
+}
+
+static gboolean select_sbc_params(struct sbc_codec_cap *cap,
+ struct sbc_codec_cap *supported)
+{
+ unsigned int max_bitpool, min_bitpool;
+
+ memset(cap, 0, sizeof(struct sbc_codec_cap));
+
+ cap->cap.media_type = AVDTP_MEDIA_TYPE_AUDIO;
+ cap->cap.media_codec_type = A2DP_CODEC_SBC;
+
+ if (supported->frequency & SBC_SAMPLING_FREQ_44100)
+ cap->frequency = SBC_SAMPLING_FREQ_44100;
+ else if (supported->frequency & SBC_SAMPLING_FREQ_48000)
+ cap->frequency = SBC_SAMPLING_FREQ_48000;
+ else if (supported->frequency & SBC_SAMPLING_FREQ_32000)
+ cap->frequency = SBC_SAMPLING_FREQ_32000;
+ else if (supported->frequency & SBC_SAMPLING_FREQ_16000)
+ cap->frequency = SBC_SAMPLING_FREQ_16000;
+ else {
+ error("No supported frequencies");
+ return FALSE;
+ }
+
+ if (supported->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO)
+ cap->channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO;
+ else if (supported->channel_mode & SBC_CHANNEL_MODE_STEREO)
+ cap->channel_mode = SBC_CHANNEL_MODE_STEREO;
+ else if (supported->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL)
+ cap->channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL;
+ else if (supported->channel_mode & SBC_CHANNEL_MODE_MONO)
+ cap->channel_mode = SBC_CHANNEL_MODE_MONO;
+ else {
+ error("No supported channel modes");
+ return FALSE;
+ }
+
+ if (supported->block_length & SBC_BLOCK_LENGTH_16)
+ cap->block_length = SBC_BLOCK_LENGTH_16;
+ else if (supported->block_length & SBC_BLOCK_LENGTH_12)
+ cap->block_length = SBC_BLOCK_LENGTH_12;
+ else if (supported->block_length & SBC_BLOCK_LENGTH_8)
+ cap->block_length = SBC_BLOCK_LENGTH_8;
+ else if (supported->block_length & SBC_BLOCK_LENGTH_4)
+ cap->block_length = SBC_BLOCK_LENGTH_4;
+ else {
+ error("No supported block lengths");
+ return FALSE;
+ }
+
+ if (supported->subbands & SBC_SUBBANDS_8)
+ cap->subbands = SBC_SUBBANDS_8;
+ else if (supported->subbands & SBC_SUBBANDS_4)
+ cap->subbands = SBC_SUBBANDS_4;
+ else {
+ error("No supported subbands");
+ return FALSE;
+ }
+
+ if (supported->allocation_method & SBC_ALLOCATION_LOUDNESS)
+ cap->allocation_method = SBC_ALLOCATION_LOUDNESS;
+ else if (supported->allocation_method & SBC_ALLOCATION_SNR)
+ cap->allocation_method = SBC_ALLOCATION_SNR;
+
+ min_bitpool = MAX(MIN_BITPOOL, supported->min_bitpool);
+ max_bitpool = MIN(default_bitpool(cap->frequency, cap->channel_mode),
+ supported->max_bitpool);
+
+ cap->min_bitpool = min_bitpool;
+ cap->max_bitpool = max_bitpool;
+
+ return TRUE;
+}
+
+static gboolean select_capabilities(struct avdtp *session,
+ struct avdtp_remote_sep *rsep,
+ GSList **caps)
+{
+ struct avdtp_service_capability *media_transport, *media_codec;
+ struct sbc_codec_cap sbc_cap;
+
+ media_codec = avdtp_get_codec(rsep);
+ if (!media_codec)
+ return FALSE;
+
+ select_sbc_params(&sbc_cap, (struct sbc_codec_cap *) media_codec->data);
+
+ media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
+ NULL, 0);
+
+ *caps = g_slist_append(*caps, media_transport);
+
+ media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap,
+ sizeof(sbc_cap));
+
+ *caps = g_slist_append(*caps, media_codec);
+
+ if (avdtp_get_delay_reporting(rsep)) {
+ struct avdtp_service_capability *delay_reporting;
+ delay_reporting = avdtp_service_cap_new(AVDTP_DELAY_REPORTING,
+ NULL, 0);
+ *caps = g_slist_append(*caps, delay_reporting);
+ }
+
+ return TRUE;
+}
+
+static void select_cb(struct media_endpoint *endpoint, void *ret, int size,
+ void *user_data)
+{
+ struct a2dp_setup *setup = user_data;
+ struct avdtp_service_capability *media_transport, *media_codec;
+ struct avdtp_media_codec_capability *cap;
+
+ if (size < 0) {
+ DBG("Endpoint replied an invalid configuration");
+ goto done;
+ }
+
+ media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
+ NULL, 0);
+
+ setup->caps = g_slist_append(setup->caps, media_transport);
+
+ cap = g_malloc0(sizeof(*cap) + size);
+ cap->media_type = AVDTP_MEDIA_TYPE_AUDIO;
+ cap->media_codec_type = setup->sep->codec;
+ memcpy(cap->data, ret, size);
+
+ media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, cap,
+ sizeof(*cap) + size);
+
+ setup->caps = g_slist_append(setup->caps, media_codec);
+ g_free(cap);
+
+done:
+ finalize_select(setup);
+}
+
+static gboolean auto_select(gpointer data)
+{
+ struct a2dp_setup *setup = data;
+
+ finalize_select(setup);
+
+ return FALSE;
+}
+
+static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
+ const char *sender)
+{
+ for (; list; list = list->next) {
+ struct a2dp_sep *sep = list->data;
+
+ /* Use sender's endpoint if available */
+ if (sender) {
+ const char *name;
+
+ if (sep->endpoint == NULL)
+ continue;
+
+ name = media_endpoint_get_sender(sep->endpoint);
+ if (g_strcmp0(sender, name) != 0)
+ continue;
+ }
+
+ if (avdtp_find_remote_sep(session, sep->lsep) == NULL)
+ continue;
+
+ return sep;
+ }
+
+ return NULL;
+}
+
+static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type,
+ const char *sender)
+{
+ struct a2dp_server *server;
+ struct a2dp_sep *sep;
+ GSList *l;
+ bdaddr_t src;
+
+ avdtp_get_peers(session, &src, NULL);
+ server = find_server(servers, &src);
+ if (!server)
+ return NULL;
+
+ l = type == AVDTP_SEP_TYPE_SINK ? server->sources : server->sinks;
+
+ /* Check sender's seps first */
+ sep = a2dp_find_sep(session, l, sender);
+ if (sep != NULL)
+ return sep;
+
+ return a2dp_find_sep(session, l, NULL);
+}
+
+unsigned int a2dp_select_capabilities(struct avdtp *session,
+ uint8_t type, const char *sender,
+ a2dp_select_cb_t cb,
+ void *user_data)
+{
+ struct a2dp_setup *setup;
+ struct a2dp_setup_cb *cb_data;
+ struct a2dp_sep *sep;
+ struct avdtp_service_capability *service;
+ struct avdtp_media_codec_capability *codec;
+
+ sep = a2dp_select_sep(session, type, sender);
+ if (!sep) {
+ error("Unable to select SEP");
+ return 0;
+ }
+
+ setup = a2dp_setup_get(session);
+ if (!setup)
+ return 0;
+
+ cb_data = setup_cb_new(setup);
+ cb_data->select_cb = cb;
+ cb_data->user_data = user_data;
+
+ setup->sep = sep;
+ setup->rsep = avdtp_find_remote_sep(session, sep->lsep);
+
+ if (setup->rsep == NULL) {
+ error("Could not find remote sep");
+ goto fail;
+ }
+
+ /* FIXME: Remove auto select when it is not longer possible to register
+ endpoint in the configuration file */
+ if (sep->endpoint == NULL) {
+ if (!select_capabilities(session, setup->rsep,
+ &setup->caps)) {
+ error("Unable to auto select remote SEP capabilities");
+ goto fail;
+ }
+
+ g_idle_add(auto_select, setup);
+
+ return cb_data->id;
+ }
+
+ service = avdtp_get_codec(setup->rsep);
+ codec = (struct avdtp_media_codec_capability *) service->data;
+
+ if (media_endpoint_select_configuration(sep->endpoint, codec->data,
+ service->length - sizeof(*codec),
+ select_cb, setup) ==
+ TRUE)
+ return cb_data->id;
+
+fail:
+ setup_cb_free(cb_data);
+ return 0;
+
+}
+
+unsigned int a2dp_config(struct avdtp *session, struct a2dp_sep *sep,
+ a2dp_config_cb_t cb, GSList *caps,
+ void *user_data)
+{
+ struct a2dp_setup_cb *cb_data;
+ GSList *l;
+ struct a2dp_server *server;
+ struct a2dp_setup *setup;
+ struct a2dp_sep *tmp;
+ struct avdtp_service_capability *cap;
+ struct avdtp_media_codec_capability *codec_cap = NULL;
+ int posix_err;
+ bdaddr_t src;
+ uint8_t remote_type;
+
+ avdtp_get_peers(session, &src, NULL);
+ server = find_server(servers, &src);
+ if (!server)
+ return 0;
+
+ for (l = caps; l != NULL; l = l->next) {
+ cap = l->data;
+
+ if (cap->category != AVDTP_MEDIA_CODEC)
+ continue;
+
+ codec_cap = (void *) cap->data;
+ break;
+ }
+
+ if (!codec_cap)
+ return 0;
+
+ if (sep->codec != codec_cap->media_codec_type)
+ return 0;
+
+ DBG("a2dp_config: selected SEP %p", sep->lsep);
+
+ setup = a2dp_setup_get(session);
+ if (!setup)
+ return 0;
+
+ cb_data = setup_cb_new(setup);
+ cb_data->config_cb = cb;
+ cb_data->user_data = user_data;
+
+ setup->sep = sep;
+ setup->stream = sep->stream;
+
+ /* Copy given caps if they are different than current caps */
+ if (setup->caps != caps) {
+ g_slist_foreach(setup->caps, (GFunc) g_free, NULL);
+ g_slist_free(setup->caps);
+ setup->caps = g_slist_copy(caps);
+ }
+
+ switch (avdtp_sep_get_state(sep->lsep)) {
+ case AVDTP_STATE_IDLE:
+ if (sep->type == AVDTP_SEP_TYPE_SOURCE) {
+ l = server->sources;
+ remote_type = AVDTP_SEP_TYPE_SINK;
+ } else {
+ remote_type = AVDTP_SEP_TYPE_SOURCE;
+ l = server->sinks;
+ }
+
+ for (; l != NULL; l = l->next) {
+ tmp = l->data;
+ if (avdtp_has_stream(session, tmp->stream))
+ break;
+ }
+
+ if (l != NULL) {
+ if (a2dp_sep_get_lock(tmp))
+ goto failed;
+ setup->reconfigure = TRUE;
+ if (avdtp_close(session, tmp->stream, FALSE) < 0) {
+ error("avdtp_close failed");
+ goto failed;
+ }
+ break;
+ }
+
+ setup->rsep = avdtp_find_remote_sep(session, sep->lsep);
+ if (setup->rsep == NULL) {
+ error("No matching ACP and INT SEPs found");
+ goto failed;
+ }
+
+ posix_err = avdtp_set_configuration(session, setup->rsep,
+ sep->lsep, caps,
+ &setup->stream);
+ if (posix_err < 0) {
+ error("avdtp_set_configuration: %s",
+ strerror(-posix_err));
+ goto failed;
+ }
+ break;
+ case AVDTP_STATE_OPEN:
+ case AVDTP_STATE_STREAMING:
+ if (avdtp_stream_has_capabilities(setup->stream, caps)) {
+ DBG("Configuration match: resuming");
+ g_idle_add((GSourceFunc) finalize_config, setup);
+ } else if (!setup->reconfigure) {
+ setup->reconfigure = TRUE;
+ if (avdtp_close(session, sep->stream, FALSE) < 0) {
+ error("avdtp_close failed");
+ goto failed;
+ }
+ }
+ break;
+ default:
+ error("SEP in bad state for requesting a new stream");
+ goto failed;
+ }
+
+ return cb_data->id;
+
+failed:
+ setup_cb_free(cb_data);
+ return 0;
+}
+
+unsigned int a2dp_resume(struct avdtp *session, struct a2dp_sep *sep,
+ a2dp_stream_cb_t cb, void *user_data)
+{
+ struct a2dp_setup_cb *cb_data;
+ struct a2dp_setup *setup;
+
+ setup = a2dp_setup_get(session);
+ if (!setup)
+ return 0;
+
+ cb_data = setup_cb_new(setup);
+ cb_data->resume_cb = cb;
+ cb_data->user_data = user_data;
+
+ setup->sep = sep;
+ setup->stream = sep->stream;
+
+ switch (avdtp_sep_get_state(sep->lsep)) {
+ case AVDTP_STATE_IDLE:
+ goto failed;
+ break;
+ case AVDTP_STATE_OPEN:
+ if (avdtp_start(session, sep->stream) < 0) {
+ error("avdtp_start failed");
+ goto failed;
+ }
+ break;
+ case AVDTP_STATE_STREAMING:
+ if (!sep->suspending && sep->suspend_timer) {
+ g_source_remove(sep->suspend_timer);
+ sep->suspend_timer = 0;
+ avdtp_unref(sep->session);
+ sep->session = NULL;
+ }
+ if (sep->suspending)
+ setup->start = TRUE;
+ else
+ g_idle_add((GSourceFunc) finalize_resume, setup);
+ break;
+ default:
+ error("SEP in bad state for resume");
+ goto failed;
+ }
+
+ return cb_data->id;
+
+failed:
+ setup_cb_free(cb_data);
+ return 0;
+}
+
+unsigned int a2dp_suspend(struct avdtp *session, struct a2dp_sep *sep,
+ a2dp_stream_cb_t cb, void *user_data)
+{
+ struct a2dp_setup_cb *cb_data;
+ struct a2dp_setup *setup;
+
+ setup = a2dp_setup_get(session);
+ if (!setup)
+ return 0;
+
+ cb_data = setup_cb_new(setup);
+ cb_data->suspend_cb = cb;
+ cb_data->user_data = user_data;
+
+ setup->sep = sep;
+ setup->stream = sep->stream;
+
+ switch (avdtp_sep_get_state(sep->lsep)) {
+ case AVDTP_STATE_IDLE:
+ error("a2dp_suspend: no stream to suspend");
+ goto failed;
+ break;
+ case AVDTP_STATE_OPEN:
+ g_idle_add((GSourceFunc) finalize_suspend, setup);
+ break;
+ case AVDTP_STATE_STREAMING:
+ if (avdtp_suspend(session, sep->stream) < 0) {
+ error("avdtp_suspend failed");
+ goto failed;
+ }
+ sep->suspending = TRUE;
+ break;
+ default:
+ error("SEP in bad state for suspend");
+ goto failed;
+ }
+
+ return cb_data->id;
+
+failed:
+ setup_cb_free(cb_data);
+ return 0;
+}
+
+gboolean a2dp_cancel(struct audio_device *dev, unsigned int id)
+{
+ struct a2dp_setup *setup;
+ GSList *l;
+
+ setup = find_setup_by_dev(dev);
+ if (!setup)
+ return FALSE;
+
+ for (l = setup->cb; l != NULL; l = g_slist_next(l)) {
+ struct a2dp_setup_cb *cb = l->data;
+
+ if (cb->id != id)
+ continue;
+
+ setup_ref(setup);
+ setup_cb_free(cb);
+
+ if (!setup->cb) {
+ DBG("aborting setup %p", setup);
+ avdtp_abort(setup->session, setup->stream);
+ return TRUE;
+ }
+
+ setup_unref(setup);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session)
+{
+ if (sep->locked)
+ return FALSE;
+
+ DBG("SEP %p locked", sep->lsep);
+ sep->locked = TRUE;
+
+ return TRUE;
+}
+
+gboolean a2dp_sep_unlock(struct a2dp_sep *sep, struct avdtp *session)
+{
+ avdtp_state_t state;
+
+ state = avdtp_sep_get_state(sep->lsep);
+
+ sep->locked = FALSE;
+
+ DBG("SEP %p unlocked", sep->lsep);
+
+ if (!sep->stream || state == AVDTP_STATE_IDLE)
+ return TRUE;
+
+ switch (state) {
+ case AVDTP_STATE_OPEN:
+ /* Set timer here */
+ break;
+ case AVDTP_STATE_STREAMING:
+ if (avdtp_suspend(session, sep->stream) == 0)
+ sep->suspending = TRUE;
+ break;
+ default:
+ break;
+ }
+
+ return TRUE;
+}
+
+gboolean a2dp_sep_get_lock(struct a2dp_sep *sep)
+{
+ return sep->locked;
+}
+
+static int stream_cmp(gconstpointer data, gconstpointer user_data)
+{
+ const struct a2dp_sep *sep = data;
+ const struct avdtp_stream *stream = user_data;
+
+ return (sep->stream != stream);
+}
+
+struct a2dp_sep *a2dp_get_sep(struct avdtp *session,
+ struct avdtp_stream *stream)
+{
+ struct a2dp_server *server;
+ bdaddr_t src, dst;
+ GSList *l;
+
+ avdtp_get_peers(session, &src, &dst);
+
+ for (l = servers; l; l = l->next) {
+ server = l->data;
+
+ if (bacmp(&src, &server->src) == 0)
+ break;
+ }
+
+ if (!l)
+ return NULL;
+
+ l = g_slist_find_custom(server->sources, stream, stream_cmp);
+ if (l)
+ return l->data;
+
+ l = g_slist_find_custom(server->sinks, stream, stream_cmp);
+ if (l)
+ return l->data;
+
+ return NULL;
+}
+
+struct avdtp_stream *a2dp_sep_get_stream(struct a2dp_sep *sep)
+{
+ return sep->stream;
+}
diff --git a/audio/a2dp.h b/audio/a2dp.h
new file mode 100644
index 0000000..5c4232d
--- /dev/null
+++ b/audio/a2dp.h
@@ -0,0 +1,164 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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
+ *
+ */
+
+#define A2DP_CODEC_SBC 0x00
+#define A2DP_CODEC_MPEG12 0x01
+#define A2DP_CODEC_MPEG24 0x02
+#define A2DP_CODEC_ATRAC 0x03
+
+#define SBC_SAMPLING_FREQ_16000 (1 << 3)
+#define SBC_SAMPLING_FREQ_32000 (1 << 2)
+#define SBC_SAMPLING_FREQ_44100 (1 << 1)
+#define SBC_SAMPLING_FREQ_48000 1
+
+#define SBC_CHANNEL_MODE_MONO (1 << 3)
+#define SBC_CHANNEL_MODE_DUAL_CHANNEL (1 << 2)
+#define SBC_CHANNEL_MODE_STEREO (1 << 1)
+#define SBC_CHANNEL_MODE_JOINT_STEREO 1
+
+#define SBC_BLOCK_LENGTH_4 (1 << 3)
+#define SBC_BLOCK_LENGTH_8 (1 << 2)
+#define SBC_BLOCK_LENGTH_12 (1 << 1)
+#define SBC_BLOCK_LENGTH_16 1
+
+#define SBC_SUBBANDS_4 (1 << 1)
+#define SBC_SUBBANDS_8 1
+
+#define SBC_ALLOCATION_SNR (1 << 1)
+#define SBC_ALLOCATION_LOUDNESS 1
+
+#define MPEG_CHANNEL_MODE_MONO (1 << 3)
+#define MPEG_CHANNEL_MODE_DUAL_CHANNEL (1 << 2)
+#define MPEG_CHANNEL_MODE_STEREO (1 << 1)
+#define MPEG_CHANNEL_MODE_JOINT_STEREO 1
+
+#define MPEG_LAYER_MP1 (1 << 2)
+#define MPEG_LAYER_MP2 (1 << 1)
+#define MPEG_LAYER_MP3 1
+
+#define MPEG_SAMPLING_FREQ_16000 (1 << 5)
+#define MPEG_SAMPLING_FREQ_22050 (1 << 4)
+#define MPEG_SAMPLING_FREQ_24000 (1 << 3)
+#define MPEG_SAMPLING_FREQ_32000 (1 << 2)
+#define MPEG_SAMPLING_FREQ_44100 (1 << 1)
+#define MPEG_SAMPLING_FREQ_48000 1
+
+#define MAX_BITPOOL 64
+#define MIN_BITPOOL 2
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct sbc_codec_cap {
+ struct avdtp_media_codec_capability cap;
+ uint8_t channel_mode:4;
+ uint8_t frequency:4;
+ uint8_t allocation_method:2;
+ uint8_t subbands:2;
+ uint8_t block_length:4;
+ uint8_t min_bitpool;
+ uint8_t max_bitpool;
+} __attribute__ ((packed));
+
+struct mpeg_codec_cap {
+ struct avdtp_media_codec_capability cap;
+ uint8_t channel_mode:4;
+ uint8_t crc:1;
+ uint8_t layer:3;
+ uint8_t frequency:6;
+ uint8_t mpf:1;
+ uint8_t rfa:1;
+ uint16_t bitrate;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct sbc_codec_cap {
+ struct avdtp_media_codec_capability cap;
+ uint8_t frequency:4;
+ uint8_t channel_mode:4;
+ uint8_t block_length:4;
+ uint8_t subbands:2;
+ uint8_t allocation_method:2;
+ uint8_t min_bitpool;
+ uint8_t max_bitpool;
+} __attribute__ ((packed));
+
+struct mpeg_codec_cap {
+ struct avdtp_media_codec_capability cap;
+ uint8_t layer:3;
+ uint8_t crc:1;
+ uint8_t channel_mode:4;
+ uint8_t rfa:1;
+ uint8_t mpf:1;
+ uint8_t frequency:6;
+ uint16_t bitrate;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+struct a2dp_sep;
+
+
+typedef void (*a2dp_select_cb_t) (struct avdtp *session,
+ struct a2dp_sep *sep, GSList *caps,
+ void *user_data);
+typedef void (*a2dp_config_cb_t) (struct avdtp *session, struct a2dp_sep *sep,
+ struct avdtp_stream *stream,
+ struct avdtp_error *err,
+ void *user_data);
+typedef void (*a2dp_stream_cb_t) (struct avdtp *session,
+ struct avdtp_error *err,
+ void *user_data);
+
+int a2dp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config);
+void a2dp_unregister(const bdaddr_t *src);
+
+struct a2dp_sep *a2dp_add_sep(const bdaddr_t *src, uint8_t type,
+ uint8_t codec, gboolean delay_reporting,
+ struct media_endpoint *endpoint, int *err);
+void a2dp_remove_sep(struct a2dp_sep *sep);
+
+struct a2dp_sep *a2dp_get(struct avdtp *session, struct avdtp_remote_sep *sep);
+
+unsigned int a2dp_select_capabilities(struct avdtp *session,
+ uint8_t type, const char *sender,
+ a2dp_select_cb_t cb,
+ void *user_data);
+unsigned int a2dp_config(struct avdtp *session, struct a2dp_sep *sep,
+ a2dp_config_cb_t cb, GSList *caps,
+ void *user_data);
+unsigned int a2dp_resume(struct avdtp *session, struct a2dp_sep *sep,
+ a2dp_stream_cb_t cb, void *user_data);
+unsigned int a2dp_suspend(struct avdtp *session, struct a2dp_sep *sep,
+ a2dp_stream_cb_t cb, void *user_data);
+gboolean a2dp_cancel(struct audio_device *dev, unsigned int id);
+
+gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session);
+gboolean a2dp_sep_unlock(struct a2dp_sep *sep, struct avdtp *session);
+gboolean a2dp_sep_get_lock(struct a2dp_sep *sep);
+struct avdtp_stream *a2dp_sep_get_stream(struct a2dp_sep *sep);
+struct a2dp_sep *a2dp_get_sep(struct avdtp *session,
+ struct avdtp_stream *stream);
diff --git a/audio/audio.conf b/audio/audio.conf
new file mode 100644
index 0000000..302e046
--- /dev/null
+++ b/audio/audio.conf
@@ -0,0 +1,45 @@
+# Configuration file for the audio service
+
+# This section contains options which are not specific to any
+# particular interface
+[General]
+
+# Switch to master role for incoming connections (defaults to true)
+#Master=true
+
+# If we want to disable support for specific services
+# Defaults to supporting all implemented services
+#Disable=Control,Source
+
+# SCO routing. Either PCM or HCI (in which case audio is routed to/from ALSA)
+# Defaults to HCI
+#SCORouting=PCM
+
+# Automatically connect both A2DP and HFP/HSP profiles for incoming
+# connections. Some headsets that support both profiles will only connect the
+# other one automatically so the default setting of true is usually a good
+# idea.
+#AutoConnect=true
+
+# Headset interface specific options (i.e. options which affect how the audio
+# service interacts with remote headset devices)
+[Headset]
+
+# Set to true to support HFP, false means only HSP is supported
+# Defaults to true
+HFP=true
+
+# Maximum number of connected HSP/HFP devices per adapter. Defaults to 1
+MaxConnected=1
+
+# Set to true to enable use of fast connectable mode (faster page scanning)
+# for HFP when incomming call starts. Default settings are restored after
+# call is answered or rejected. Page scan interval is much shorter and page
+# scan type changed to interlaced. Such allows faster connection initiated
+# by a headset.
+FastConnectable=false
+
+# Just an example of potential config options for the other interfaces
+#[A2DP]
+#SBCSources=1
+#MPEG12Sources=0
diff --git a/audio/avdtp.c b/audio/avdtp.c
new file mode 100644
index 0000000..83b1aa2
--- /dev/null
+++ b/audio/avdtp.c
@@ -0,0 +1,3914 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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 <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <signal.h>
+#include <netinet/in.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "log.h"
+
+#include "../src/adapter.h"
+#include "../src/manager.h"
+#include "../src/device.h"
+
+#include "device.h"
+#include "manager.h"
+#include "control.h"
+#include "avdtp.h"
+#include "glib-helper.h"
+#include "btio.h"
+#include "sink.h"
+#include "source.h"
+
+#define AVDTP_PSM 25
+
+#define MAX_SEID 0x3E
+
+#define AVDTP_DISCOVER 0x01
+#define AVDTP_GET_CAPABILITIES 0x02
+#define AVDTP_SET_CONFIGURATION 0x03
+#define AVDTP_GET_CONFIGURATION 0x04
+#define AVDTP_RECONFIGURE 0x05
+#define AVDTP_OPEN 0x06
+#define AVDTP_START 0x07
+#define AVDTP_CLOSE 0x08
+#define AVDTP_SUSPEND 0x09
+#define AVDTP_ABORT 0x0A
+#define AVDTP_SECURITY_CONTROL 0x0B
+#define AVDTP_GET_ALL_CAPABILITIES 0x0C
+#define AVDTP_DELAY_REPORT 0x0D
+
+#define AVDTP_PKT_TYPE_SINGLE 0x00
+#define AVDTP_PKT_TYPE_START 0x01
+#define AVDTP_PKT_TYPE_CONTINUE 0x02
+#define AVDTP_PKT_TYPE_END 0x03
+
+#define AVDTP_MSG_TYPE_COMMAND 0x00
+#define AVDTP_MSG_TYPE_GEN_REJECT 0x01
+#define AVDTP_MSG_TYPE_ACCEPT 0x02
+#define AVDTP_MSG_TYPE_REJECT 0x03
+
+#define REQ_TIMEOUT 6
+#define ABORT_TIMEOUT 2
+#define DISCONNECT_TIMEOUT 1
+#define STREAM_TIMEOUT 20
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avdtp_common_header {
+ uint8_t message_type:2;
+ uint8_t packet_type:2;
+ uint8_t transaction:4;
+} __attribute__ ((packed));
+
+struct avdtp_single_header {
+ uint8_t message_type:2;
+ uint8_t packet_type:2;
+ uint8_t transaction:4;
+ uint8_t signal_id:6;
+ uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct avdtp_start_header {
+ uint8_t message_type:2;
+ uint8_t packet_type:2;
+ uint8_t transaction:4;
+ uint8_t no_of_packets;
+ uint8_t signal_id:6;
+ uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct avdtp_continue_header {
+ uint8_t message_type:2;
+ uint8_t packet_type:2;
+ uint8_t transaction:4;
+} __attribute__ ((packed));
+
+struct seid_info {
+ uint8_t rfa0:1;
+ uint8_t inuse:1;
+ uint8_t seid:6;
+ uint8_t rfa2:3;
+ uint8_t type:1;
+ uint8_t media_type:4;
+} __attribute__ ((packed));
+
+struct seid {
+ uint8_t rfa0:2;
+ uint8_t seid:6;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct avdtp_common_header {
+ uint8_t transaction:4;
+ uint8_t packet_type:2;
+ uint8_t message_type:2;
+} __attribute__ ((packed));
+
+struct avdtp_single_header {
+ uint8_t transaction:4;
+ uint8_t packet_type:2;
+ uint8_t message_type:2;
+ uint8_t rfa0:2;
+ uint8_t signal_id:6;
+} __attribute__ ((packed));
+
+struct avdtp_start_header {
+ uint8_t transaction:4;
+ uint8_t packet_type:2;
+ uint8_t message_type:2;
+ uint8_t no_of_packets;
+ uint8_t rfa0:2;
+ uint8_t signal_id:6;
+} __attribute__ ((packed));
+
+struct avdtp_continue_header {
+ uint8_t transaction:4;
+ uint8_t packet_type:2;
+ uint8_t message_type:2;
+} __attribute__ ((packed));
+
+struct seid_info {
+ uint8_t seid:6;
+ uint8_t inuse:1;
+ uint8_t rfa0:1;
+ uint8_t media_type:4;
+ uint8_t type:1;
+ uint8_t rfa2:3;
+} __attribute__ ((packed));
+
+struct seid {
+ uint8_t seid:6;
+ uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+/* packets */
+
+struct discover_resp {
+ struct seid_info seps[0];
+} __attribute__ ((packed));
+
+struct getcap_resp {
+ uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct start_req {
+ struct seid first_seid;
+ struct seid other_seids[0];
+} __attribute__ ((packed));
+
+struct suspend_req {
+ struct seid first_seid;
+ struct seid other_seids[0];
+} __attribute__ ((packed));
+
+struct seid_rej {
+ uint8_t error;
+} __attribute__ ((packed));
+
+struct conf_rej {
+ uint8_t category;
+ uint8_t error;
+} __attribute__ ((packed));
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct seid_req {
+ uint8_t rfa0:2;
+ uint8_t acp_seid:6;
+} __attribute__ ((packed));
+
+struct setconf_req {
+ uint8_t rfa0:2;
+ uint8_t acp_seid:6;
+ uint8_t rfa1:2;
+ uint8_t int_seid:6;
+
+ uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct stream_rej {
+ uint8_t rfa0:2;
+ uint8_t acp_seid:6;
+ uint8_t error;
+} __attribute__ ((packed));
+
+struct reconf_req {
+ uint8_t rfa0:2;
+ uint8_t acp_seid:6;
+
+ uint8_t serv_cap;
+ uint8_t serv_cap_len;
+
+ uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct delay_req {
+ uint8_t rfa0:2;
+ uint8_t acp_seid:6;
+ uint16_t delay;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct seid_req {
+ uint8_t acp_seid:6;
+ uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct setconf_req {
+ uint8_t acp_seid:6;
+ uint8_t rfa0:2;
+ uint8_t int_seid:6;
+ uint8_t rfa1:2;
+
+ uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct stream_rej {
+ uint8_t acp_seid:6;
+ uint8_t rfa0:2;
+ uint8_t error;
+} __attribute__ ((packed));
+
+struct reconf_req {
+ uint8_t acp_seid:6;
+ uint8_t rfa0:2;
+
+ uint8_t serv_cap;
+ uint8_t serv_cap_len;
+
+ uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct delay_req {
+ uint8_t acp_seid:6;
+ uint8_t rfa0:2;
+ uint16_t delay;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+struct in_buf {
+ gboolean active;
+ int no_of_packets;
+ uint8_t transaction;
+ uint8_t message_type;
+ uint8_t signal_id;
+ uint8_t buf[1024];
+ uint8_t data_size;
+};
+
+struct pending_req {
+ uint8_t transaction;
+ uint8_t signal_id;
+ void *data;
+ size_t data_size;
+ struct avdtp_stream *stream; /* Set if the request targeted a stream */
+ guint timeout;
+};
+
+struct avdtp_remote_sep {
+ uint8_t seid;
+ uint8_t type;
+ uint8_t media_type;
+ struct avdtp_service_capability *codec;
+ gboolean delay_reporting;
+ GSList *caps; /* of type struct avdtp_service_capability */
+ struct avdtp_stream *stream;
+};
+
+struct avdtp_server {
+ bdaddr_t src;
+ uint16_t version;
+ GIOChannel *io;
+ GSList *seps;
+ GSList *sessions;
+};
+
+struct avdtp_local_sep {
+ avdtp_state_t state;
+ struct avdtp_stream *stream;
+ struct seid_info info;
+ uint8_t codec;
+ gboolean delay_reporting;
+ GSList *caps;
+ struct avdtp_sep_ind *ind;
+ struct avdtp_sep_cfm *cfm;
+ void *user_data;
+ struct avdtp_server *server;
+};
+
+struct stream_callback {
+ avdtp_stream_state_cb cb;
+ void *user_data;
+ unsigned int id;
+};
+
+struct avdtp_state_callback {
+ avdtp_session_state_cb cb;
+ void *user_data;
+ unsigned int id;
+};
+
+struct avdtp_stream {
+ GIOChannel *io;
+ uint16_t imtu;
+ uint16_t omtu;
+ struct avdtp *session;
+ struct avdtp_local_sep *lsep;
+ uint8_t rseid;
+ GSList *caps;
+ GSList *callbacks;
+ struct avdtp_service_capability *codec;
+ guint io_id; /* Transport GSource ID */
+ guint timer; /* Waiting for other side to close or open
+ * the transport channel */
+ gboolean open_acp; /* If we are in ACT role for Open */
+ gboolean close_int; /* If we are in INT role for Close */
+ gboolean abort_int; /* If we are in INT role for Abort */
+ guint idle_timer;
+ gboolean delay_reporting;
+ uint16_t delay; /* AVDTP 1.3 Delay Reporting feature */
+};
+
+/* Structure describing an AVDTP connection between two devices */
+
+struct avdtp {
+ int ref;
+ int free_lock;
+
+ uint16_t version;
+
+ struct avdtp_server *server;
+ bdaddr_t dst;
+
+ avdtp_session_state_t state;
+
+ /* True if the session should be automatically disconnected */
+ gboolean auto_dc;
+
+ /* True if the entire device is being disconnected */
+ gboolean device_disconnect;
+
+ GIOChannel *io;
+ guint io_id;
+
+ GSList *seps; /* Elements of type struct avdtp_remote_sep * */
+
+ GSList *streams; /* Elements of type struct avdtp_stream * */
+
+ GSList *req_queue; /* Elements of type struct pending_req * */
+ GSList *prio_queue; /* Same as req_queue but is processed before it */
+
+ struct avdtp_stream *pending_open;
+
+ uint16_t imtu;
+ uint16_t omtu;
+
+ struct in_buf in;
+
+ char *buf;
+
+ avdtp_discover_cb_t discov_cb;
+ void *user_data;
+
+ struct pending_req *req;
+
+ guint dc_timer;
+
+ /* Attempt stream setup instead of disconnecting */
+ gboolean stream_setup;
+
+ DBusPendingCall *pending_auth;
+};
+
+static GSList *servers = NULL;
+
+static GSList *avdtp_callbacks = NULL;
+
+static gboolean auto_connect = TRUE;
+
+static int send_request(struct avdtp *session, gboolean priority,
+ struct avdtp_stream *stream, uint8_t signal_id,
+ void *buffer, size_t size);
+static gboolean avdtp_parse_resp(struct avdtp *session,
+ struct avdtp_stream *stream,
+ uint8_t transaction, uint8_t signal_id,
+ void *buf, int size);
+static gboolean avdtp_parse_rej(struct avdtp *session,
+ struct avdtp_stream *stream,
+ uint8_t transaction, uint8_t signal_id,
+ void *buf, int size);
+static int process_queue(struct avdtp *session);
+static void connection_lost(struct avdtp *session, int err);
+static void avdtp_sep_set_state(struct avdtp *session,
+ struct avdtp_local_sep *sep,
+ avdtp_state_t state);
+static void auth_cb(DBusError *derr, void *user_data);
+
+static struct avdtp_server *find_server(GSList *list, const bdaddr_t *src)
+{
+ GSList *l;
+
+ for (l = list; l; l = l->next) {
+ struct avdtp_server *server = l->data;
+
+ if (bacmp(&server->src, src) == 0)
+ return server;
+ }
+
+ return NULL;
+}
+
+static const char *avdtp_statestr(avdtp_state_t state)
+{
+ switch (state) {
+ case AVDTP_STATE_IDLE:
+ return "IDLE";
+ case AVDTP_STATE_CONFIGURED:
+ return "CONFIGURED";
+ case AVDTP_STATE_OPEN:
+ return "OPEN";
+ case AVDTP_STATE_STREAMING:
+ return "STREAMING";
+ case AVDTP_STATE_CLOSING:
+ return "CLOSING";
+ case AVDTP_STATE_ABORTING:
+ return "ABORTING";
+ default:
+ return "<unknown state>";
+ }
+}
+
+static gboolean try_send(int sk, void *data, size_t len)
+{
+ int err;
+
+ do {
+ err = send(sk, data, len, 0);
+ } while (err < 0 && errno == EINTR);
+
+ if (err < 0) {
+ error("send: %s (%d)", strerror(errno), errno);
+ return FALSE;
+ } else if ((size_t) err != len) {
+ error("try_send: complete buffer not sent (%d/%zu bytes)",
+ err, len);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean avdtp_send(struct avdtp *session, uint8_t transaction,
+ uint8_t message_type, uint8_t signal_id,
+ void *data, size_t len)
+{
+ unsigned int cont_fragments, sent;
+ struct avdtp_start_header start;
+ struct avdtp_continue_header cont;
+ int sock;
+
+ if (session->io == NULL) {
+ error("avdtp_send: session is closed");
+ return FALSE;
+ }
+
+ sock = g_io_channel_unix_get_fd(session->io);
+
+ /* Single packet - no fragmentation */
+ if (sizeof(struct avdtp_single_header) + len <= session->omtu) {
+ struct avdtp_single_header single;
+
+ memset(&single, 0, sizeof(single));
+
+ single.transaction = transaction;
+ single.packet_type = AVDTP_PKT_TYPE_SINGLE;
+ single.message_type = message_type;
+ single.signal_id = signal_id;
+
+ memcpy(session->buf, &single, sizeof(single));
+ memcpy(session->buf + sizeof(single), data, len);
+
+ return try_send(sock, session->buf, sizeof(single) + len);
+ }
+
+ /* Check if there is enough space to start packet */
+ if (session->omtu < sizeof(start)) {
+ error("No enough space to fragment packet");
+ return FALSE;
+ }
+
+ /* Count the number of needed fragments */
+ cont_fragments = (len - (session->omtu - sizeof(start))) /
+ (session->omtu - sizeof(cont)) + 1;
+
+ DBG("%zu bytes split into %d fragments", len, cont_fragments + 1);
+
+ /* Send the start packet */
+ memset(&start, 0, sizeof(start));
+ start.transaction = transaction;
+ start.packet_type = AVDTP_PKT_TYPE_START;
+ start.message_type = message_type;
+ start.no_of_packets = cont_fragments + 1;
+ start.signal_id = signal_id;
+
+ memcpy(session->buf, &start, sizeof(start));
+ memcpy(session->buf + sizeof(start), data,
+ session->omtu - sizeof(start));
+
+ if (!try_send(sock, session->buf, session->omtu))
+ return FALSE;
+
+ DBG("first packet with %zu bytes sent", session->omtu - sizeof(start));
+
+ sent = session->omtu - sizeof(start);
+
+ /* Send the continue fragments and the end packet */
+ while (sent < len) {
+ int left, to_copy;
+
+ left = len - sent;
+ if (left + sizeof(cont) > session->omtu) {
+ cont.packet_type = AVDTP_PKT_TYPE_CONTINUE;
+ to_copy = session->omtu - sizeof(cont);
+ DBG("sending continue with %d bytes", to_copy);
+ } else {
+ cont.packet_type = AVDTP_PKT_TYPE_END;
+ to_copy = left;
+ DBG("sending end with %d bytes", to_copy);
+ }
+
+ cont.transaction = transaction;
+ cont.message_type = message_type;
+
+ memcpy(session->buf, &cont, sizeof(cont));
+ memcpy(session->buf + sizeof(cont), data + sent, to_copy);
+
+ if (!try_send(sock, session->buf, to_copy + sizeof(cont)))
+ return FALSE;
+
+ sent += to_copy;
+ }
+
+ return TRUE;
+}
+
+static void pending_req_free(struct pending_req *req)
+{
+ if (req->timeout)
+ g_source_remove(req->timeout);
+ g_free(req->data);
+ g_free(req);
+}
+
+static void close_stream(struct avdtp_stream *stream)
+{
+ int sock;
+
+ if (stream->io == NULL)
+ return;
+
+ sock = g_io_channel_unix_get_fd(stream->io);
+
+ shutdown(sock, SHUT_RDWR);
+
+ g_io_channel_shutdown(stream->io, FALSE, NULL);
+
+ g_io_channel_unref(stream->io);
+ stream->io = NULL;
+}
+
+static gboolean stream_close_timeout(gpointer user_data)
+{
+ struct avdtp_stream *stream = user_data;
+
+ DBG("Timed out waiting for peer to close the transport channel");
+
+ stream->timer = 0;
+
+ close_stream(stream);
+
+ return FALSE;
+}
+
+static gboolean stream_open_timeout(gpointer user_data)
+{
+ struct avdtp_stream *stream = user_data;
+
+ DBG("Timed out waiting for peer to open the transport channel");
+
+ stream->timer = 0;
+
+ stream->session->pending_open = NULL;
+
+ avdtp_abort(stream->session, stream);
+
+ return FALSE;
+}
+
+static gboolean disconnect_timeout(gpointer user_data)
+{
+ struct avdtp *session = user_data;
+ struct audio_device *dev;
+ gboolean stream_setup;
+
+ session->dc_timer = 0;
+ stream_setup = session->stream_setup;
+ session->stream_setup = FALSE;
+
+ dev = manager_get_device(&session->server->src, &session->dst, FALSE);
+
+ if (dev && dev->sink && stream_setup)
+ sink_setup_stream(dev->sink, session);
+ else if (dev && dev->source && stream_setup)
+ source_setup_stream(dev->source, session);
+ else
+ connection_lost(session, ETIMEDOUT);
+
+ return FALSE;
+}
+
+static void remove_disconnect_timer(struct avdtp *session)
+{
+ g_source_remove(session->dc_timer);
+ session->dc_timer = 0;
+ session->stream_setup = FALSE;
+}
+
+static void set_disconnect_timer(struct avdtp *session)
+{
+ if (session->dc_timer)
+ remove_disconnect_timer(session);
+
+ if (session->device_disconnect) {
+ g_idle_add(disconnect_timeout, session);
+ return;
+ }
+
+ session->dc_timer = g_timeout_add_seconds(DISCONNECT_TIMEOUT,
+ disconnect_timeout,
+ session);
+}
+
+void avdtp_error_init(struct avdtp_error *err, uint8_t category, int id)
+{
+ err->category = category;
+
+ if (category == AVDTP_ERRNO)
+ err->err.posix_errno = id;
+ else
+ err->err.error_code = id;
+}
+
+uint8_t avdtp_error_category(struct avdtp_error *err)
+{
+ return err->category;
+}
+
+int avdtp_error_error_code(struct avdtp_error *err)
+{
+ assert(err->category != AVDTP_ERRNO);
+ return err->err.error_code;
+}
+
+int avdtp_error_posix_errno(struct avdtp_error *err)
+{
+ assert(err->category == AVDTP_ERRNO);
+ return err->err.posix_errno;
+}
+
+static struct avdtp_stream *find_stream_by_rseid(struct avdtp *session,
+ uint8_t rseid)
+{
+ GSList *l;
+
+ for (l = session->streams; l != NULL; l = g_slist_next(l)) {
+ struct avdtp_stream *stream = l->data;
+
+ if (stream->rseid == rseid)
+ return stream;
+ }
+
+ return NULL;
+}
+
+static struct avdtp_remote_sep *find_remote_sep(GSList *seps, uint8_t seid)
+{
+ GSList *l;
+
+ for (l = seps; l != NULL; l = g_slist_next(l)) {
+ struct avdtp_remote_sep *sep = l->data;
+
+ if (sep->seid == seid)
+ return sep;
+ }
+
+ return NULL;
+}
+
+static void avdtp_set_state(struct avdtp *session,
+ avdtp_session_state_t new_state)
+{
+ GSList *l;
+ struct audio_device *dev;
+ bdaddr_t src, dst;
+ avdtp_session_state_t old_state = session->state;
+
+ session->state = new_state;
+
+ avdtp_get_peers(session, &src, &dst);
+ dev = manager_get_device(&src, &dst, FALSE);
+ if (dev == NULL) {
+ error("avdtp_set_state(): no matching audio device");
+ return;
+ }
+
+ for (l = avdtp_callbacks; l != NULL; l = l->next) {
+ struct avdtp_state_callback *cb = l->data;
+ cb->cb(dev, session, old_state, new_state, cb->user_data);
+ }
+}
+
+static void stream_free(struct avdtp_stream *stream)
+{
+ struct avdtp_remote_sep *rsep;
+
+ stream->lsep->info.inuse = 0;
+ stream->lsep->stream = NULL;
+
+ rsep = find_remote_sep(stream->session->seps, stream->rseid);
+ if (rsep)
+ rsep->stream = NULL;
+
+ if (stream->timer)
+ g_source_remove(stream->timer);
+
+ if (stream->io)
+ close_stream(stream);
+
+ if (stream->io_id)
+ g_source_remove(stream->io_id);
+
+ g_slist_foreach(stream->callbacks, (GFunc) g_free, NULL);
+ g_slist_free(stream->callbacks);
+
+ g_slist_foreach(stream->caps, (GFunc) g_free, NULL);
+ g_slist_free(stream->caps);
+
+ g_free(stream);
+}
+
+static gboolean stream_timeout(gpointer user_data)
+{
+ struct avdtp_stream *stream = user_data;
+ struct avdtp *session = stream->session;
+
+ if (avdtp_close(session, stream, FALSE) < 0)
+ error("stream_timeout: closing AVDTP stream failed");
+
+ stream->idle_timer = 0;
+
+ return FALSE;
+}
+
+static gboolean transport_cb(GIOChannel *chan, GIOCondition cond,
+ gpointer data)
+{
+ struct avdtp_stream *stream = data;
+ struct avdtp_local_sep *sep = stream->lsep;
+
+ if (stream->close_int && sep->cfm && sep->cfm->close)
+ sep->cfm->close(stream->session, sep, stream, NULL,
+ sep->user_data);
+
+ if (!(cond & G_IO_NVAL))
+ close_stream(stream);
+
+ stream->io_id = 0;
+
+ if (!stream->abort_int)
+ avdtp_sep_set_state(stream->session, sep, AVDTP_STATE_IDLE);
+
+ return FALSE;
+}
+
+static int get_send_buffer_size(int sk)
+{
+ int size;
+ socklen_t optlen = sizeof(size);
+
+ if (getsockopt(sk, SOL_SOCKET, SO_SNDBUF, &size, &optlen) < 0) {
+ int err = -errno;
+ error("getsockopt(SO_SNDBUF) failed: %s (%d)", strerror(-err),
+ -err);
+ return err;
+ }
+
+ /*
+ * Doubled value is returned by getsockopt since kernel uses that
+ * space for its own purposes (see man 7 socket, bookkeeping overhead
+ * for SO_SNDBUF).
+ */
+ return size / 2;
+}
+
+static int set_send_buffer_size(int sk, int size)
+{
+ socklen_t optlen = sizeof(size);
+
+ if (setsockopt(sk, SOL_SOCKET, SO_SNDBUF, &size, optlen) < 0) {
+ int err = -errno;
+ error("setsockopt(SO_SNDBUF) failed: %s (%d)", strerror(-err),
+ -err);
+ return err;
+ }
+
+ return 0;
+}
+
+static void handle_transport_connect(struct avdtp *session, GIOChannel *io,
+ uint16_t imtu, uint16_t omtu)
+{
+ struct avdtp_stream *stream = session->pending_open;
+ struct avdtp_local_sep *sep = stream->lsep;
+
+ session->pending_open = NULL;
+
+ if (stream->timer) {
+ g_source_remove(stream->timer);
+ stream->timer = 0;
+ }
+
+ if (io == NULL) {
+ if (!stream->open_acp && sep->cfm && sep->cfm->open) {
+ struct avdtp_error err;
+ avdtp_error_init(&err, AVDTP_ERRNO, EIO);
+ sep->cfm->open(session, sep, NULL, &err,
+ sep->user_data);
+ }
+ return;
+ }
+
+ if (stream->io == NULL)
+ stream->io = g_io_channel_ref(io);
+
+ stream->omtu = omtu;
+ stream->imtu = imtu;
+
+ /* only if local SEP is of type SRC */
+ if (sep->info.type == AVDTP_SEP_TYPE_SOURCE) {
+ int sk, buf_size, min_buf_size;
+
+ sk = g_io_channel_unix_get_fd(stream->io);
+ buf_size = get_send_buffer_size(sk);
+ if (buf_size < 0)
+ goto proceed;
+
+ DBG("sk %d, omtu %d, send buffer size %d", sk, omtu, buf_size);
+ min_buf_size = omtu * 2;
+ if (buf_size < min_buf_size) {
+ DBG("send buffer size to be increassed to %d",
+ min_buf_size);
+ set_send_buffer_size(sk, min_buf_size);
+ }
+ }
+
+proceed:
+ if (!stream->open_acp && sep->cfm && sep->cfm->open)
+ sep->cfm->open(session, sep, stream, NULL, sep->user_data);
+
+ avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN);
+
+ stream->io_id = g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ (GIOFunc) transport_cb, stream);
+}
+
+static int pending_req_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct pending_req *req = a;
+ const struct avdtp_stream *stream = b;
+
+ if (req->stream == stream)
+ return 0;
+
+ return -1;
+}
+
+static void cleanup_queue(struct avdtp *session, struct avdtp_stream *stream)
+{
+ GSList *l;
+ struct pending_req *req;
+
+ while ((l = g_slist_find_custom(session->prio_queue, stream,
+ pending_req_cmp))) {
+ req = l->data;
+ pending_req_free(req);
+ session->prio_queue = g_slist_remove(session->prio_queue, req);
+ }
+
+ while ((l = g_slist_find_custom(session->req_queue, stream,
+ pending_req_cmp))) {
+ req = l->data;
+ pending_req_free(req);
+ session->req_queue = g_slist_remove(session->req_queue, req);
+ }
+}
+
+static void handle_unanswered_req(struct avdtp *session,
+ struct avdtp_stream *stream)
+{
+ struct pending_req *req;
+ struct avdtp_local_sep *lsep;
+ struct avdtp_error err;
+
+ if (session->req->signal_id == AVDTP_ABORT) {
+ /* Avoid freeing the Abort request here */
+ DBG("handle_unanswered_req: Abort req, returning");
+ session->req->stream = NULL;
+ return;
+ }
+
+ req = session->req;
+ session->req = NULL;
+
+ avdtp_error_init(&err, AVDTP_ERRNO, EIO);
+
+ lsep = stream->lsep;
+
+ switch (req->signal_id) {
+ case AVDTP_RECONFIGURE:
+ error("No reply to Reconfigure request");
+ if (lsep && lsep->cfm && lsep->cfm->reconfigure)
+ lsep->cfm->reconfigure(session, lsep, stream, &err,
+ lsep->user_data);
+ break;
+ case AVDTP_OPEN:
+ error("No reply to Open request");
+ if (lsep && lsep->cfm && lsep->cfm->open)
+ lsep->cfm->open(session, lsep, stream, &err,
+ lsep->user_data);
+ break;
+ case AVDTP_START:
+ error("No reply to Start request");
+ if (lsep && lsep->cfm && lsep->cfm->start)
+ lsep->cfm->start(session, lsep, stream, &err,
+ lsep->user_data);
+ break;
+ case AVDTP_SUSPEND:
+ error("No reply to Suspend request");
+ if (lsep && lsep->cfm && lsep->cfm->suspend)
+ lsep->cfm->suspend(session, lsep, stream, &err,
+ lsep->user_data);
+ break;
+ case AVDTP_CLOSE:
+ error("No reply to Close request");
+ if (lsep && lsep->cfm && lsep->cfm->close)
+ lsep->cfm->close(session, lsep, stream, &err,
+ lsep->user_data);
+ break;
+ case AVDTP_SET_CONFIGURATION:
+ error("No reply to SetConfiguration request");
+ if (lsep && lsep->cfm && lsep->cfm->set_configuration)
+ lsep->cfm->set_configuration(session, lsep, stream,
+ &err, lsep->user_data);
+ }
+
+ pending_req_free(req);
+}
+
+static void avdtp_sep_set_state(struct avdtp *session,
+ struct avdtp_local_sep *sep,
+ avdtp_state_t state)
+{
+ struct avdtp_stream *stream = sep->stream;
+ avdtp_state_t old_state;
+ struct avdtp_error err, *err_ptr = NULL;
+ GSList *l;
+
+ if (!stream) {
+ error("Error changing sep state: stream not available");
+ return;
+ }
+
+ if (sep->state == state) {
+ avdtp_error_init(&err, AVDTP_ERRNO, EIO);
+ DBG("stream state change failed: %s", avdtp_strerror(&err));
+ err_ptr = &err;
+ } else {
+ err_ptr = NULL;
+ DBG("stream state changed: %s -> %s",
+ avdtp_statestr(sep->state),
+ avdtp_statestr(state));
+ }
+
+ old_state = sep->state;
+ sep->state = state;
+
+ for (l = stream->callbacks; l != NULL; l = g_slist_next(l)) {
+ struct stream_callback *cb = l->data;
+ cb->cb(stream, old_state, state, err_ptr, cb->user_data);
+ }
+
+ switch (state) {
+ case AVDTP_STATE_CONFIGURED:
+ if (sep->info.type == AVDTP_SEP_TYPE_SINK)
+ avdtp_delay_report(session, stream, stream->delay);
+ break;
+ case AVDTP_STATE_OPEN:
+ if (old_state > AVDTP_STATE_OPEN && session->auto_dc)
+ stream->idle_timer = g_timeout_add_seconds(STREAM_TIMEOUT,
+ stream_timeout,
+ stream);
+ break;
+ case AVDTP_STATE_STREAMING:
+ case AVDTP_STATE_CLOSING:
+ case AVDTP_STATE_ABORTING:
+ if (stream->idle_timer) {
+ g_source_remove(stream->idle_timer);
+ stream->idle_timer = 0;
+ }
+ break;
+ case AVDTP_STATE_IDLE:
+ if (stream->idle_timer) {
+ g_source_remove(stream->idle_timer);
+ stream->idle_timer = 0;
+ }
+ session->streams = g_slist_remove(session->streams, stream);
+ if (session->pending_open == stream)
+ handle_transport_connect(session, NULL, 0, 0);
+ if (session->req && session->req->stream == stream)
+ handle_unanswered_req(session, stream);
+ /* Remove pending commands for this stream from the queue */
+ cleanup_queue(session, stream);
+ stream_free(stream);
+ break;
+ default:
+ break;
+ }
+}
+
+static void finalize_discovery(struct avdtp *session, int err)
+{
+ struct avdtp_error avdtp_err;
+
+ avdtp_error_init(&avdtp_err, AVDTP_ERRNO, err);
+
+ if (!session->discov_cb)
+ return;
+
+ session->discov_cb(session, session->seps,
+ err ? &avdtp_err : NULL,
+ session->user_data);
+
+ session->discov_cb = NULL;
+ session->user_data = NULL;
+}
+
+static void release_stream(struct avdtp_stream *stream, struct avdtp *session)
+{
+ struct avdtp_local_sep *sep = stream->lsep;
+
+ if (sep->cfm && sep->cfm->abort &&
+ (sep->state != AVDTP_STATE_ABORTING ||
+ stream->abort_int))
+ sep->cfm->abort(session, sep, stream, NULL, sep->user_data);
+
+ avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE);
+}
+
+static void connection_lost(struct avdtp *session, int err)
+{
+ char address[18];
+ struct audio_device *dev;
+
+ ba2str(&session->dst, address);
+ DBG("Disconnected from %s", address);
+
+ dev = manager_get_device(&session->server->src, &session->dst, FALSE);
+
+ if (dev != NULL && session->state == AVDTP_SESSION_STATE_CONNECTING &&
+ err != EACCES)
+ audio_device_cancel_authorization(dev, auth_cb, session);
+
+ session->free_lock = 1;
+
+ finalize_discovery(session, err);
+
+ g_slist_foreach(session->streams, (GFunc) release_stream, session);
+ session->streams = NULL;
+
+ session->free_lock = 0;
+
+ if (session->io) {
+ g_io_channel_shutdown(session->io, FALSE, NULL);
+ g_io_channel_unref(session->io);
+ session->io = NULL;
+ }
+
+ avdtp_set_state(session, AVDTP_SESSION_STATE_DISCONNECTED);
+
+ if (session->io_id) {
+ g_source_remove(session->io_id);
+ session->io_id = 0;
+ }
+
+ if (session->dc_timer)
+ remove_disconnect_timer(session);
+
+ session->auto_dc = TRUE;
+
+ if (session->ref != 1)
+ error("connection_lost: ref count not 1 after all callbacks");
+ else
+ avdtp_unref(session);
+}
+
+void avdtp_unref(struct avdtp *session)
+{
+ struct avdtp_server *server;
+
+ if (!session)
+ return;
+
+ session->ref--;
+
+ DBG("%p: ref=%d", session, session->ref);
+
+ if (session->ref == 1) {
+ if (session->state == AVDTP_SESSION_STATE_CONNECTING &&
+ session->io) {
+ struct audio_device *dev;
+ dev = manager_get_device(&session->server->src,
+ &session->dst, FALSE);
+ audio_device_cancel_authorization(dev, auth_cb,
+ session);
+ g_io_channel_shutdown(session->io, TRUE, NULL);
+ g_io_channel_unref(session->io);
+ session->io = NULL;
+ avdtp_set_state(session,
+ AVDTP_SESSION_STATE_DISCONNECTED);
+ }
+
+ if (session->io)
+ set_disconnect_timer(session);
+ else if (!session->free_lock) /* Drop the local ref if we
+ aren't connected */
+ session->ref--;
+ }
+
+ if (session->ref > 0)
+ return;
+
+ server = session->server;
+
+ DBG("%p: freeing session and removing from list", session);
+
+ if (session->dc_timer)
+ remove_disconnect_timer(session);
+
+ server->sessions = g_slist_remove(server->sessions, session);
+
+ if (session->req)
+ pending_req_free(session->req);
+
+ g_slist_foreach(session->seps, (GFunc) g_free, NULL);
+ g_slist_free(session->seps);
+
+ g_free(session->buf);
+
+ g_free(session);
+}
+
+struct avdtp *avdtp_ref(struct avdtp *session)
+{
+ session->ref++;
+ DBG("%p: ref=%d", session, session->ref);
+ if (session->dc_timer)
+ remove_disconnect_timer(session);
+ return session;
+}
+
+static struct avdtp_local_sep *find_local_sep_by_seid(struct avdtp_server *server,
+ uint8_t seid)
+{
+ GSList *l;
+
+ for (l = server->seps; l != NULL; l = g_slist_next(l)) {
+ struct avdtp_local_sep *sep = l->data;
+
+ if (sep->info.seid == seid)
+ return sep;
+ }
+
+ return NULL;
+}
+
+struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,
+ struct avdtp_local_sep *lsep)
+{
+ GSList *l;
+
+ if (lsep->info.inuse)
+ return NULL;
+
+ for (l = session->seps; l != NULL; l = g_slist_next(l)) {
+ struct avdtp_remote_sep *sep = l->data;
+ struct avdtp_service_capability *cap;
+ struct avdtp_media_codec_capability *codec_data;
+
+ /* Type must be different: source <-> sink */
+ if (sep->type == lsep->info.type)
+ continue;
+
+ if (sep->media_type != lsep->info.media_type)
+ continue;
+
+ if (!sep->codec)
+ continue;
+
+ cap = sep->codec;
+ codec_data = (void *) cap->data;
+
+ if (codec_data->media_codec_type != lsep->codec)
+ continue;
+
+ if (sep->stream == NULL)
+ return sep;
+ }
+
+ return NULL;
+}
+
+static GSList *caps_to_list(uint8_t *data, int size,
+ struct avdtp_service_capability **codec,
+ gboolean *delay_reporting)
+{
+ GSList *caps;
+ int processed;
+
+ if (delay_reporting)
+ *delay_reporting = FALSE;
+
+ for (processed = 0, caps = NULL; processed + 2 <= size;) {
+ struct avdtp_service_capability *cap;
+ uint8_t length, category;
+
+ category = data[0];
+ length = data[1];
+
+ if (processed + 2 + length > size) {
+ error("Invalid capability data in getcap resp");
+ break;
+ }
+
+ cap = g_malloc(sizeof(struct avdtp_service_capability) +
+ length);
+ memcpy(cap, data, 2 + length);
+
+ processed += 2 + length;
+ data += 2 + length;
+
+ caps = g_slist_append(caps, cap);
+
+ if (category == AVDTP_MEDIA_CODEC &&
+ length >=
+ sizeof(struct avdtp_media_codec_capability))
+ *codec = cap;
+ else if (category == AVDTP_DELAY_REPORTING && delay_reporting)
+ *delay_reporting = TRUE;
+ }
+
+ return caps;
+}
+
+static gboolean avdtp_unknown_cmd(struct avdtp *session, uint8_t transaction,
+ uint8_t signal_id)
+{
+ return avdtp_send(session, transaction, AVDTP_MSG_TYPE_GEN_REJECT,
+ signal_id, NULL, 0);
+}
+
+static gboolean avdtp_discover_cmd(struct avdtp *session, uint8_t transaction,
+ void *buf, int size)
+{
+ GSList *l;
+ unsigned int rsp_size, sep_count, i;
+ struct seid_info *seps;
+ gboolean ret;
+
+ sep_count = g_slist_length(session->server->seps);
+
+ if (sep_count == 0) {
+ uint8_t err = AVDTP_NOT_SUPPORTED_COMMAND;
+ return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+ AVDTP_DISCOVER, &err, sizeof(err));
+ }
+
+ rsp_size = sep_count * sizeof(struct seid_info);
+
+ seps = g_new0(struct seid_info, sep_count);
+
+ for (l = session->server->seps, i = 0; l != NULL; l = l->next, i++) {
+ struct avdtp_local_sep *sep = l->data;
+
+ memcpy(&seps[i], &sep->info, sizeof(struct seid_info));
+ }
+
+ ret = avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+ AVDTP_DISCOVER, seps, rsp_size);
+ g_free(seps);
+
+ return ret;
+}
+
+static gboolean avdtp_getcap_cmd(struct avdtp *session, uint8_t transaction,
+ struct seid_req *req, unsigned int size,
+ gboolean get_all)
+{
+ GSList *l, *caps;
+ struct avdtp_local_sep *sep = NULL;
+ unsigned int rsp_size;
+ uint8_t err, buf[1024], *ptr = buf;
+ uint8_t cmd;
+
+ cmd = get_all ? AVDTP_GET_ALL_CAPABILITIES : AVDTP_GET_CAPABILITIES;
+
+ if (size < sizeof(struct seid_req)) {
+ err = AVDTP_BAD_LENGTH;
+ goto failed;
+ }
+
+ sep = find_local_sep_by_seid(session->server, req->acp_seid);
+ if (!sep) {
+ err = AVDTP_BAD_ACP_SEID;
+ goto failed;
+ }
+
+ if (get_all && session->server->version < 0x0103)
+ return avdtp_unknown_cmd(session, transaction, cmd);
+
+ if (!sep->ind->get_capability(session, sep, get_all, &caps,
+ &err, sep->user_data))
+ goto failed;
+
+ for (l = caps, rsp_size = 0; l != NULL; l = g_slist_next(l)) {
+ struct avdtp_service_capability *cap = l->data;
+
+ if (rsp_size + cap->length + 2 > sizeof(buf))
+ break;
+
+ memcpy(ptr, cap, cap->length + 2);
+ rsp_size += cap->length + 2;
+ ptr += cap->length + 2;
+
+ g_free(cap);
+ }
+
+ g_slist_free(caps);
+
+ return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, cmd,
+ buf, rsp_size);
+
+failed:
+ return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, cmd,
+ &err, sizeof(err));
+}
+
+static void setconf_cb(struct avdtp *session, struct avdtp_stream *stream,
+ struct avdtp_error *err)
+{
+ struct conf_rej rej;
+ struct avdtp_local_sep *sep;
+
+ if (err != NULL) {
+ rej.error = AVDTP_UNSUPPORTED_CONFIGURATION;
+ rej.category = err->err.error_code;
+ avdtp_send(session, session->in.transaction,
+ AVDTP_MSG_TYPE_REJECT, AVDTP_SET_CONFIGURATION,
+ &rej, sizeof(rej));
+ return;
+ }
+
+ if (!avdtp_send(session, session->in.transaction, AVDTP_MSG_TYPE_ACCEPT,
+ AVDTP_SET_CONFIGURATION, NULL, 0)) {
+ stream_free(stream);
+ return;
+ }
+
+ sep = stream->lsep;
+ sep->stream = stream;
+ sep->info.inuse = 1;
+ session->streams = g_slist_append(session->streams, stream);
+
+ avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED);
+}
+
+static gboolean avdtp_setconf_cmd(struct avdtp *session, uint8_t transaction,
+ struct setconf_req *req, unsigned int size)
+{
+ struct conf_rej rej;
+ struct avdtp_local_sep *sep;
+ struct avdtp_stream *stream;
+ uint8_t err, category = 0x00;
+ struct audio_device *dev;
+ bdaddr_t src, dst;
+ GSList *l;
+
+ if (size < sizeof(struct setconf_req)) {
+ error("Too short getcap request");
+ return FALSE;
+ }
+
+ sep = find_local_sep_by_seid(session->server, req->acp_seid);
+ if (!sep) {
+ err = AVDTP_BAD_ACP_SEID;
+ goto failed;
+ }
+
+ if (sep->stream) {
+ err = AVDTP_SEP_IN_USE;
+ goto failed;
+ }
+
+ avdtp_get_peers(session, &src, &dst);
+ dev = manager_get_device(&src, &dst, FALSE);
+ if (!dev) {
+ error("Unable to get a audio device object");
+ err = AVDTP_BAD_STATE;
+ goto failed;
+ }
+
+ switch (sep->info.type) {
+ case AVDTP_SEP_TYPE_SOURCE:
+ if (!dev->sink) {
+ btd_device_add_uuid(dev->btd_dev, A2DP_SINK_UUID);
+ if (!dev->sink) {
+ error("Unable to get a audio sink object");
+ err = AVDTP_BAD_STATE;
+ goto failed;
+ }
+ }
+ break;
+ case AVDTP_SEP_TYPE_SINK:
+ if (!dev->source) {
+ btd_device_add_uuid(dev->btd_dev, A2DP_SOURCE_UUID);
+ if (!dev->sink) {
+ error("Unable to get a audio source object");
+ err = AVDTP_BAD_STATE;
+ goto failed;
+ }
+ }
+ break;
+ }
+
+ stream = g_new0(struct avdtp_stream, 1);
+ stream->session = session;
+ stream->lsep = sep;
+ stream->rseid = req->int_seid;
+ stream->caps = caps_to_list(req->caps,
+ size - sizeof(struct setconf_req),
+ &stream->codec,
+ &stream->delay_reporting);
+
+ /* Verify that the Media Transport capability's length = 0. Reject otherwise */
+ for (l = stream->caps; l != NULL; l = g_slist_next(l)) {
+ struct avdtp_service_capability *cap = l->data;
+
+ if (cap->category == AVDTP_MEDIA_TRANSPORT && cap->length != 0) {
+ err = AVDTP_BAD_MEDIA_TRANSPORT_FORMAT;
+ goto failed_stream;
+ }
+ }
+
+ if (stream->delay_reporting && session->version < 0x0103)
+ session->version = 0x0103;
+
+ if (sep->ind && sep->ind->set_configuration) {
+ if (!sep->ind->set_configuration(session, sep, stream,
+ stream->caps,
+ setconf_cb,
+ sep->user_data)) {
+ err = AVDTP_UNSUPPORTED_CONFIGURATION;
+ category = 0x00;
+ goto failed_stream;
+ }
+ } else {
+ if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+ AVDTP_SET_CONFIGURATION, NULL, 0)) {
+ stream_free(stream);
+ return FALSE;
+ }
+
+ sep->stream = stream;
+ sep->info.inuse = 1;
+ session->streams = g_slist_append(session->streams, stream);
+
+ avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED);
+ }
+
+ return TRUE;
+
+failed_stream:
+ stream_free(stream);
+failed:
+ rej.error = err;
+ rej.category = category;
+ return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+ AVDTP_SET_CONFIGURATION, &rej, sizeof(rej));
+}
+
+static gboolean avdtp_getconf_cmd(struct avdtp *session, uint8_t transaction,
+ struct seid_req *req, int size)
+{
+ GSList *l;
+ struct avdtp_local_sep *sep = NULL;
+ int rsp_size;
+ uint8_t err;
+ uint8_t buf[1024];
+ uint8_t *ptr = buf;
+
+ if (size < (int) sizeof(struct seid_req)) {
+ error("Too short getconf request");
+ return FALSE;
+ }
+
+ memset(buf, 0, sizeof(buf));
+
+ sep = find_local_sep_by_seid(session->server, req->acp_seid);
+ if (!sep) {
+ err = AVDTP_BAD_ACP_SEID;
+ goto failed;
+ }
+ if (!sep->stream || !sep->stream->caps) {
+ err = AVDTP_UNSUPPORTED_CONFIGURATION;
+ goto failed;
+ }
+
+ for (l = sep->stream->caps, rsp_size = 0; l != NULL; l = g_slist_next(l)) {
+ struct avdtp_service_capability *cap = l->data;
+
+ if (rsp_size + cap->length + 2 > (int) sizeof(buf))
+ break;
+
+ memcpy(ptr, cap, cap->length + 2);
+ rsp_size += cap->length + 2;
+ ptr += cap->length + 2;
+ }
+
+ return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+ AVDTP_GET_CONFIGURATION, buf, rsp_size);
+
+failed:
+ return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+ AVDTP_GET_CONFIGURATION, &err, sizeof(err));
+}
+
+static gboolean avdtp_reconf_cmd(struct avdtp *session, uint8_t transaction,
+ struct seid_req *req, int size)
+{
+ return avdtp_unknown_cmd(session, transaction, AVDTP_RECONFIGURE);
+}
+
+static gboolean avdtp_open_cmd(struct avdtp *session, uint8_t transaction,
+ struct seid_req *req, unsigned int size)
+{
+ struct avdtp_local_sep *sep;
+ struct avdtp_stream *stream;
+ uint8_t err;
+
+ if (size < sizeof(struct seid_req)) {
+ error("Too short abort request");
+ return FALSE;
+ }
+
+ sep = find_local_sep_by_seid(session->server, req->acp_seid);
+ if (!sep) {
+ err = AVDTP_BAD_ACP_SEID;
+ goto failed;
+ }
+
+ if (sep->state != AVDTP_STATE_CONFIGURED) {
+ err = AVDTP_BAD_STATE;
+ goto failed;
+ }
+
+ stream = sep->stream;
+
+ if (sep->ind && sep->ind->open) {
+ if (!sep->ind->open(session, sep, stream, &err,
+ sep->user_data))
+ goto failed;
+ }
+
+ if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+ AVDTP_OPEN, NULL, 0))
+ return FALSE;
+
+ stream->open_acp = TRUE;
+ session->pending_open = stream;
+ stream->timer = g_timeout_add_seconds(REQ_TIMEOUT,
+ stream_open_timeout,
+ stream);
+
+ return TRUE;
+
+failed:
+ return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+ AVDTP_OPEN, &err, sizeof(err));
+}
+
+static gboolean avdtp_start_cmd(struct avdtp *session, uint8_t transaction,
+ struct start_req *req, unsigned int size)
+{
+ struct avdtp_local_sep *sep;
+ struct avdtp_stream *stream;
+ struct stream_rej rej;
+ struct seid *seid;
+ uint8_t err, failed_seid;
+ int seid_count, i;
+
+ if (size < sizeof(struct start_req)) {
+ error("Too short start request");
+ return FALSE;
+ }
+
+ seid_count = 1 + size - sizeof(struct start_req);
+
+ seid = &req->first_seid;
+
+ for (i = 0; i < seid_count; i++, seid++) {
+ failed_seid = seid->seid;
+
+ sep = find_local_sep_by_seid(session->server,
+ req->first_seid.seid);
+ if (!sep || !sep->stream) {
+ err = AVDTP_BAD_ACP_SEID;
+ goto failed;
+ }
+
+ stream = sep->stream;
+
+ if (sep->state != AVDTP_STATE_OPEN) {
+ err = AVDTP_BAD_STATE;
+ goto failed;
+ }
+
+ if (sep->ind && sep->ind->start) {
+ if (!sep->ind->start(session, sep, stream, &err,
+ sep->user_data))
+ goto failed;
+ }
+
+ avdtp_sep_set_state(session, sep, AVDTP_STATE_STREAMING);
+ }
+
+ return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+ AVDTP_START, NULL, 0);
+
+failed:
+ memset(&rej, 0, sizeof(rej));
+ rej.acp_seid = failed_seid;
+ rej.error = err;
+ return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+ AVDTP_START, &rej, sizeof(rej));
+}
+
+static gboolean avdtp_close_cmd(struct avdtp *session, uint8_t transaction,
+ struct seid_req *req, unsigned int size)
+{
+ struct avdtp_local_sep *sep;
+ struct avdtp_stream *stream;
+ uint8_t err;
+
+ if (size < sizeof(struct seid_req)) {
+ error("Too short close request");
+ return FALSE;
+ }
+
+ sep = find_local_sep_by_seid(session->server, req->acp_seid);
+ if (!sep || !sep->stream) {
+ err = AVDTP_BAD_ACP_SEID;
+ goto failed;
+ }
+
+ if (sep->state != AVDTP_STATE_OPEN &&
+ sep->state != AVDTP_STATE_STREAMING) {
+ err = AVDTP_BAD_STATE;
+ goto failed;
+ }
+
+ stream = sep->stream;
+
+ if (sep->ind && sep->ind->close) {
+ if (!sep->ind->close(session, sep, stream, &err,
+ sep->user_data))
+ goto failed;
+ }
+
+ avdtp_sep_set_state(session, sep, AVDTP_STATE_CLOSING);
+
+ if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+ AVDTP_CLOSE, NULL, 0))
+ return FALSE;
+
+ stream->timer = g_timeout_add_seconds(REQ_TIMEOUT,
+ stream_close_timeout,
+ stream);
+
+ return TRUE;
+
+failed:
+ return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+ AVDTP_CLOSE, &err, sizeof(err));
+}
+
+static gboolean avdtp_suspend_cmd(struct avdtp *session, uint8_t transaction,
+ struct suspend_req *req, unsigned int size)
+{
+ struct avdtp_local_sep *sep;
+ struct avdtp_stream *stream;
+ struct stream_rej rej;
+ struct seid *seid;
+ uint8_t err, failed_seid;
+ int seid_count, i;
+
+ if (size < sizeof(struct suspend_req)) {
+ error("Too short suspend request");
+ return FALSE;
+ }
+
+ seid_count = 1 + size - sizeof(struct suspend_req);
+
+ seid = &req->first_seid;
+
+ for (i = 0; i < seid_count; i++, seid++) {
+ failed_seid = seid->seid;
+
+ sep = find_local_sep_by_seid(session->server,
+ req->first_seid.seid);
+ if (!sep || !sep->stream) {
+ err = AVDTP_BAD_ACP_SEID;
+ goto failed;
+ }
+
+ stream = sep->stream;
+
+ if (sep->state != AVDTP_STATE_STREAMING) {
+ err = AVDTP_BAD_STATE;
+ goto failed;
+ }
+
+ if (sep->ind && sep->ind->suspend) {
+ if (!sep->ind->suspend(session, sep, stream, &err,
+ sep->user_data))
+ goto failed;
+ }
+
+ avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN);
+ }
+
+ return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+ AVDTP_SUSPEND, NULL, 0);
+
+failed:
+ memset(&rej, 0, sizeof(rej));
+ rej.acp_seid = failed_seid;
+ rej.error = err;
+ return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+ AVDTP_SUSPEND, &rej, sizeof(rej));
+}
+
+static gboolean avdtp_abort_cmd(struct avdtp *session, uint8_t transaction,
+ struct seid_req *req, unsigned int size)
+{
+ struct avdtp_local_sep *sep;
+ uint8_t err;
+ gboolean ret;
+
+ if (size < sizeof(struct seid_req)) {
+ error("Too short abort request");
+ return FALSE;
+ }
+
+ sep = find_local_sep_by_seid(session->server, req->acp_seid);
+ if (!sep || !sep->stream) {
+ err = AVDTP_BAD_ACP_SEID;
+ goto failed;
+ }
+
+ if (sep->ind && sep->ind->abort) {
+ if (!sep->ind->abort(session, sep, sep->stream, &err,
+ sep->user_data))
+ goto failed;
+ }
+
+ ret = avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+ AVDTP_ABORT, NULL, 0);
+ if (ret)
+ avdtp_sep_set_state(session, sep, AVDTP_STATE_ABORTING);
+
+ return ret;
+
+failed:
+ return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+ AVDTP_ABORT, &err, sizeof(err));
+}
+
+static gboolean avdtp_secctl_cmd(struct avdtp *session, uint8_t transaction,
+ struct seid_req *req, int size)
+{
+ return avdtp_unknown_cmd(session, transaction, AVDTP_SECURITY_CONTROL);
+}
+
+static gboolean avdtp_delayreport_cmd(struct avdtp *session,
+ uint8_t transaction,
+ struct delay_req *req,
+ unsigned int size)
+{
+ struct avdtp_local_sep *sep;
+ struct avdtp_stream *stream;
+ uint8_t err;
+
+ if (size < sizeof(struct delay_req)) {
+ error("Too short delay report request");
+ return FALSE;
+ }
+
+ sep = find_local_sep_by_seid(session->server, req->acp_seid);
+ if (!sep || !sep->stream) {
+ err = AVDTP_BAD_ACP_SEID;
+ goto failed;
+ }
+
+ stream = sep->stream;
+
+ if (sep->state != AVDTP_STATE_CONFIGURED &&
+ sep->state != AVDTP_STATE_STREAMING) {
+ err = AVDTP_BAD_STATE;
+ goto failed;
+ }
+
+ stream->delay = ntohs(req->delay);
+
+ if (sep->ind && sep->ind->delayreport) {
+ if (!sep->ind->delayreport(session, sep, stream->rseid,
+ stream->delay, &err,
+ sep->user_data))
+ goto failed;
+ }
+
+ return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+ AVDTP_DELAY_REPORT, NULL, 0);
+
+failed:
+ return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+ AVDTP_DELAY_REPORT, &err, sizeof(err));
+}
+
+static gboolean avdtp_parse_cmd(struct avdtp *session, uint8_t transaction,
+ uint8_t signal_id, void *buf, int size)
+{
+ switch (signal_id) {
+ case AVDTP_DISCOVER:
+ DBG("Received DISCOVER_CMD");
+ return avdtp_discover_cmd(session, transaction, buf, size);
+ case AVDTP_GET_CAPABILITIES:
+ DBG("Received GET_CAPABILITIES_CMD");
+ return avdtp_getcap_cmd(session, transaction, buf, size,
+ FALSE);
+ case AVDTP_GET_ALL_CAPABILITIES:
+ DBG("Received GET_ALL_CAPABILITIES_CMD");
+ return avdtp_getcap_cmd(session, transaction, buf, size, TRUE);
+ case AVDTP_SET_CONFIGURATION:
+ DBG("Received SET_CONFIGURATION_CMD");
+ return avdtp_setconf_cmd(session, transaction, buf, size);
+ case AVDTP_GET_CONFIGURATION:
+ DBG("Received GET_CONFIGURATION_CMD");
+ return avdtp_getconf_cmd(session, transaction, buf, size);
+ case AVDTP_RECONFIGURE:
+ DBG("Received RECONFIGURE_CMD");
+ return avdtp_reconf_cmd(session, transaction, buf, size);
+ case AVDTP_OPEN:
+ DBG("Received OPEN_CMD");
+ return avdtp_open_cmd(session, transaction, buf, size);
+ case AVDTP_START:
+ DBG("Received START_CMD");
+ return avdtp_start_cmd(session, transaction, buf, size);
+ case AVDTP_CLOSE:
+ DBG("Received CLOSE_CMD");
+ return avdtp_close_cmd(session, transaction, buf, size);
+ case AVDTP_SUSPEND:
+ DBG("Received SUSPEND_CMD");
+ return avdtp_suspend_cmd(session, transaction, buf, size);
+ case AVDTP_ABORT:
+ DBG("Received ABORT_CMD");
+ return avdtp_abort_cmd(session, transaction, buf, size);
+ case AVDTP_SECURITY_CONTROL:
+ DBG("Received SECURITY_CONTROL_CMD");
+ return avdtp_secctl_cmd(session, transaction, buf, size);
+ case AVDTP_DELAY_REPORT:
+ DBG("Received DELAY_REPORT_CMD");
+ return avdtp_delayreport_cmd(session, transaction, buf, size);
+ default:
+ DBG("Received unknown request id %u", signal_id);
+ return avdtp_unknown_cmd(session, transaction, signal_id);
+ }
+}
+
+enum avdtp_parse_result { PARSE_ERROR, PARSE_FRAGMENT, PARSE_SUCCESS };
+
+static enum avdtp_parse_result avdtp_parse_data(struct avdtp *session,
+ void *buf, size_t size)
+{
+ struct avdtp_common_header *header = buf;
+ struct avdtp_single_header *single = (void *) session->buf;
+ struct avdtp_start_header *start = (void *) session->buf;
+ void *payload;
+ gsize payload_size;
+
+ switch (header->packet_type) {
+ case AVDTP_PKT_TYPE_SINGLE:
+ if (size < sizeof(*single)) {
+ error("Received too small single packet (%zu bytes)", size);
+ return PARSE_ERROR;
+ }
+ if (session->in.active) {
+ error("SINGLE: Invalid AVDTP packet fragmentation");
+ return PARSE_ERROR;
+ }
+
+ payload = session->buf + sizeof(*single);
+ payload_size = size - sizeof(*single);
+
+ session->in.active = TRUE;
+ session->in.data_size = 0;
+ session->in.no_of_packets = 1;
+ session->in.transaction = header->transaction;
+ session->in.message_type = header->message_type;
+ session->in.signal_id = single->signal_id;
+
+ break;
+ case AVDTP_PKT_TYPE_START:
+ if (size < sizeof(*start)) {
+ error("Received too small start packet (%zu bytes)", size);
+ return PARSE_ERROR;
+ }
+ if (session->in.active) {
+ error("START: Invalid AVDTP packet fragmentation");
+ return PARSE_ERROR;
+ }
+
+ session->in.active = TRUE;
+ session->in.data_size = 0;
+ session->in.transaction = header->transaction;
+ session->in.message_type = header->message_type;
+ session->in.no_of_packets = start->no_of_packets;
+ session->in.signal_id = start->signal_id;
+
+ payload = session->buf + sizeof(*start);
+ payload_size = size - sizeof(*start);
+
+ break;
+ case AVDTP_PKT_TYPE_CONTINUE:
+ if (size < sizeof(struct avdtp_continue_header)) {
+ error("Received too small continue packet (%zu bytes)",
+ size);
+ return PARSE_ERROR;
+ }
+ if (!session->in.active) {
+ error("CONTINUE: Invalid AVDTP packet fragmentation");
+ return PARSE_ERROR;
+ }
+ if (session->in.transaction != header->transaction) {
+ error("Continue transaction id doesn't match");
+ return PARSE_ERROR;
+ }
+ if (session->in.no_of_packets <= 1) {
+ error("Too few continue packets");
+ return PARSE_ERROR;
+ }
+
+ payload = session->buf + sizeof(struct avdtp_continue_header);
+ payload_size = size - sizeof(struct avdtp_continue_header);
+
+ break;
+ case AVDTP_PKT_TYPE_END:
+ if (size < sizeof(struct avdtp_continue_header)) {
+ error("Received too small end packet (%zu bytes)", size);
+ return PARSE_ERROR;
+ }
+ if (!session->in.active) {
+ error("END: Invalid AVDTP packet fragmentation");
+ return PARSE_ERROR;
+ }
+ if (session->in.transaction != header->transaction) {
+ error("End transaction id doesn't match");
+ return PARSE_ERROR;
+ }
+ if (session->in.no_of_packets > 1) {
+ error("Got an end packet too early");
+ return PARSE_ERROR;
+ }
+
+ payload = session->buf + sizeof(struct avdtp_continue_header);
+ payload_size = size - sizeof(struct avdtp_continue_header);
+
+ break;
+ default:
+ error("Invalid AVDTP packet type 0x%02X", header->packet_type);
+ return PARSE_ERROR;
+ }
+
+ if (session->in.data_size + payload_size >
+ sizeof(session->in.buf)) {
+ error("Not enough incoming buffer space!");
+ return PARSE_ERROR;
+ }
+
+ memcpy(session->in.buf + session->in.data_size, payload, payload_size);
+ session->in.data_size += payload_size;
+
+ if (session->in.no_of_packets > 1) {
+ session->in.no_of_packets--;
+ DBG("Received AVDTP fragment. %d to go",
+ session->in.no_of_packets);
+ return PARSE_FRAGMENT;
+ }
+
+ session->in.active = FALSE;
+
+ return PARSE_SUCCESS;
+}
+
+static gboolean session_cb(GIOChannel *chan, GIOCondition cond,
+ gpointer data)
+{
+ struct avdtp *session = data;
+ struct avdtp_common_header *header;
+ ssize_t size;
+ int fd;
+
+ DBG("");
+
+ if (cond & G_IO_NVAL)
+ return FALSE;
+
+ header = (void *) session->buf;
+
+ if (cond & (G_IO_HUP | G_IO_ERR))
+ goto failed;
+
+ fd = g_io_channel_unix_get_fd(chan);
+ size = read(fd, session->buf, session->imtu);
+ if (size < 0) {
+ error("IO Channel read error");
+ goto failed;
+ }
+
+ if ((size_t) size < sizeof(struct avdtp_common_header)) {
+ error("Received too small packet (%zu bytes)", size);
+ goto failed;
+ }
+
+ switch (avdtp_parse_data(session, session->buf, size)) {
+ case PARSE_ERROR:
+ goto failed;
+ case PARSE_FRAGMENT:
+ return TRUE;
+ case PARSE_SUCCESS:
+ break;
+ }
+
+ if (session->in.message_type == AVDTP_MSG_TYPE_COMMAND) {
+ if (!avdtp_parse_cmd(session, session->in.transaction,
+ session->in.signal_id,
+ session->in.buf,
+ session->in.data_size)) {
+ error("Unable to handle command. Disconnecting");
+ goto failed;
+ }
+
+ if (session->ref == 1 && !session->streams && !session->req)
+ set_disconnect_timer(session);
+
+ if (session->streams && session->dc_timer)
+ remove_disconnect_timer(session);
+
+ return TRUE;
+ }
+
+ if (session->req == NULL) {
+ error("No pending request, ignoring message");
+ return TRUE;
+ }
+
+ if (header->transaction != session->req->transaction) {
+ error("Transaction label doesn't match");
+ return TRUE;
+ }
+
+ if (session->in.signal_id != session->req->signal_id) {
+ error("Reponse signal doesn't match");
+ return TRUE;
+ }
+
+ g_source_remove(session->req->timeout);
+ session->req->timeout = 0;
+
+ switch (header->message_type) {
+ case AVDTP_MSG_TYPE_ACCEPT:
+ if (!avdtp_parse_resp(session, session->req->stream,
+ session->in.transaction,
+ session->in.signal_id,
+ session->in.buf,
+ session->in.data_size)) {
+ error("Unable to parse accept response");
+ goto failed;
+ }
+ break;
+ case AVDTP_MSG_TYPE_REJECT:
+ if (!avdtp_parse_rej(session, session->req->stream,
+ session->in.transaction,
+ session->in.signal_id,
+ session->in.buf,
+ session->in.data_size)) {
+ error("Unable to parse reject response");
+ goto failed;
+ }
+ break;
+ case AVDTP_MSG_TYPE_GEN_REJECT:
+ error("Received a General Reject message");
+ break;
+ default:
+ error("Unknown message type 0x%02X", header->message_type);
+ break;
+ }
+
+ pending_req_free(session->req);
+ session->req = NULL;
+
+ process_queue(session);
+
+ return TRUE;
+
+failed:
+ connection_lost(session, EIO);
+
+ return FALSE;
+}
+
+static struct avdtp *find_session(GSList *list, const bdaddr_t *dst)
+{
+ GSList *l;
+
+ for (l = list; l != NULL; l = g_slist_next(l)) {
+ struct avdtp *s = l->data;
+
+ if (bacmp(dst, &s->dst))
+ continue;
+
+ return s;
+ }
+
+ return NULL;
+}
+
+static uint16_t get_version(struct avdtp *session)
+{
+ struct btd_adapter *adapter;
+ struct btd_device *device;
+ const sdp_record_t *rec;
+ sdp_list_t *protos;
+ sdp_data_t *proto_desc;
+ char addr[18];
+ uint16_t ver = 0x0100;
+
+ adapter = manager_find_adapter(&session->server->src);
+ if (!adapter)
+ goto done;
+
+ ba2str(&session->dst, addr);
+ device = adapter_find_device(adapter, addr);
+ if (!device)
+ goto done;
+
+ rec = btd_device_get_record(device, A2DP_SINK_UUID);
+ if (!rec)
+ rec = btd_device_get_record(device, A2DP_SOURCE_UUID);
+
+ if (!rec)
+ goto done;
+
+ if (sdp_get_access_protos(rec, &protos) < 0)
+ goto done;
+
+ proto_desc = sdp_get_proto_desc(protos, AVDTP_UUID);
+ if (proto_desc && proto_desc->dtd == SDP_UINT16)
+ ver = proto_desc->val.uint16;
+
+ sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL);
+ sdp_list_free(protos, NULL);
+
+done:
+ return ver;
+}
+
+static struct avdtp *avdtp_get_internal(const bdaddr_t *src, const bdaddr_t *dst)
+{
+ struct avdtp_server *server;
+ struct avdtp *session;
+
+ assert(src != NULL);
+ assert(dst != NULL);
+
+ server = find_server(servers, src);
+ if (server == NULL)
+ return NULL;
+
+ session = find_session(server->sessions, dst);
+ if (session) {
+ if (session->pending_auth)
+ return NULL;
+ else
+ return session;
+ }
+
+ session = g_new0(struct avdtp, 1);
+
+ session->server = server;
+ bacpy(&session->dst, dst);
+ session->ref = 1;
+ /* We don't use avdtp_set_state() here since this isn't a state change
+ * but just setting of the initial state */
+ session->state = AVDTP_SESSION_STATE_DISCONNECTED;
+ session->auto_dc = TRUE;
+
+ session->version = get_version(session);
+
+ server->sessions = g_slist_append(server->sessions, session);
+
+ return session;
+}
+
+struct avdtp *avdtp_get(bdaddr_t *src, bdaddr_t *dst)
+{
+ struct avdtp *session;
+
+ session = avdtp_get_internal(src, dst);
+
+ if (!session)
+ return NULL;
+
+ return avdtp_ref(session);
+}
+
+static void avdtp_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+ struct avdtp *session = user_data;
+ char address[18];
+ GError *gerr = NULL;
+
+ if (err) {
+ error("%s", err->message);
+ goto failed;
+ }
+
+ if (!session->io)
+ session->io = g_io_channel_ref(chan);
+
+ bt_io_get(chan, BT_IO_L2CAP, &gerr,
+ BT_IO_OPT_OMTU, &session->omtu,
+ BT_IO_OPT_IMTU, &session->imtu,
+ BT_IO_OPT_INVALID);
+ if (gerr) {
+ error("%s", gerr->message);
+ g_error_free(gerr);
+ goto failed;
+ }
+
+ ba2str(&session->dst, address);
+ DBG("AVDTP: connected %s channel to %s",
+ session->pending_open ? "transport" : "signaling",
+ address);
+
+ if (session->state == AVDTP_SESSION_STATE_CONNECTING) {
+ DBG("AVDTP imtu=%u, omtu=%u", session->imtu, session->omtu);
+
+ session->buf = g_malloc0(session->imtu);
+ avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTED);
+
+ if (session->io_id)
+ g_source_remove(session->io_id);
+
+ /* This watch should be low priority since otherwise the
+ * connect callback might be dispatched before the session
+ * callback if the kernel wakes us up at the same time for
+ * them. This could happen if a headset is very quick in
+ * sending the Start command after connecting the stream
+ * transport channel.
+ */
+ session->io_id = g_io_add_watch_full(chan,
+ G_PRIORITY_LOW,
+ G_IO_IN | G_IO_ERR | G_IO_HUP
+ | G_IO_NVAL,
+ (GIOFunc) session_cb, session,
+ NULL);
+
+ if (session->stream_setup) {
+ set_disconnect_timer(session);
+ avdtp_set_auto_disconnect(session, FALSE);
+ }
+ } else if (session->pending_open)
+ handle_transport_connect(session, chan, session->imtu,
+ session->omtu);
+ else
+ goto failed;
+
+ process_queue(session);
+
+ return;
+
+failed:
+ if (session->pending_open) {
+ struct avdtp_stream *stream = session->pending_open;
+
+ handle_transport_connect(session, NULL, 0, 0);
+
+ if (avdtp_abort(session, stream) < 0)
+ avdtp_sep_set_state(session, stream->lsep,
+ AVDTP_STATE_IDLE);
+ } else
+ connection_lost(session, EIO);
+}
+
+static void auth_cb(DBusError *derr, void *user_data)
+{
+ struct avdtp *session = user_data;
+ GError *err = NULL;
+
+ if (derr && dbus_error_is_set(derr)) {
+ error("Access denied: %s", derr->message);
+ connection_lost(session, EACCES);
+ return;
+ }
+
+ if (!bt_io_accept(session->io, avdtp_connect_cb, session, NULL,
+ &err)) {
+ error("bt_io_accept: %s", err->message);
+ connection_lost(session, EACCES);
+ g_error_free(err);
+ return;
+ }
+
+ /* This is so that avdtp_connect_cb will know to do the right thing
+ * with respect to the disconnect timer */
+ session->stream_setup = TRUE;
+}
+
+static void avdtp_confirm_cb(GIOChannel *chan, gpointer data)
+{
+ struct avdtp *session;
+ struct audio_device *dev;
+ char address[18];
+ bdaddr_t src, dst;
+ int perr;
+ GError *err = NULL;
+
+ bt_io_get(chan, BT_IO_L2CAP, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &src,
+ BT_IO_OPT_DEST_BDADDR, &dst,
+ BT_IO_OPT_DEST, address,
+ BT_IO_OPT_INVALID);
+ if (err) {
+ error("%s", err->message);
+ g_error_free(err);
+ goto drop;
+ }
+
+ DBG("AVDTP: incoming connect from %s", address);
+
+ session = avdtp_get_internal(&src, &dst);
+ if (!session)
+ goto drop;
+
+ /* This state (ie, session is already *connecting*) happens when the
+ * device initiates a connect (really a config'd L2CAP channel) even
+ * though there is a connect we initiated in progress. In sink.c &
+ * source.c, this state is referred to as XCASE connect:connect.
+ * Abort the device's channel in favor of our own.
+ */
+ if (session->state == AVDTP_SESSION_STATE_CONNECTING) {
+ DBG("connect already in progress (XCASE connect:connect)");
+ goto drop;
+ }
+
+ if (session->pending_open && session->pending_open->open_acp) {
+ if (!bt_io_accept(chan, avdtp_connect_cb, session, NULL, NULL))
+ goto drop;
+ return;
+ }
+
+ if (session->io) {
+ error("Refusing unexpected connect from %s", address);
+ goto drop;
+ }
+
+ dev = manager_get_device(&src, &dst, FALSE);
+ if (!dev) {
+ dev = manager_get_device(&src, &dst, TRUE);
+ if (!dev) {
+ error("Unable to get audio device object for %s",
+ address);
+ goto drop;
+ }
+ btd_device_add_uuid(dev->btd_dev, ADVANCED_AUDIO_UUID);
+ }
+
+ session->io = g_io_channel_ref(chan);
+ avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTING);
+
+ session->io_id = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ (GIOFunc) session_cb, session);
+
+ perr = audio_device_request_authorization(dev, ADVANCED_AUDIO_UUID,
+ auth_cb, session);
+ if (perr < 0) {
+ avdtp_unref(session);
+ goto drop;
+ }
+
+ dev->auto_connect = auto_connect;
+
+ return;
+
+drop:
+ g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+static GIOChannel *l2cap_connect(struct avdtp *session)
+{
+ GError *err = NULL;
+ GIOChannel *io;
+
+ io = bt_io_connect(BT_IO_L2CAP, avdtp_connect_cb, session,
+ NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &session->server->src,
+ BT_IO_OPT_DEST_BDADDR, &session->dst,
+ BT_IO_OPT_PSM, AVDTP_PSM,
+ BT_IO_OPT_INVALID);
+ if (!io) {
+ error("%s", err->message);
+ g_error_free(err);
+ return NULL;
+ }
+
+ return io;
+}
+
+static void queue_request(struct avdtp *session, struct pending_req *req,
+ gboolean priority)
+{
+ if (priority)
+ session->prio_queue = g_slist_append(session->prio_queue, req);
+ else
+ session->req_queue = g_slist_append(session->req_queue, req);
+}
+
+static uint8_t req_get_seid(struct pending_req *req)
+{
+ if (req->signal_id == AVDTP_DISCOVER)
+ return 0;
+
+ return ((struct seid_req *) (req->data))->acp_seid;
+}
+
+static int cancel_request(struct avdtp *session, int err)
+{
+ struct pending_req *req;
+ struct seid_req sreq;
+ struct avdtp_local_sep *lsep;
+ struct avdtp_stream *stream;
+ uint8_t seid;
+ struct avdtp_error averr;
+
+ req = session->req;
+ session->req = NULL;
+
+ avdtp_error_init(&averr, AVDTP_ERRNO, err);
+
+ seid = req_get_seid(req);
+ if (seid)
+ stream = find_stream_by_rseid(session, seid);
+ else
+ stream = NULL;
+
+ if (stream) {
+ stream->abort_int = TRUE;
+ lsep = stream->lsep;
+ } else
+ lsep = NULL;
+
+ switch (req->signal_id) {
+ case AVDTP_RECONFIGURE:
+ error("Reconfigure: %s (%d)", strerror(err), err);
+ if (lsep && lsep->cfm && lsep->cfm->reconfigure)
+ lsep->cfm->reconfigure(session, lsep, stream, &averr,
+ lsep->user_data);
+ break;
+ case AVDTP_OPEN:
+ error("Open: %s (%d)", strerror(err), err);
+ if (lsep && lsep->cfm && lsep->cfm->open)
+ lsep->cfm->open(session, lsep, stream, &averr,
+ lsep->user_data);
+ break;
+ case AVDTP_START:
+ error("Start: %s (%d)", strerror(err), err);
+ if (lsep && lsep->cfm && lsep->cfm->start)
+ lsep->cfm->start(session, lsep, stream, &averr,
+ lsep->user_data);
+ break;
+ case AVDTP_SUSPEND:
+ error("Suspend: %s (%d)", strerror(err), err);
+ if (lsep && lsep->cfm && lsep->cfm->suspend)
+ lsep->cfm->suspend(session, lsep, stream, &averr,
+ lsep->user_data);
+ break;
+ case AVDTP_CLOSE:
+ error("Close: %s (%d)", strerror(err), err);
+ if (lsep && lsep->cfm && lsep->cfm->close) {
+ lsep->cfm->close(session, lsep, stream, &averr,
+ lsep->user_data);
+ if (stream)
+ stream->close_int = FALSE;
+ }
+ break;
+ case AVDTP_SET_CONFIGURATION:
+ error("SetConfiguration: %s (%d)", strerror(err), err);
+ if (lsep && lsep->cfm && lsep->cfm->set_configuration)
+ lsep->cfm->set_configuration(session, lsep, stream,
+ &averr, lsep->user_data);
+ goto failed;
+ case AVDTP_DISCOVER:
+ error("Discover: %s (%d)", strerror(err), err);
+ goto failed;
+ case AVDTP_GET_CAPABILITIES:
+ error("GetCapabilities: %s (%d)", strerror(err), err);
+ goto failed;
+ case AVDTP_ABORT:
+ error("Abort: %s (%d)", strerror(err), err);
+ goto failed;
+ }
+
+ if (!stream)
+ goto failed;
+
+ memset(&sreq, 0, sizeof(sreq));
+ sreq.acp_seid = seid;
+
+ err = send_request(session, TRUE, stream, AVDTP_ABORT, &sreq,
+ sizeof(sreq));
+ if (err < 0) {
+ error("Unable to send abort request");
+ goto failed;
+ }
+
+ goto done;
+
+failed:
+ connection_lost(session, err);
+done:
+ pending_req_free(req);
+ return err;
+}
+
+static gboolean request_timeout(gpointer user_data)
+{
+ struct avdtp *session = user_data;
+
+ cancel_request(session, ETIMEDOUT);
+
+ return FALSE;
+}
+
+static int send_req(struct avdtp *session, gboolean priority,
+ struct pending_req *req)
+{
+ static int transaction = 0;
+ int err;
+
+ if (session->state == AVDTP_SESSION_STATE_DISCONNECTED) {
+ session->io = l2cap_connect(session);
+ if (!session->io)
+ goto failed;
+ avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTING);
+ }
+
+ if (session->state < AVDTP_SESSION_STATE_CONNECTED ||
+ session->req != NULL) {
+ queue_request(session, req, priority);
+ return 0;
+ }
+
+ req->transaction = transaction++;
+ transaction %= 16;
+
+ /* FIXME: Should we retry to send if the buffer
+ was not totally sent or in case of EINTR? */
+ if (!avdtp_send(session, req->transaction, AVDTP_MSG_TYPE_COMMAND,
+ req->signal_id, req->data, req->data_size)) {
+ err = -EIO;
+ goto failed;
+ }
+
+ session->req = req;
+
+ req->timeout = g_timeout_add_seconds(req->signal_id == AVDTP_ABORT ?
+ ABORT_TIMEOUT : REQ_TIMEOUT,
+ request_timeout,
+ session);
+ return 0;
+
+failed:
+ g_free(req->data);
+ g_free(req);
+ return err;
+}
+
+static int send_request(struct avdtp *session, gboolean priority,
+ struct avdtp_stream *stream, uint8_t signal_id,
+ void *buffer, size_t size)
+{
+ struct pending_req *req;
+
+ if (stream && stream->abort_int && signal_id != AVDTP_ABORT) {
+ DBG("Unable to send requests while aborting");
+ return -EINVAL;
+ }
+
+ req = g_new0(struct pending_req, 1);
+ req->signal_id = signal_id;
+ req->data = g_malloc(size);
+ memcpy(req->data, buffer, size);
+ req->data_size = size;
+ req->stream = stream;
+
+ return send_req(session, priority, req);
+}
+
+static gboolean avdtp_discover_resp(struct avdtp *session,
+ struct discover_resp *resp, int size)
+{
+ int sep_count, i;
+ uint8_t getcap_cmd;
+
+ if (session->version >= 0x0103 && session->server->version >= 0x0103)
+ getcap_cmd = AVDTP_GET_ALL_CAPABILITIES;
+ else
+ getcap_cmd = AVDTP_GET_CAPABILITIES;
+
+ sep_count = size / sizeof(struct seid_info);
+
+ for (i = 0; i < sep_count; i++) {
+ struct avdtp_remote_sep *sep;
+ struct avdtp_stream *stream;
+ struct seid_req req;
+ int ret;
+
+ DBG("seid %d type %d media %d in use %d",
+ resp->seps[i].seid, resp->seps[i].type,
+ resp->seps[i].media_type, resp->seps[i].inuse);
+
+ stream = find_stream_by_rseid(session, resp->seps[i].seid);
+
+ sep = find_remote_sep(session->seps, resp->seps[i].seid);
+ if (!sep) {
+ if (resp->seps[i].inuse && !stream)
+ continue;
+ sep = g_new0(struct avdtp_remote_sep, 1);
+ session->seps = g_slist_append(session->seps, sep);
+ }
+
+ sep->stream = stream;
+ sep->seid = resp->seps[i].seid;
+ sep->type = resp->seps[i].type;
+ sep->media_type = resp->seps[i].media_type;
+
+ memset(&req, 0, sizeof(req));
+ req.acp_seid = sep->seid;
+
+ ret = send_request(session, TRUE, NULL, getcap_cmd,
+ &req, sizeof(req));
+ if (ret < 0) {
+ finalize_discovery(session, -ret);
+ break;
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean avdtp_get_capabilities_resp(struct avdtp *session,
+ struct getcap_resp *resp,
+ unsigned int size)
+{
+ struct avdtp_remote_sep *sep;
+ uint8_t seid;
+
+ /* Check for minimum required packet size includes:
+ * 1. getcap resp header
+ * 2. media transport capability (2 bytes)
+ * 3. media codec capability type + length (2 bytes)
+ * 4. the actual media codec elements
+ * */
+ if (size < (sizeof(struct getcap_resp) + 4 +
+ sizeof(struct avdtp_media_codec_capability))) {
+ error("Too short getcap resp packet");
+ return FALSE;
+ }
+
+ seid = ((struct seid_req *) session->req->data)->acp_seid;
+
+ sep = find_remote_sep(session->seps, seid);
+
+ DBG("seid %d type %d media %d", sep->seid,
+ sep->type, sep->media_type);
+
+ if (sep->caps) {
+ g_slist_foreach(sep->caps, (GFunc) g_free, NULL);
+ g_slist_free(sep->caps);
+ sep->caps = NULL;
+ sep->codec = NULL;
+ sep->delay_reporting = FALSE;
+ }
+
+ sep->caps = caps_to_list(resp->caps, size - sizeof(struct getcap_resp),
+ &sep->codec, &sep->delay_reporting);
+
+ return TRUE;
+}
+
+static gboolean avdtp_set_configuration_resp(struct avdtp *session,
+ struct avdtp_stream *stream,
+ struct avdtp_single_header *resp,
+ int size)
+{
+ struct avdtp_local_sep *sep = stream->lsep;
+
+ if (sep->cfm && sep->cfm->set_configuration)
+ sep->cfm->set_configuration(session, sep, stream, NULL,
+ sep->user_data);
+
+ avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED);
+
+ return TRUE;
+}
+
+static gboolean avdtp_reconfigure_resp(struct avdtp *session,
+ struct avdtp_stream *stream,
+ struct avdtp_single_header *resp, int size)
+{
+ return TRUE;
+}
+
+static gboolean avdtp_open_resp(struct avdtp *session, struct avdtp_stream *stream,
+ struct seid_rej *resp, int size)
+{
+ struct avdtp_local_sep *sep = stream->lsep;
+
+ stream->io = l2cap_connect(session);
+ if (!stream->io) {
+ avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE);
+ return FALSE;
+ }
+
+ session->pending_open = stream;
+
+ return TRUE;
+}
+
+static gboolean avdtp_start_resp(struct avdtp *session,
+ struct avdtp_stream *stream,
+ struct seid_rej *resp, int size)
+{
+ struct avdtp_local_sep *sep = stream->lsep;
+
+ if (sep->cfm && sep->cfm->start)
+ sep->cfm->start(session, sep, stream, NULL, sep->user_data);
+
+ /* We might be in STREAMING already if both sides send START_CMD at the
+ * same time and the one in SNK role doesn't reject it as it should */
+ if (sep->state != AVDTP_STATE_STREAMING)
+ avdtp_sep_set_state(session, sep, AVDTP_STATE_STREAMING);
+
+ return TRUE;
+}
+
+static gboolean avdtp_close_resp(struct avdtp *session,
+ struct avdtp_stream *stream,
+ struct seid_rej *resp, int size)
+{
+ struct avdtp_local_sep *sep = stream->lsep;
+
+ avdtp_sep_set_state(session, sep, AVDTP_STATE_CLOSING);
+
+ close_stream(stream);
+
+ return TRUE;
+}
+
+static gboolean avdtp_suspend_resp(struct avdtp *session,
+ struct avdtp_stream *stream,
+ void *data, int size)
+{
+ struct avdtp_local_sep *sep = stream->lsep;
+
+ avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN);
+
+ if (sep->cfm && sep->cfm->suspend)
+ sep->cfm->suspend(session, sep, stream, NULL, sep->user_data);
+
+ return TRUE;
+}
+
+static gboolean avdtp_abort_resp(struct avdtp *session,
+ struct avdtp_stream *stream,
+ struct seid_rej *resp, int size)
+{
+ struct avdtp_local_sep *sep = stream->lsep;
+
+ avdtp_sep_set_state(session, sep, AVDTP_STATE_ABORTING);
+
+ if (sep->cfm && sep->cfm->abort)
+ sep->cfm->abort(session, sep, stream, NULL, sep->user_data);
+
+ avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE);
+
+ return TRUE;
+}
+
+static gboolean avdtp_delay_report_resp(struct avdtp *session,
+ struct avdtp_stream *stream,
+ void *data, int size)
+{
+ struct avdtp_local_sep *sep = stream->lsep;
+
+ if (sep->cfm && sep->cfm->delay_report)
+ sep->cfm->delay_report(session, sep, stream, NULL, sep->user_data);
+
+ return TRUE;
+}
+
+static gboolean avdtp_parse_resp(struct avdtp *session,
+ struct avdtp_stream *stream,
+ uint8_t transaction, uint8_t signal_id,
+ void *buf, int size)
+{
+ struct pending_req *next;
+ const char *get_all = "";
+
+ if (session->prio_queue)
+ next = session->prio_queue->data;
+ else if (session->req_queue)
+ next = session->req_queue->data;
+ else
+ next = NULL;
+
+ switch (signal_id) {
+ case AVDTP_DISCOVER:
+ DBG("DISCOVER request succeeded");
+ return avdtp_discover_resp(session, buf, size);
+ case AVDTP_GET_ALL_CAPABILITIES:
+ get_all = "ALL_";
+ case AVDTP_GET_CAPABILITIES:
+ DBG("GET_%sCAPABILITIES request succeeded", get_all);
+ if (!avdtp_get_capabilities_resp(session, buf, size))
+ return FALSE;
+ if (!(next && (next->signal_id == AVDTP_GET_CAPABILITIES ||
+ next->signal_id == AVDTP_GET_ALL_CAPABILITIES)))
+ finalize_discovery(session, 0);
+ return TRUE;
+ }
+
+ /* The remaining commands require an existing stream so bail out
+ * here if the stream got unexpectedly disconnected */
+ if (!stream) {
+ DBG("AVDTP: stream was closed while waiting for reply");
+ return TRUE;
+ }
+
+ switch (signal_id) {
+ case AVDTP_SET_CONFIGURATION:
+ DBG("SET_CONFIGURATION request succeeded");
+ return avdtp_set_configuration_resp(session, stream,
+ buf, size);
+ case AVDTP_RECONFIGURE:
+ DBG("RECONFIGURE request succeeded");
+ return avdtp_reconfigure_resp(session, stream, buf, size);
+ case AVDTP_OPEN:
+ DBG("OPEN request succeeded");
+ return avdtp_open_resp(session, stream, buf, size);
+ case AVDTP_SUSPEND:
+ DBG("SUSPEND request succeeded");
+ return avdtp_suspend_resp(session, stream, buf, size);
+ case AVDTP_START:
+ DBG("START request succeeded");
+ return avdtp_start_resp(session, stream, buf, size);
+ case AVDTP_CLOSE:
+ DBG("CLOSE request succeeded");
+ return avdtp_close_resp(session, stream, buf, size);
+ case AVDTP_ABORT:
+ DBG("ABORT request succeeded");
+ return avdtp_abort_resp(session, stream, buf, size);
+ case AVDTP_DELAY_REPORT:
+ DBG("DELAY_REPORT request succeeded");
+ return avdtp_delay_report_resp(session, stream, buf, size);
+ }
+
+ error("Unknown signal id in accept response: %u", signal_id);
+ return TRUE;
+}
+
+static gboolean seid_rej_to_err(struct seid_rej *rej, unsigned int size,
+ struct avdtp_error *err)
+{
+ if (size < sizeof(struct seid_rej)) {
+ error("Too small packet for seid_rej");
+ return FALSE;
+ }
+
+ avdtp_error_init(err, 0x00, rej->error);
+
+ return TRUE;
+}
+
+static gboolean conf_rej_to_err(struct conf_rej *rej, unsigned int size,
+ struct avdtp_error *err)
+{
+ if (size < sizeof(struct conf_rej)) {
+ error("Too small packet for conf_rej");
+ return FALSE;
+ }
+
+ avdtp_error_init(err, rej->category, rej->error);
+
+ return TRUE;
+}
+
+static gboolean stream_rej_to_err(struct stream_rej *rej, unsigned int size,
+ struct avdtp_error *err,
+ uint8_t *acp_seid)
+{
+ if (size < sizeof(struct stream_rej)) {
+ error("Too small packet for stream_rej");
+ return FALSE;
+ }
+
+ avdtp_error_init(err, 0x00, rej->error);
+
+ if (acp_seid)
+ *acp_seid = rej->acp_seid;
+
+ return TRUE;
+}
+
+static gboolean avdtp_parse_rej(struct avdtp *session,
+ struct avdtp_stream *stream,
+ uint8_t transaction, uint8_t signal_id,
+ void *buf, int size)
+{
+ struct avdtp_error err;
+ uint8_t acp_seid;
+ struct avdtp_local_sep *sep = stream ? stream->lsep : NULL;
+
+ switch (signal_id) {
+ case AVDTP_DISCOVER:
+ if (!seid_rej_to_err(buf, size, &err))
+ return FALSE;
+ error("DISCOVER request rejected: %s (%d)",
+ avdtp_strerror(&err), err.err.error_code);
+ return TRUE;
+ case AVDTP_GET_CAPABILITIES:
+ case AVDTP_GET_ALL_CAPABILITIES:
+ if (!seid_rej_to_err(buf, size, &err))
+ return FALSE;
+ error("GET_CAPABILITIES request rejected: %s (%d)",
+ avdtp_strerror(&err), err.err.error_code);
+ return TRUE;
+ case AVDTP_OPEN:
+ if (!seid_rej_to_err(buf, size, &err))
+ return FALSE;
+ error("OPEN request rejected: %s (%d)",
+ avdtp_strerror(&err), err.err.error_code);
+ if (sep && sep->cfm && sep->cfm->open)
+ sep->cfm->open(session, sep, stream, &err,
+ sep->user_data);
+ return TRUE;
+ case AVDTP_SET_CONFIGURATION:
+ if (!conf_rej_to_err(buf, size, &err))
+ return FALSE;
+ error("SET_CONFIGURATION request rejected: %s (%d)",
+ avdtp_strerror(&err), err.err.error_code);
+ if (sep && sep->cfm && sep->cfm->set_configuration)
+ sep->cfm->set_configuration(session, sep, stream,
+ &err, sep->user_data);
+ return TRUE;
+ case AVDTP_RECONFIGURE:
+ if (!conf_rej_to_err(buf, size, &err))
+ return FALSE;
+ error("RECONFIGURE request rejected: %s (%d)",
+ avdtp_strerror(&err), err.err.error_code);
+ if (sep && sep->cfm && sep->cfm->reconfigure)
+ sep->cfm->reconfigure(session, sep, stream, &err,
+ sep->user_data);
+ return TRUE;
+ case AVDTP_START:
+ if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+ return FALSE;
+ error("START request rejected: %s (%d)",
+ avdtp_strerror(&err), err.err.error_code);
+ if (sep && sep->cfm && sep->cfm->start)
+ sep->cfm->start(session, sep, stream, &err,
+ sep->user_data);
+ return TRUE;
+ case AVDTP_SUSPEND:
+ if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+ return FALSE;
+ error("SUSPEND request rejected: %s (%d)",
+ avdtp_strerror(&err), err.err.error_code);
+ if (sep && sep->cfm && sep->cfm->suspend)
+ sep->cfm->suspend(session, sep, stream, &err,
+ sep->user_data);
+ return TRUE;
+ case AVDTP_CLOSE:
+ if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+ return FALSE;
+ error("CLOSE request rejected: %s (%d)",
+ avdtp_strerror(&err), err.err.error_code);
+ if (sep && sep->cfm && sep->cfm->close) {
+ sep->cfm->close(session, sep, stream, &err,
+ sep->user_data);
+ stream->close_int = FALSE;
+ }
+ return TRUE;
+ case AVDTP_ABORT:
+ if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+ return FALSE;
+ error("ABORT request rejected: %s (%d)",
+ avdtp_strerror(&err), err.err.error_code);
+ if (sep && sep->cfm && sep->cfm->abort)
+ sep->cfm->abort(session, sep, stream, &err,
+ sep->user_data);
+ return FALSE;
+ case AVDTP_DELAY_REPORT:
+ if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+ return FALSE;
+ error("DELAY_REPORT request rejected: %s (%d)",
+ avdtp_strerror(&err), err.err.error_code);
+ if (sep && sep->cfm && sep->cfm->delay_report)
+ sep->cfm->delay_report(session, sep, stream, &err,
+ sep->user_data);
+ return TRUE;
+ default:
+ error("Unknown reject response signal id: %u", signal_id);
+ return TRUE;
+ }
+}
+
+gboolean avdtp_is_connected(const bdaddr_t *src, const bdaddr_t *dst)
+{
+ struct avdtp_server *server;
+ struct avdtp *session;
+
+ server = find_server(servers, src);
+ if (!server)
+ return FALSE;
+
+ session = find_session(server->sessions, dst);
+ if (!session)
+ return FALSE;
+
+ if (session->state != AVDTP_SESSION_STATE_DISCONNECTED)
+ return TRUE;
+
+ return FALSE;
+}
+
+struct avdtp_service_capability *avdtp_stream_get_codec(
+ struct avdtp_stream *stream)
+{
+ GSList *l;
+
+ for (l = stream->caps; l; l = l->next) {
+ struct avdtp_service_capability *cap = l->data;
+
+ if (cap->category == AVDTP_MEDIA_CODEC)
+ return cap;
+ }
+
+ return NULL;
+}
+
+gboolean avdtp_stream_has_capability(struct avdtp_stream *stream,
+ struct avdtp_service_capability *cap)
+{
+ GSList *l;
+ struct avdtp_service_capability *stream_cap;
+
+ for (l = stream->caps; l; l = g_slist_next(l)) {
+ stream_cap = l->data;
+
+ if (stream_cap->category != cap->category ||
+ stream_cap->length != cap->length)
+ continue;
+
+ if (memcmp(stream_cap->data, cap->data, cap->length) == 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream,
+ GSList *caps)
+{
+ GSList *l;
+
+ for (l = caps; l; l = g_slist_next(l)) {
+ struct avdtp_service_capability *cap = l->data;
+
+ if (!avdtp_stream_has_capability(stream, cap))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+struct avdtp_remote_sep *avdtp_stream_get_remote_sep(
+ struct avdtp_stream *stream)
+{
+ return avdtp_get_remote_sep(stream->session, stream->rseid);
+}
+
+gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock,
+ uint16_t *imtu, uint16_t *omtu,
+ GSList **caps)
+{
+ if (stream->io == NULL)
+ return FALSE;
+
+ if (sock)
+ *sock = g_io_channel_unix_get_fd(stream->io);
+
+ if (omtu)
+ *omtu = stream->omtu;
+
+ if (imtu)
+ *imtu = stream->imtu;
+
+ if (caps)
+ *caps = stream->caps;
+
+ return TRUE;
+}
+
+static int process_queue(struct avdtp *session)
+{
+ GSList **queue, *l;
+ struct pending_req *req;
+
+ if (session->req)
+ return 0;
+
+ if (session->prio_queue)
+ queue = &session->prio_queue;
+ else
+ queue = &session->req_queue;
+
+ if (!*queue)
+ return 0;
+
+ l = *queue;
+ req = l->data;
+
+ *queue = g_slist_remove(*queue, req);
+
+ return send_req(session, FALSE, req);
+}
+
+struct avdtp_remote_sep *avdtp_get_remote_sep(struct avdtp *session,
+ uint8_t seid)
+{
+ GSList *l;
+
+ for (l = session->seps; l; l = l->next) {
+ struct avdtp_remote_sep *sep = l->data;
+
+ if (sep->seid == seid)
+ return sep;
+ }
+
+ return NULL;
+}
+
+uint8_t avdtp_get_seid(struct avdtp_remote_sep *sep)
+{
+ return sep->seid;
+}
+
+uint8_t avdtp_get_type(struct avdtp_remote_sep *sep)
+{
+ return sep->type;
+}
+
+struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep)
+{
+ return sep->codec;
+}
+
+gboolean avdtp_get_delay_reporting(struct avdtp_remote_sep *sep)
+{
+ return sep->delay_reporting;
+}
+
+struct avdtp_stream *avdtp_get_stream(struct avdtp_remote_sep *sep)
+{
+ return sep->stream;
+}
+
+struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category,
+ void *data, int length)
+{
+ struct avdtp_service_capability *cap;
+
+ if (category < AVDTP_MEDIA_TRANSPORT || category > AVDTP_DELAY_REPORTING)
+ return NULL;
+
+ cap = g_malloc(sizeof(struct avdtp_service_capability) + length);
+ cap->category = category;
+ cap->length = length;
+ memcpy(cap->data, data, length);
+
+ return cap;
+}
+
+static gboolean process_discover(gpointer data)
+{
+ struct avdtp *session = data;
+
+ finalize_discovery(session, 0);
+
+ return FALSE;
+}
+
+int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb,
+ void *user_data)
+{
+ int err;
+
+ if (session->discov_cb)
+ return -EBUSY;
+
+ if (session->seps) {
+ session->discov_cb = cb;
+ session->user_data = user_data;
+ g_idle_add(process_discover, session);
+ return 0;
+ }
+
+ err = send_request(session, FALSE, NULL, AVDTP_DISCOVER, NULL, 0);
+ if (err == 0) {
+ session->discov_cb = cb;
+ session->user_data = user_data;
+ }
+
+ return err;
+}
+
+gboolean avdtp_stream_remove_cb(struct avdtp *session,
+ struct avdtp_stream *stream,
+ unsigned int id)
+{
+ GSList *l;
+ struct stream_callback *cb;
+
+ if (!stream)
+ return FALSE;
+
+ for (cb = NULL, l = stream->callbacks; l != NULL; l = l->next) {
+ struct stream_callback *tmp = l->data;
+ if (tmp && tmp->id == id) {
+ cb = tmp;
+ break;
+ }
+ }
+
+ if (!cb)
+ return FALSE;
+
+ stream->callbacks = g_slist_remove(stream->callbacks, cb);
+ g_free(cb);
+
+ return TRUE;
+}
+
+unsigned int avdtp_stream_add_cb(struct avdtp *session,
+ struct avdtp_stream *stream,
+ avdtp_stream_state_cb cb, void *data)
+{
+ struct stream_callback *stream_cb;
+ static unsigned int id = 0;
+
+ stream_cb = g_new(struct stream_callback, 1);
+ stream_cb->cb = cb;
+ stream_cb->user_data = data;
+ stream_cb->id = ++id;
+
+ stream->callbacks = g_slist_append(stream->callbacks, stream_cb);;
+
+ return stream_cb->id;
+}
+
+int avdtp_get_configuration(struct avdtp *session, struct avdtp_stream *stream)
+{
+ struct seid_req req;
+
+ if (session->state < AVDTP_SESSION_STATE_CONNECTED)
+ return -EINVAL;
+
+ memset(&req, 0, sizeof(req));
+ req.acp_seid = stream->rseid;
+
+ return send_request(session, FALSE, stream, AVDTP_GET_CONFIGURATION,
+ &req, sizeof(req));
+}
+
+static void copy_capabilities(gpointer data, gpointer user_data)
+{
+ struct avdtp_service_capability *src_cap = data;
+ struct avdtp_service_capability *dst_cap;
+ GSList **l = user_data;
+
+ dst_cap = avdtp_service_cap_new(src_cap->category, src_cap->data,
+ src_cap->length);
+
+ *l = g_slist_append(*l, dst_cap);
+}
+
+int avdtp_set_configuration(struct avdtp *session,
+ struct avdtp_remote_sep *rsep,
+ struct avdtp_local_sep *lsep,
+ GSList *caps,
+ struct avdtp_stream **stream)
+{
+ struct setconf_req *req;
+ struct avdtp_stream *new_stream;
+ unsigned char *ptr;
+ int err, caps_len;
+ struct avdtp_service_capability *cap;
+ GSList *l;
+
+ if (session->state != AVDTP_SESSION_STATE_CONNECTED)
+ return -ENOTCONN;
+
+ if (!(lsep && rsep))
+ return -EINVAL;
+
+ DBG("%p: int_seid=%u, acp_seid=%u", session,
+ lsep->info.seid, rsep->seid);
+
+ new_stream = g_new0(struct avdtp_stream, 1);
+ new_stream->session = session;
+ new_stream->lsep = lsep;
+ new_stream->rseid = rsep->seid;
+
+ if (rsep->delay_reporting && lsep->delay_reporting) {
+ struct avdtp_service_capability *delay_reporting;
+
+ delay_reporting = avdtp_service_cap_new(AVDTP_DELAY_REPORTING,
+ NULL, 0);
+ caps = g_slist_append(caps, delay_reporting);
+ new_stream->delay_reporting = TRUE;
+ }
+
+ g_slist_foreach(caps, copy_capabilities, &new_stream->caps);
+
+ /* Calculate total size of request */
+ for (l = caps, caps_len = 0; l != NULL; l = g_slist_next(l)) {
+ cap = l->data;
+ caps_len += cap->length + 2;
+ }
+
+ req = g_malloc0(sizeof(struct setconf_req) + caps_len);
+
+ req->int_seid = lsep->info.seid;
+ req->acp_seid = rsep->seid;
+
+ /* Copy the capabilities into the request */
+ for (l = caps, ptr = req->caps; l != NULL; l = g_slist_next(l)) {
+ cap = l->data;
+ memcpy(ptr, cap, cap->length + 2);
+ ptr += cap->length + 2;
+ }
+
+ err = send_request(session, FALSE, new_stream,
+ AVDTP_SET_CONFIGURATION, req,
+ sizeof(struct setconf_req) + caps_len);
+ if (err < 0)
+ stream_free(new_stream);
+ else {
+ lsep->info.inuse = 1;
+ lsep->stream = new_stream;
+ rsep->stream = new_stream;
+ session->streams = g_slist_append(session->streams, new_stream);
+ if (stream)
+ *stream = new_stream;
+ }
+
+ g_free(req);
+
+ return err;
+}
+
+int avdtp_reconfigure(struct avdtp *session, GSList *caps,
+ struct avdtp_stream *stream)
+{
+ struct reconf_req *req;
+ unsigned char *ptr;
+ int caps_len, err;
+ GSList *l;
+ struct avdtp_service_capability *cap;
+
+ if (!g_slist_find(session->streams, stream))
+ return -EINVAL;
+
+ if (stream->lsep->state != AVDTP_STATE_OPEN)
+ return -EINVAL;
+
+ /* Calculate total size of request */
+ for (l = caps, caps_len = 0; l != NULL; l = g_slist_next(l)) {
+ cap = l->data;
+ caps_len += cap->length + 2;
+ }
+
+ req = g_malloc0(sizeof(struct reconf_req) + caps_len);
+
+ req->acp_seid = stream->rseid;
+
+ /* Copy the capabilities into the request */
+ for (l = caps, ptr = req->caps; l != NULL; l = g_slist_next(l)) {
+ cap = l->data;
+ memcpy(ptr, cap, cap->length + 2);
+ ptr += cap->length + 2;
+ }
+
+ err = send_request(session, FALSE, stream, AVDTP_RECONFIGURE, req,
+ sizeof(*req) + caps_len);
+ g_free(req);
+
+ return err;
+}
+
+int avdtp_open(struct avdtp *session, struct avdtp_stream *stream)
+{
+ struct seid_req req;
+
+ if (!g_slist_find(session->streams, stream))
+ return -EINVAL;
+
+ if (stream->lsep->state > AVDTP_STATE_CONFIGURED)
+ return -EINVAL;
+
+ memset(&req, 0, sizeof(req));
+ req.acp_seid = stream->rseid;
+
+ return send_request(session, FALSE, stream, AVDTP_OPEN,
+ &req, sizeof(req));
+}
+
+int avdtp_start(struct avdtp *session, struct avdtp_stream *stream)
+{
+ struct start_req req;
+
+ if (!g_slist_find(session->streams, stream))
+ return -EINVAL;
+
+ if (stream->lsep->state != AVDTP_STATE_OPEN)
+ return -EINVAL;
+
+ if (stream->close_int == TRUE) {
+ error("avdtp_start: rejecting start since close is initiated");
+ return -EINVAL;
+ }
+
+ memset(&req, 0, sizeof(req));
+ req.first_seid.seid = stream->rseid;
+
+ return send_request(session, FALSE, stream, AVDTP_START,
+ &req, sizeof(req));
+}
+
+int avdtp_close(struct avdtp *session, struct avdtp_stream *stream,
+ gboolean immediate)
+{
+ struct seid_req req;
+ int ret;
+
+ if (!g_slist_find(session->streams, stream))
+ return -EINVAL;
+
+ if (stream->lsep->state < AVDTP_STATE_OPEN)
+ return -EINVAL;
+
+ if (stream->close_int == TRUE) {
+ error("avdtp_close: rejecting since close is already initiated");
+ return -EINVAL;
+ }
+
+ if (immediate && session->req && stream == session->req->stream)
+ return avdtp_abort(session, stream);
+
+ memset(&req, 0, sizeof(req));
+ req.acp_seid = stream->rseid;
+
+ ret = send_request(session, FALSE, stream, AVDTP_CLOSE,
+ &req, sizeof(req));
+ if (ret == 0)
+ stream->close_int = TRUE;
+
+ return ret;
+}
+
+int avdtp_suspend(struct avdtp *session, struct avdtp_stream *stream)
+{
+ struct seid_req req;
+
+ if (!g_slist_find(session->streams, stream))
+ return -EINVAL;
+
+ if (stream->lsep->state <= AVDTP_STATE_OPEN || stream->close_int)
+ return -EINVAL;
+
+ memset(&req, 0, sizeof(req));
+ req.acp_seid = stream->rseid;
+
+ return send_request(session, FALSE, stream, AVDTP_SUSPEND,
+ &req, sizeof(req));
+}
+
+int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream)
+{
+ struct seid_req req;
+ int ret;
+
+ if (!g_slist_find(session->streams, stream))
+ return -EINVAL;
+
+ if (stream->lsep->state == AVDTP_STATE_ABORTING)
+ return -EINVAL;
+
+ if (session->req && stream == session->req->stream)
+ return cancel_request(session, ECANCELED);
+
+ memset(&req, 0, sizeof(req));
+ req.acp_seid = stream->rseid;
+
+ ret = send_request(session, TRUE, stream, AVDTP_ABORT,
+ &req, sizeof(req));
+ if (ret == 0)
+ stream->abort_int = TRUE;
+
+ return ret;
+}
+
+int avdtp_delay_report(struct avdtp *session, struct avdtp_stream *stream,
+ uint16_t delay)
+{
+ struct delay_req req;
+
+ if (!g_slist_find(session->streams, stream))
+ return -EINVAL;
+
+ if (stream->lsep->state != AVDTP_STATE_CONFIGURED &&
+ stream->lsep->state != AVDTP_STATE_STREAMING)
+ return -EINVAL;
+
+ if (!stream->delay_reporting || session->version < 0x0103 ||
+ session->server->version < 0x0103)
+ return -EINVAL;
+
+ stream->delay = delay;
+
+ memset(&req, 0, sizeof(req));
+ req.acp_seid = stream->rseid;
+ req.delay = htons(delay);
+
+ return send_request(session, TRUE, stream, AVDTP_DELAY_REPORT,
+ &req, sizeof(req));
+}
+
+struct avdtp_local_sep *avdtp_register_sep(const bdaddr_t *src, uint8_t type,
+ uint8_t media_type,
+ uint8_t codec_type,
+ gboolean delay_reporting,
+ struct avdtp_sep_ind *ind,
+ struct avdtp_sep_cfm *cfm,
+ void *user_data)
+{
+ struct avdtp_server *server;
+ struct avdtp_local_sep *sep;
+
+ server = find_server(servers, src);
+ if (!server)
+ return NULL;
+
+ if (g_slist_length(server->seps) > MAX_SEID)
+ return NULL;
+
+ sep = g_new0(struct avdtp_local_sep, 1);
+
+ sep->state = AVDTP_STATE_IDLE;
+ sep->info.seid = g_slist_length(server->seps) + 1;
+ sep->info.type = type;
+ sep->info.media_type = media_type;
+ sep->codec = codec_type;
+ sep->ind = ind;
+ sep->cfm = cfm;
+ sep->user_data = user_data;
+ sep->server = server;
+ sep->delay_reporting = TRUE;
+
+ DBG("SEP %p registered: type:%d codec:%d seid:%d", sep,
+ sep->info.type, sep->codec, sep->info.seid);
+ server->seps = g_slist_append(server->seps, sep);
+
+ return sep;
+}
+
+int avdtp_unregister_sep(struct avdtp_local_sep *sep)
+{
+ struct avdtp_server *server;
+
+ if (!sep)
+ return -EINVAL;
+
+ server = sep->server;
+ server->seps = g_slist_remove(server->seps, sep);
+
+ if (sep->stream)
+ release_stream(sep->stream, sep->stream->session);
+
+ DBG("SEP %p unregistered: type:%d codec:%d seid:%d", sep,
+ sep->info.type, sep->codec, sep->info.seid);
+
+ g_free(sep);
+
+ return 0;
+}
+
+static GIOChannel *avdtp_server_socket(const bdaddr_t *src, gboolean master)
+{
+ GError *err = NULL;
+ GIOChannel *io;
+
+ io = bt_io_listen(BT_IO_L2CAP, NULL, avdtp_confirm_cb,
+ NULL, NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, src,
+ BT_IO_OPT_PSM, AVDTP_PSM,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+ BT_IO_OPT_MASTER, master,
+ BT_IO_OPT_INVALID);
+ if (!io) {
+ error("%s", err->message);
+ g_error_free(err);
+ }
+
+ return io;
+}
+
+const char *avdtp_strerror(struct avdtp_error *err)
+{
+ if (err->category == AVDTP_ERRNO)
+ return strerror(err->err.posix_errno);
+
+ switch(err->err.error_code) {
+ case AVDTP_BAD_HEADER_FORMAT:
+ return "Bad Header Format";
+ case AVDTP_BAD_LENGTH:
+ return "Bad Packet Lenght";
+ case AVDTP_BAD_ACP_SEID:
+ return "Bad Acceptor SEID";
+ case AVDTP_SEP_IN_USE:
+ return "Stream End Point in Use";
+ case AVDTP_SEP_NOT_IN_USE:
+ return "Stream End Point Not in Use";
+ case AVDTP_BAD_SERV_CATEGORY:
+ return "Bad Service Category";
+ case AVDTP_BAD_PAYLOAD_FORMAT:
+ return "Bad Payload format";
+ case AVDTP_NOT_SUPPORTED_COMMAND:
+ return "Command Not Supported";
+ case AVDTP_INVALID_CAPABILITIES:
+ return "Invalid Capabilities";
+ case AVDTP_BAD_RECOVERY_TYPE:
+ return "Bad Recovery Type";
+ case AVDTP_BAD_MEDIA_TRANSPORT_FORMAT:
+ return "Bad Media Transport Format";
+ case AVDTP_BAD_RECOVERY_FORMAT:
+ return "Bad Recovery Format";
+ case AVDTP_BAD_ROHC_FORMAT:
+ return "Bad Header Compression Format";
+ case AVDTP_BAD_CP_FORMAT:
+ return "Bad Content Protetion Format";
+ case AVDTP_BAD_MULTIPLEXING_FORMAT:
+ return "Bad Multiplexing Format";
+ case AVDTP_UNSUPPORTED_CONFIGURATION:
+ return "Configuration not supported";
+ case AVDTP_BAD_STATE:
+ return "Bad State";
+ default:
+ return "Unknow error";
+ }
+}
+
+avdtp_state_t avdtp_sep_get_state(struct avdtp_local_sep *sep)
+{
+ return sep->state;
+}
+
+void avdtp_get_peers(struct avdtp *session, bdaddr_t *src, bdaddr_t *dst)
+{
+ if (src)
+ bacpy(src, &session->server->src);
+ if (dst)
+ bacpy(dst, &session->dst);
+}
+
+int avdtp_init(const bdaddr_t *src, GKeyFile *config, uint16_t *version)
+{
+ GError *err = NULL;
+ gboolean tmp, master = TRUE;
+ struct avdtp_server *server;
+ uint16_t ver = 0x0102;
+
+ if (!config)
+ goto proceed;
+
+ tmp = g_key_file_get_boolean(config, "General",
+ "Master", &err);
+ if (err) {
+ DBG("audio.conf: %s", err->message);
+ g_clear_error(&err);
+ } else
+ master = tmp;
+
+ tmp = g_key_file_get_boolean(config, "General", "AutoConnect",
+ &err);
+ if (err)
+ g_clear_error(&err);
+ else
+ auto_connect = tmp;
+
+ if (g_key_file_get_boolean(config, "A2DP", "DelayReporting", NULL))
+ ver = 0x0103;
+
+proceed:
+ server = g_new0(struct avdtp_server, 1);
+ if (!server)
+ return -ENOMEM;
+
+ server->version = ver;
+
+ if (version)
+ *version = server->version;
+
+ server->io = avdtp_server_socket(src, master);
+ if (!server->io) {
+ g_free(server);
+ return -1;
+ }
+
+ bacpy(&server->src, src);
+
+ servers = g_slist_append(servers, server);
+
+ return 0;
+}
+
+void avdtp_exit(const bdaddr_t *src)
+{
+ struct avdtp_server *server;
+ GSList *l;
+
+ server = find_server(servers, src);
+ if (!server)
+ return;
+
+ for (l = server->sessions; l; l = l->next) {
+ struct avdtp *session = l->data;
+
+ connection_lost(session, -ECONNABORTED);
+ }
+
+ servers = g_slist_remove(servers, server);
+
+ g_io_channel_shutdown(server->io, TRUE, NULL);
+ g_io_channel_unref(server->io);
+ g_free(server);
+}
+
+gboolean avdtp_has_stream(struct avdtp *session, struct avdtp_stream *stream)
+{
+ return g_slist_find(session->streams, stream) ? TRUE : FALSE;
+}
+
+void avdtp_set_auto_disconnect(struct avdtp *session, gboolean auto_dc)
+{
+ session->auto_dc = auto_dc;
+}
+
+gboolean avdtp_stream_setup_active(struct avdtp *session)
+{
+ return session->stream_setup;
+}
+
+void avdtp_set_device_disconnect(struct avdtp *session, gboolean dev_dc)
+{
+ session->device_disconnect = dev_dc;
+}
+
+unsigned int avdtp_add_state_cb(avdtp_session_state_cb cb, void *user_data)
+{
+ struct avdtp_state_callback *state_cb;
+ static unsigned int id = 0;
+
+ state_cb = g_new(struct avdtp_state_callback, 1);
+ state_cb->cb = cb;
+ state_cb->user_data = user_data;
+ state_cb->id = ++id;
+
+ avdtp_callbacks = g_slist_append(avdtp_callbacks, state_cb);;
+
+ return state_cb->id;
+}
+
+gboolean avdtp_remove_state_cb(unsigned int id)
+{
+ GSList *l;
+
+ for (l = avdtp_callbacks; l != NULL; l = l->next) {
+ struct avdtp_state_callback *cb = l->data;
+ if (cb && cb->id == id) {
+ avdtp_callbacks = g_slist_remove(avdtp_callbacks, cb);
+ g_free(cb);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
diff --git a/audio/avdtp.h b/audio/avdtp.h
new file mode 100644
index 0000000..5f37dc3
--- /dev/null
+++ b/audio/avdtp.h
@@ -0,0 +1,316 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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
+ *
+ */
+
+typedef enum {
+ AVDTP_SESSION_STATE_DISCONNECTED,
+ AVDTP_SESSION_STATE_CONNECTING,
+ AVDTP_SESSION_STATE_CONNECTED
+} avdtp_session_state_t;
+
+struct avdtp;
+struct avdtp_stream;
+struct avdtp_local_sep;
+struct avdtp_remote_sep;
+struct avdtp_error {
+ uint8_t category;
+ union {
+ uint8_t error_code;
+ int posix_errno;
+ } err;
+};
+
+/* SEP capability categories */
+#define AVDTP_MEDIA_TRANSPORT 0x01
+#define AVDTP_REPORTING 0x02
+#define AVDTP_RECOVERY 0x03
+#define AVDTP_CONTENT_PROTECTION 0x04
+#define AVDTP_HEADER_COMPRESSION 0x05
+#define AVDTP_MULTIPLEXING 0x06
+#define AVDTP_MEDIA_CODEC 0x07
+#define AVDTP_DELAY_REPORTING 0x08
+#define AVDTP_ERRNO 0xff
+
+/* AVDTP error definitions */
+#define AVDTP_BAD_HEADER_FORMAT 0x01
+#define AVDTP_BAD_LENGTH 0x11
+#define AVDTP_BAD_ACP_SEID 0x12
+#define AVDTP_SEP_IN_USE 0x13
+#define AVDTP_SEP_NOT_IN_USE 0x14
+#define AVDTP_BAD_SERV_CATEGORY 0x17
+#define AVDTP_BAD_PAYLOAD_FORMAT 0x18
+#define AVDTP_NOT_SUPPORTED_COMMAND 0x19
+#define AVDTP_INVALID_CAPABILITIES 0x1A
+#define AVDTP_BAD_RECOVERY_TYPE 0x22
+#define AVDTP_BAD_MEDIA_TRANSPORT_FORMAT 0x23
+#define AVDTP_BAD_RECOVERY_FORMAT 0x25
+#define AVDTP_BAD_ROHC_FORMAT 0x26
+#define AVDTP_BAD_CP_FORMAT 0x27
+#define AVDTP_BAD_MULTIPLEXING_FORMAT 0x28
+#define AVDTP_UNSUPPORTED_CONFIGURATION 0x29
+#define AVDTP_BAD_STATE 0x31
+
+/* SEP types definitions */
+#define AVDTP_SEP_TYPE_SOURCE 0x00
+#define AVDTP_SEP_TYPE_SINK 0x01
+
+/* Media types definitions */
+#define AVDTP_MEDIA_TYPE_AUDIO 0x00
+#define AVDTP_MEDIA_TYPE_VIDEO 0x01
+#define AVDTP_MEDIA_TYPE_MULTIMEDIA 0x02
+
+typedef enum {
+ AVDTP_STATE_IDLE,
+ AVDTP_STATE_CONFIGURED,
+ AVDTP_STATE_OPEN,
+ AVDTP_STATE_STREAMING,
+ AVDTP_STATE_CLOSING,
+ AVDTP_STATE_ABORTING,
+} avdtp_state_t;
+
+struct avdtp_service_capability {
+ uint8_t category;
+ uint8_t length;
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avdtp_media_codec_capability {
+ uint8_t rfa0:4;
+ uint8_t media_type:4;
+ uint8_t media_codec_type;
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct avdtp_media_codec_capability {
+ uint8_t media_type:4;
+ uint8_t rfa0:4;
+ uint8_t media_codec_type;
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+typedef void (*avdtp_session_state_cb) (struct audio_device *dev,
+ struct avdtp *session,
+ avdtp_session_state_t old_state,
+ avdtp_session_state_t new_state,
+ void *user_data);
+
+typedef void (*avdtp_stream_state_cb) (struct avdtp_stream *stream,
+ avdtp_state_t old_state,
+ avdtp_state_t new_state,
+ struct avdtp_error *err,
+ void *user_data);
+
+typedef void (*avdtp_set_configuration_cb) (struct avdtp *session,
+ struct avdtp_stream *stream,
+ struct avdtp_error *err);
+
+/* Callbacks for when a reply is received to a command that we sent */
+struct avdtp_sep_cfm {
+ void (*set_configuration) (struct avdtp *session,
+ struct avdtp_local_sep *lsep,
+ struct avdtp_stream *stream,
+ struct avdtp_error *err,
+ void *user_data);
+ void (*get_configuration) (struct avdtp *session,
+ struct avdtp_local_sep *lsep,
+ struct avdtp_stream *stream,
+ struct avdtp_error *err,
+ void *user_data);
+ void (*open) (struct avdtp *session, struct avdtp_local_sep *lsep,
+ struct avdtp_stream *stream, struct avdtp_error *err,
+ void *user_data);
+ void (*start) (struct avdtp *session, struct avdtp_local_sep *lsep,
+ struct avdtp_stream *stream, struct avdtp_error *err,
+ void *user_data);
+ void (*suspend) (struct avdtp *session, struct avdtp_local_sep *lsep,
+ struct avdtp_stream *stream,
+ struct avdtp_error *err, void *user_data);
+ void (*close) (struct avdtp *session, struct avdtp_local_sep *lsep,
+ struct avdtp_stream *stream,
+ struct avdtp_error *err, void *user_data);
+ void (*abort) (struct avdtp *session, struct avdtp_local_sep *lsep,
+ struct avdtp_stream *stream,
+ struct avdtp_error *err, void *user_data);
+ void (*reconfigure) (struct avdtp *session,
+ struct avdtp_local_sep *lsep,
+ struct avdtp_stream *stream,
+ struct avdtp_error *err, void *user_data);
+ void (*delay_report) (struct avdtp *session, struct avdtp_local_sep *lsep,
+ struct avdtp_stream *stream,
+ struct avdtp_error *err, void *user_data);
+};
+
+/* Callbacks for indicating when we received a new command. The return value
+ * indicates whether the command should be rejected or accepted */
+struct avdtp_sep_ind {
+ gboolean (*get_capability) (struct avdtp *session,
+ struct avdtp_local_sep *sep,
+ gboolean get_all,
+ GSList **caps, uint8_t *err,
+ void *user_data);
+ gboolean (*set_configuration) (struct avdtp *session,
+ struct avdtp_local_sep *lsep,
+ struct avdtp_stream *stream,
+ GSList *caps,
+ avdtp_set_configuration_cb cb,
+ void *user_data);
+ gboolean (*get_configuration) (struct avdtp *session,
+ struct avdtp_local_sep *lsep,
+ uint8_t *err, void *user_data);
+ gboolean (*open) (struct avdtp *session, struct avdtp_local_sep *lsep,
+ struct avdtp_stream *stream, uint8_t *err,
+ void *user_data);
+ gboolean (*start) (struct avdtp *session, struct avdtp_local_sep *lsep,
+ struct avdtp_stream *stream, uint8_t *err,
+ void *user_data);
+ gboolean (*suspend) (struct avdtp *session,
+ struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream, uint8_t *err,
+ void *user_data);
+ gboolean (*close) (struct avdtp *session, struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream, uint8_t *err,
+ void *user_data);
+ gboolean (*abort) (struct avdtp *session, struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream, uint8_t *err,
+ void *user_data);
+ gboolean (*reconfigure) (struct avdtp *session,
+ struct avdtp_local_sep *lsep,
+ uint8_t *err, void *user_data);
+ gboolean (*delayreport) (struct avdtp *session,
+ struct avdtp_local_sep *lsep,
+ uint8_t rseid, uint16_t delay,
+ uint8_t *err, void *user_data);
+};
+
+typedef void (*avdtp_discover_cb_t) (struct avdtp *session, GSList *seps,
+ struct avdtp_error *err, void *user_data);
+
+struct avdtp *avdtp_get(bdaddr_t *src, bdaddr_t *dst);
+
+void avdtp_unref(struct avdtp *session);
+struct avdtp *avdtp_ref(struct avdtp *session);
+
+gboolean avdtp_is_connected(const bdaddr_t *src, const bdaddr_t *dst);
+
+struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category,
+ void *data, int size);
+
+struct avdtp_remote_sep *avdtp_get_remote_sep(struct avdtp *session,
+ uint8_t seid);
+
+uint8_t avdtp_get_seid(struct avdtp_remote_sep *sep);
+
+uint8_t avdtp_get_type(struct avdtp_remote_sep *sep);
+
+struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep);
+
+gboolean avdtp_get_delay_reporting(struct avdtp_remote_sep *sep);
+
+struct avdtp_stream *avdtp_get_stream(struct avdtp_remote_sep *sep);
+
+int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb,
+ void *user_data);
+
+gboolean avdtp_has_stream(struct avdtp *session, struct avdtp_stream *stream);
+
+unsigned int avdtp_stream_add_cb(struct avdtp *session,
+ struct avdtp_stream *stream,
+ avdtp_stream_state_cb cb, void *data);
+gboolean avdtp_stream_remove_cb(struct avdtp *session,
+ struct avdtp_stream *stream,
+ unsigned int id);
+
+gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock,
+ uint16_t *imtu, uint16_t *omtu,
+ GSList **caps);
+struct avdtp_service_capability *avdtp_stream_get_codec(
+ struct avdtp_stream *stream);
+gboolean avdtp_stream_has_capability(struct avdtp_stream *stream,
+ struct avdtp_service_capability *cap);
+gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream,
+ GSList *caps);
+struct avdtp_remote_sep *avdtp_stream_get_remote_sep(
+ struct avdtp_stream *stream);
+
+unsigned int avdtp_add_state_cb(avdtp_session_state_cb cb, void *user_data);
+
+gboolean avdtp_remove_state_cb(unsigned int id);
+
+int avdtp_set_configuration(struct avdtp *session,
+ struct avdtp_remote_sep *rsep,
+ struct avdtp_local_sep *lsep,
+ GSList *caps,
+ struct avdtp_stream **stream);
+
+int avdtp_get_configuration(struct avdtp *session,
+ struct avdtp_stream *stream);
+
+int avdtp_open(struct avdtp *session, struct avdtp_stream *stream);
+int avdtp_reconfigure(struct avdtp *session, GSList *caps,
+ struct avdtp_stream *stream);
+int avdtp_start(struct avdtp *session, struct avdtp_stream *stream);
+int avdtp_suspend(struct avdtp *session, struct avdtp_stream *stream);
+int avdtp_close(struct avdtp *session, struct avdtp_stream *stream,
+ gboolean immediate);
+int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream);
+int avdtp_delay_report(struct avdtp *session, struct avdtp_stream *stream,
+ uint16_t delay);
+
+struct avdtp_local_sep *avdtp_register_sep(const bdaddr_t *src, uint8_t type,
+ uint8_t media_type,
+ uint8_t codec_type,
+ gboolean delay_reporting,
+ struct avdtp_sep_ind *ind,
+ struct avdtp_sep_cfm *cfm,
+ void *user_data);
+
+/* Find a matching pair of local and remote SEP ID's */
+struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,
+ struct avdtp_local_sep *lsep);
+
+int avdtp_unregister_sep(struct avdtp_local_sep *sep);
+
+avdtp_state_t avdtp_sep_get_state(struct avdtp_local_sep *sep);
+
+void avdtp_error_init(struct avdtp_error *err, uint8_t type, int id);
+const char *avdtp_strerror(struct avdtp_error *err);
+uint8_t avdtp_error_category(struct avdtp_error *err);
+int avdtp_error_error_code(struct avdtp_error *err);
+int avdtp_error_posix_errno(struct avdtp_error *err);
+
+void avdtp_get_peers(struct avdtp *session, bdaddr_t *src, bdaddr_t *dst);
+
+void avdtp_set_auto_disconnect(struct avdtp *session, gboolean auto_dc);
+gboolean avdtp_stream_setup_active(struct avdtp *session);
+void avdtp_set_device_disconnect(struct avdtp *session, gboolean dev_dc);
+
+int avdtp_init(const bdaddr_t *src, GKeyFile *config, uint16_t *version);
+void avdtp_exit(const bdaddr_t *src);
diff --git a/audio/bluetooth.conf b/audio/bluetooth.conf
new file mode 100644
index 0000000..55b51e4
--- /dev/null
+++ b/audio/bluetooth.conf
@@ -0,0 +1,36 @@
+# Please note that this ALSA configuration file fragment needs be enabled in
+# /etc/asound.conf or a similar configuration file with directives similar to
+# the following:
+#
+#@hooks [
+# {
+# func load
+# files [
+# "/etc/alsa/bluetooth.conf"
+# ]
+# errors false
+# }
+#]
+
+pcm.rawbluetooth {
+ @args [ ADDRESS ]
+ @args.ADDRESS {
+ type string
+ }
+ type bluetooth
+ device $ADDRESS
+}
+
+pcm.bluetooth {
+ @args [ ADDRESS ]
+ @args.ADDRESS {
+ type string
+ }
+ type plug
+ slave {
+ pcm {
+ type bluetooth
+ device $ADDRESS
+ }
+ }
+}
diff --git a/audio/control.c b/audio/control.c
new file mode 100644
index 0000000..95c0406
--- /dev/null
+++ b/audio/control.c
@@ -0,0 +1,1193 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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 <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+#include "error.h"
+#include "uinput.h"
+#include "adapter.h"
+#include "../src/device.h"
+#include "device.h"
+#include "manager.h"
+#include "avdtp.h"
+#include "control.h"
+#include "sdpd.h"
+#include "glib-helper.h"
+#include "btio.h"
+#include "dbus-common.h"
+
+#define AVCTP_PSM 23
+
+/* Message types */
+#define AVCTP_COMMAND 0
+#define AVCTP_RESPONSE 1
+
+/* Packet types */
+#define AVCTP_PACKET_SINGLE 0
+#define AVCTP_PACKET_START 1
+#define AVCTP_PACKET_CONTINUE 2
+#define AVCTP_PACKET_END 3
+
+/* ctype entries */
+#define CTYPE_CONTROL 0x0
+#define CTYPE_STATUS 0x1
+#define CTYPE_NOT_IMPLEMENTED 0x8
+#define CTYPE_ACCEPTED 0x9
+#define CTYPE_REJECTED 0xA
+#define CTYPE_STABLE 0xC
+
+/* opcodes */
+#define OP_UNITINFO 0x30
+#define OP_SUBUNITINFO 0x31
+#define OP_PASSTHROUGH 0x7c
+
+/* subunits of interest */
+#define SUBUNIT_PANEL 0x09
+
+/* operands in passthrough commands */
+#define VOL_UP_OP 0x41
+#define VOL_DOWN_OP 0x42
+#define MUTE_OP 0x43
+#define PLAY_OP 0x44
+#define STOP_OP 0x45
+#define PAUSE_OP 0x46
+#define RECORD_OP 0x47
+#define REWIND_OP 0x48
+#define FAST_FORWARD_OP 0x49
+#define EJECT_OP 0x4a
+#define FORWARD_OP 0x4b
+#define BACKWARD_OP 0x4c
+
+#define QUIRK_NO_RELEASE 1 << 0
+
+static DBusConnection *connection = NULL;
+
+static GSList *servers = NULL;
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avctp_header {
+ uint8_t ipid:1;
+ uint8_t cr:1;
+ uint8_t packet_type:2;
+ uint8_t transaction:4;
+ uint16_t pid;
+} __attribute__ ((packed));
+#define AVCTP_HEADER_LENGTH 3
+
+struct avrcp_header {
+ uint8_t code:4;
+ uint8_t _hdr0:4;
+ uint8_t subunit_id:3;
+ uint8_t subunit_type:5;
+ uint8_t opcode;
+} __attribute__ ((packed));
+#define AVRCP_HEADER_LENGTH 3
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct avctp_header {
+ uint8_t transaction:4;
+ uint8_t packet_type:2;
+ uint8_t cr:1;
+ uint8_t ipid:1;
+ uint16_t pid;
+} __attribute__ ((packed));
+#define AVCTP_HEADER_LENGTH 3
+
+struct avrcp_header {
+ uint8_t _hdr0:4;
+ uint8_t code:4;
+ uint8_t subunit_type:5;
+ uint8_t subunit_id:3;
+ uint8_t opcode;
+} __attribute__ ((packed));
+#define AVRCP_HEADER_LENGTH 3
+
+#else
+#error "Unknown byte order"
+#endif
+
+struct avctp_state_callback {
+ avctp_state_cb cb;
+ void *user_data;
+ unsigned int id;
+};
+
+struct avctp_server {
+ bdaddr_t src;
+ GIOChannel *io;
+ uint32_t tg_record_id;
+ uint32_t ct_record_id;
+};
+
+struct control {
+ struct audio_device *dev;
+
+ avctp_state_t state;
+
+ int uinput;
+
+ GIOChannel *io;
+ guint io_id;
+
+ uint16_t mtu;
+
+ gboolean target;
+
+ uint8_t key_quirks[256];
+};
+
+static struct {
+ const char *name;
+ uint8_t avrcp;
+ uint16_t uinput;
+} key_map[] = {
+ { "PLAY", PLAY_OP, KEY_PLAYCD },
+ { "STOP", STOP_OP, KEY_STOPCD },
+ { "PAUSE", PAUSE_OP, KEY_PAUSECD },
+ { "FORWARD", FORWARD_OP, KEY_NEXTSONG },
+ { "BACKWARD", BACKWARD_OP, KEY_PREVIOUSSONG },
+ { "REWIND", REWIND_OP, KEY_REWIND },
+ { "FAST FORWARD", FAST_FORWARD_OP, KEY_FASTFORWARD },
+ { NULL }
+};
+
+static GSList *avctp_callbacks = NULL;
+
+static void auth_cb(DBusError *derr, void *user_data);
+
+static sdp_record_t *avrcp_ct_record()
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, l2cap, avctp, avrct;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t *record;
+ sdp_data_t *psm, *version, *features;
+ uint16_t lp = AVCTP_PSM;
+ uint16_t avrcp_ver = 0x0100, avctp_ver = 0x0103, feat = 0x000f;
+
+ record = sdp_record_alloc();
+ if (!record)
+ return NULL;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(record, root);
+
+ /* Service Class ID List */
+ sdp_uuid16_create(&avrct, AV_REMOTE_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &avrct);
+ sdp_set_service_classes(record, svclass_id);
+
+ /* Protocol Descriptor List */
+ sdp_uuid16_create(&l2cap, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap);
+ psm = sdp_data_alloc(SDP_UINT16, &lp);
+ proto[0] = sdp_list_append(proto[0], psm);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&avctp, AVCTP_UUID);
+ proto[1] = sdp_list_append(0, &avctp);
+ version = sdp_data_alloc(SDP_UINT16, &avctp_ver);
+ proto[1] = sdp_list_append(proto[1], version);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(record, aproto);
+
+ /* Bluetooth Profile Descriptor List */
+ sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
+ profile[0].version = avrcp_ver;
+ pfseq = sdp_list_append(0, &profile[0]);
+ sdp_set_profile_descs(record, pfseq);
+
+ features = sdp_data_alloc(SDP_UINT16, &feat);
+ sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+ sdp_set_info_attr(record, "AVRCP CT", 0, 0);
+
+ free(psm);
+ free(version);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(pfseq, 0);
+ sdp_list_free(aproto, 0);
+ sdp_list_free(root, 0);
+ sdp_list_free(svclass_id, 0);
+
+ return record;
+}
+
+static sdp_record_t *avrcp_tg_record()
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, l2cap, avctp, avrtg;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t *record;
+ sdp_data_t *psm, *version, *features;
+ uint16_t lp = AVCTP_PSM;
+ uint16_t avrcp_ver = 0x0100, avctp_ver = 0x0103, feat = 0x000f;
+
+ record = sdp_record_alloc();
+ if (!record)
+ return NULL;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(record, root);
+
+ /* Service Class ID List */
+ sdp_uuid16_create(&avrtg, AV_REMOTE_TARGET_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &avrtg);
+ sdp_set_service_classes(record, svclass_id);
+
+ /* Protocol Descriptor List */
+ sdp_uuid16_create(&l2cap, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap);
+ psm = sdp_data_alloc(SDP_UINT16, &lp);
+ proto[0] = sdp_list_append(proto[0], psm);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&avctp, AVCTP_UUID);
+ proto[1] = sdp_list_append(0, &avctp);
+ version = sdp_data_alloc(SDP_UINT16, &avctp_ver);
+ proto[1] = sdp_list_append(proto[1], version);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(record, aproto);
+
+ /* Bluetooth Profile Descriptor List */
+ sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
+ profile[0].version = avrcp_ver;
+ pfseq = sdp_list_append(0, &profile[0]);
+ sdp_set_profile_descs(record, pfseq);
+
+ features = sdp_data_alloc(SDP_UINT16, &feat);
+ sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+ sdp_set_info_attr(record, "AVRCP TG", 0, 0);
+
+ free(psm);
+ free(version);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+ sdp_list_free(pfseq, 0);
+ sdp_list_free(root, 0);
+ sdp_list_free(svclass_id, 0);
+
+ return record;
+}
+
+static int send_event(int fd, uint16_t type, uint16_t code, int32_t value)
+{
+ struct uinput_event event;
+
+ memset(&event, 0, sizeof(event));
+ event.type = type;
+ event.code = code;
+ event.value = value;
+
+ return write(fd, &event, sizeof(event));
+}
+
+static void send_key(int fd, uint16_t key, int pressed)
+{
+ if (fd < 0)
+ return;
+
+ send_event(fd, EV_KEY, key, pressed);
+ send_event(fd, EV_SYN, SYN_REPORT, 0);
+}
+
+static void handle_panel_passthrough(struct control *control,
+ const unsigned char *operands,
+ int operand_count)
+{
+ const char *status;
+ int pressed, i;
+
+ if (operand_count == 0)
+ return;
+
+ if (operands[0] & 0x80) {
+ status = "released";
+ pressed = 0;
+ } else {
+ status = "pressed";
+ pressed = 1;
+ }
+
+ for (i = 0; key_map[i].name != NULL; i++) {
+ uint8_t key_quirks;
+
+ if ((operands[0] & 0x7F) != key_map[i].avrcp)
+ continue;
+
+ DBG("AVRCP: %s %s", key_map[i].name, status);
+
+ key_quirks = control->key_quirks[key_map[i].avrcp];
+
+ if (key_quirks & QUIRK_NO_RELEASE) {
+ if (!pressed) {
+ DBG("AVRCP: Ignoring release");
+ break;
+ }
+
+ DBG("AVRCP: treating key press as press + release");
+ send_key(control->uinput, key_map[i].uinput, 1);
+ send_key(control->uinput, key_map[i].uinput, 0);
+ break;
+ }
+
+ send_key(control->uinput, key_map[i].uinput, pressed);
+ break;
+ }
+
+ if (key_map[i].name == NULL)
+ DBG("AVRCP: unknown button 0x%02X %s",
+ operands[0] & 0x7F, status);
+}
+
+static void avctp_disconnected(struct audio_device *dev)
+{
+ struct control *control = dev->control;
+
+ if (!control)
+ return;
+
+ if (control->io) {
+ g_io_channel_shutdown(control->io, TRUE, NULL);
+ g_io_channel_unref(control->io);
+ control->io = NULL;
+ }
+
+ if (control->io_id) {
+ g_source_remove(control->io_id);
+ control->io_id = 0;
+
+ if (control->state == AVCTP_STATE_CONNECTING)
+ audio_device_cancel_authorization(dev, auth_cb,
+ control);
+ }
+
+ if (control->uinput >= 0) {
+ char address[18];
+
+ ba2str(&dev->dst, address);
+ DBG("AVRCP: closing uinput for %s", address);
+
+ ioctl(control->uinput, UI_DEV_DESTROY);
+ close(control->uinput);
+ control->uinput = -1;
+ }
+}
+
+static void avctp_set_state(struct control *control, avctp_state_t new_state)
+{
+ GSList *l;
+ struct audio_device *dev = control->dev;
+ avctp_state_t old_state = control->state;
+ gboolean value;
+
+ switch (new_state) {
+ case AVCTP_STATE_DISCONNECTED:
+ DBG("AVCTP Disconnected");
+
+ avctp_disconnected(control->dev);
+
+ if (old_state != AVCTP_STATE_CONNECTED)
+ break;
+
+ value = FALSE;
+ g_dbus_emit_signal(dev->conn, dev->path,
+ AUDIO_CONTROL_INTERFACE,
+ "Disconnected", DBUS_TYPE_INVALID);
+ emit_property_changed(dev->conn, dev->path,
+ AUDIO_CONTROL_INTERFACE, "Connected",
+ DBUS_TYPE_BOOLEAN, &value);
+
+ if (!audio_device_is_active(dev, NULL))
+ audio_device_set_authorized(dev, FALSE);
+
+ break;
+ case AVCTP_STATE_CONNECTING:
+ DBG("AVCTP Connecting");
+ break;
+ case AVCTP_STATE_CONNECTED:
+ DBG("AVCTP Connected");
+ value = TRUE;
+ g_dbus_emit_signal(control->dev->conn, control->dev->path,
+ AUDIO_CONTROL_INTERFACE, "Connected",
+ DBUS_TYPE_INVALID);
+ emit_property_changed(control->dev->conn, control->dev->path,
+ AUDIO_CONTROL_INTERFACE, "Connected",
+ DBUS_TYPE_BOOLEAN, &value);
+ break;
+ default:
+ error("Invalid AVCTP state %d", new_state);
+ return;
+ }
+
+ control->state = new_state;
+
+ for (l = avctp_callbacks; l != NULL; l = l->next) {
+ struct avctp_state_callback *cb = l->data;
+ cb->cb(control->dev, old_state, new_state, cb->user_data);
+ }
+}
+
+static gboolean control_cb(GIOChannel *chan, GIOCondition cond,
+ gpointer data)
+{
+ struct control *control = data;
+ unsigned char buf[1024], *operands;
+ struct avctp_header *avctp;
+ struct avrcp_header *avrcp;
+ int ret, packet_size, operand_count, sock;
+
+ if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
+ goto failed;
+
+ sock = g_io_channel_unix_get_fd(control->io);
+
+ ret = read(sock, buf, sizeof(buf));
+ if (ret <= 0)
+ goto failed;
+
+ DBG("Got %d bytes of data for AVCTP session %p", ret, control);
+
+ if ((unsigned int) ret < sizeof(struct avctp_header)) {
+ error("Too small AVCTP packet");
+ goto failed;
+ }
+
+ packet_size = ret;
+
+ avctp = (struct avctp_header *) buf;
+
+ DBG("AVCTP transaction %u, packet type %u, C/R %u, IPID %u, "
+ "PID 0x%04X",
+ avctp->transaction, avctp->packet_type,
+ avctp->cr, avctp->ipid, ntohs(avctp->pid));
+
+ ret -= sizeof(struct avctp_header);
+ if ((unsigned int) ret < sizeof(struct avrcp_header)) {
+ error("Too small AVRCP packet");
+ goto failed;
+ }
+
+ avrcp = (struct avrcp_header *) (buf + sizeof(struct avctp_header));
+
+ ret -= sizeof(struct avrcp_header);
+
+ operands = buf + sizeof(struct avctp_header) + sizeof(struct avrcp_header);
+ operand_count = ret;
+
+ DBG("AVRCP %s 0x%01X, subunit_type 0x%02X, subunit_id 0x%01X, "
+ "opcode 0x%02X, %d operands",
+ avctp->cr ? "response" : "command",
+ avrcp->code, avrcp->subunit_type, avrcp->subunit_id,
+ avrcp->opcode, operand_count);
+
+ if (avctp->packet_type != AVCTP_PACKET_SINGLE) {
+ avctp->cr = AVCTP_RESPONSE;
+ avrcp->code = CTYPE_NOT_IMPLEMENTED;
+ } else if (avctp->pid != htons(AV_REMOTE_SVCLASS_ID)) {
+ avctp->ipid = 1;
+ avctp->cr = AVCTP_RESPONSE;
+ avrcp->code = CTYPE_REJECTED;
+ } else if (avctp->cr == AVCTP_COMMAND &&
+ avrcp->code == CTYPE_CONTROL &&
+ avrcp->subunit_type == SUBUNIT_PANEL &&
+ avrcp->opcode == OP_PASSTHROUGH) {
+ handle_panel_passthrough(control, operands, operand_count);
+ avctp->cr = AVCTP_RESPONSE;
+ avrcp->code = CTYPE_ACCEPTED;
+ } else if (avctp->cr == AVCTP_COMMAND &&
+ avrcp->code == CTYPE_STATUS &&
+ (avrcp->opcode == OP_UNITINFO
+ || avrcp->opcode == OP_SUBUNITINFO)) {
+ avctp->cr = AVCTP_RESPONSE;
+ avrcp->code = CTYPE_STABLE;
+ /* The first operand should be 0x07 for the UNITINFO response.
+ * Neither AVRCP (section 22.1, page 117) nor AVC Digital
+ * Interface Command Set (section 9.2.1, page 45) specs
+ * explain this value but both use it */
+ if (operand_count >= 1 && avrcp->opcode == OP_UNITINFO)
+ operands[0] = 0x07;
+ if (operand_count >= 2)
+ operands[1] = SUBUNIT_PANEL << 3;
+ DBG("reply to %s", avrcp->opcode == OP_UNITINFO ?
+ "OP_UNITINFO" : "OP_SUBUNITINFO");
+ } else {
+ avctp->cr = AVCTP_RESPONSE;
+ avrcp->code = CTYPE_REJECTED;
+ }
+ ret = write(sock, buf, packet_size);
+
+ return TRUE;
+
+failed:
+ DBG("AVCTP session %p got disconnected", control);
+ avctp_set_state(control, AVCTP_STATE_DISCONNECTED);
+ return FALSE;
+}
+
+static int uinput_create(char *name)
+{
+ struct uinput_dev dev;
+ int fd, err, i;
+
+ fd = open("/dev/uinput", O_RDWR);
+ if (fd < 0) {
+ fd = open("/dev/input/uinput", O_RDWR);
+ if (fd < 0) {
+ fd = open("/dev/misc/uinput", O_RDWR);
+ if (fd < 0) {
+ err = errno;
+ error("Can't open input device: %s (%d)",
+ strerror(err), err);
+ return -err;
+ }
+ }
+ }
+
+ memset(&dev, 0, sizeof(dev));
+ if (name)
+ strncpy(dev.name, name, UINPUT_MAX_NAME_SIZE - 1);
+
+ dev.id.bustype = BUS_BLUETOOTH;
+ dev.id.vendor = 0x0000;
+ dev.id.product = 0x0000;
+ dev.id.version = 0x0000;
+
+ if (write(fd, &dev, sizeof(dev)) < 0) {
+ err = errno;
+ error("Can't write device information: %s (%d)",
+ strerror(err), err);
+ close(fd);
+ errno = err;
+ return -err;
+ }
+
+ ioctl(fd, UI_SET_EVBIT, EV_KEY);
+ ioctl(fd, UI_SET_EVBIT, EV_REL);
+ ioctl(fd, UI_SET_EVBIT, EV_REP);
+ ioctl(fd, UI_SET_EVBIT, EV_SYN);
+
+ for (i = 0; key_map[i].name != NULL; i++)
+ ioctl(fd, UI_SET_KEYBIT, key_map[i].uinput);
+
+ if (ioctl(fd, UI_DEV_CREATE, NULL) < 0) {
+ err = errno;
+ error("Can't create uinput device: %s (%d)",
+ strerror(err), err);
+ close(fd);
+ errno = err;
+ return -err;
+ }
+
+ return fd;
+}
+
+static void init_uinput(struct control *control)
+{
+ struct audio_device *dev = control->dev;
+ char address[18], name[248 + 1];
+
+ device_get_name(dev->btd_dev, name, sizeof(name));
+ if (g_str_equal(name, "Nokia CK-20W")) {
+ control->key_quirks[FORWARD_OP] |= QUIRK_NO_RELEASE;
+ control->key_quirks[BACKWARD_OP] |= QUIRK_NO_RELEASE;
+ control->key_quirks[PLAY_OP] |= QUIRK_NO_RELEASE;
+ control->key_quirks[PAUSE_OP] |= QUIRK_NO_RELEASE;
+ }
+
+ ba2str(&dev->dst, address);
+
+ control->uinput = uinput_create(address);
+ if (control->uinput < 0)
+ error("AVRCP: failed to init uinput for %s", address);
+ else
+ DBG("AVRCP: uinput initialized for %s", address);
+}
+
+static void avctp_connect_cb(GIOChannel *chan, GError *err, gpointer data)
+{
+ struct control *control = data;
+ char address[18];
+ uint16_t imtu;
+ GError *gerr = NULL;
+
+ if (err) {
+ avctp_set_state(control, AVCTP_STATE_DISCONNECTED);
+ error("%s", err->message);
+ return;
+ }
+
+ bt_io_get(chan, BT_IO_L2CAP, &gerr,
+ BT_IO_OPT_DEST, &address,
+ BT_IO_OPT_IMTU, &imtu,
+ BT_IO_OPT_INVALID);
+ if (gerr) {
+ avctp_set_state(control, AVCTP_STATE_DISCONNECTED);
+ error("%s", gerr->message);
+ g_error_free(gerr);
+ return;
+ }
+
+ DBG("AVCTP: connected to %s", address);
+
+ if (!control->io)
+ control->io = g_io_channel_ref(chan);
+
+ init_uinput(control);
+
+ avctp_set_state(control, AVCTP_STATE_CONNECTED);
+ control->mtu = imtu;
+ control->io_id = g_io_add_watch(chan,
+ G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ (GIOFunc) control_cb, control);
+}
+
+static void auth_cb(DBusError *derr, void *user_data)
+{
+ struct control *control = user_data;
+ GError *err = NULL;
+
+ if (control->io_id) {
+ g_source_remove(control->io_id);
+ control->io_id = 0;
+ }
+
+ if (derr && dbus_error_is_set(derr)) {
+ error("Access denied: %s", derr->message);
+ avctp_set_state(control, AVCTP_STATE_DISCONNECTED);
+ return;
+ }
+
+ if (!bt_io_accept(control->io, avctp_connect_cb, control,
+ NULL, &err)) {
+ error("bt_io_accept: %s", err->message);
+ g_error_free(err);
+ avctp_set_state(control, AVCTP_STATE_DISCONNECTED);
+ }
+}
+
+static void avctp_confirm_cb(GIOChannel *chan, gpointer data)
+{
+ struct control *control = NULL;
+ struct audio_device *dev;
+ char address[18];
+ bdaddr_t src, dst;
+ GError *err = NULL;
+
+ bt_io_get(chan, BT_IO_L2CAP, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &src,
+ BT_IO_OPT_DEST_BDADDR, &dst,
+ BT_IO_OPT_DEST, address,
+ BT_IO_OPT_INVALID);
+ if (err) {
+ error("%s", err->message);
+ g_error_free(err);
+ g_io_channel_shutdown(chan, TRUE, NULL);
+ return;
+ }
+
+ dev = manager_get_device(&src, &dst, TRUE);
+ if (!dev) {
+ error("Unable to get audio device object for %s", address);
+ goto drop;
+ }
+
+ if (!dev->control) {
+ btd_device_add_uuid(dev->btd_dev, AVRCP_REMOTE_UUID);
+ if (!dev->control)
+ goto drop;
+ }
+
+ control = dev->control;
+
+ if (control->io) {
+ error("Refusing unexpected connect from %s", address);
+ goto drop;
+ }
+
+ avctp_set_state(control, AVCTP_STATE_CONNECTING);
+ control->io = g_io_channel_ref(chan);
+
+ if (audio_device_request_authorization(dev, AVRCP_TARGET_UUID,
+ auth_cb, dev->control) < 0)
+ goto drop;
+
+ control->io_id = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ control_cb, control);
+ return;
+
+drop:
+ if (!control || !control->io)
+ g_io_channel_shutdown(chan, TRUE, NULL);
+ if (control)
+ avctp_set_state(control, AVCTP_STATE_DISCONNECTED);
+}
+
+static GIOChannel *avctp_server_socket(const bdaddr_t *src, gboolean master)
+{
+ GError *err = NULL;
+ GIOChannel *io;
+
+ io = bt_io_listen(BT_IO_L2CAP, NULL, avctp_confirm_cb, NULL,
+ NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, src,
+ BT_IO_OPT_PSM, AVCTP_PSM,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+ BT_IO_OPT_MASTER, master,
+ BT_IO_OPT_INVALID);
+ if (!io) {
+ error("%s", err->message);
+ g_error_free(err);
+ }
+
+ return io;
+}
+
+gboolean avrcp_connect(struct audio_device *dev)
+{
+ struct control *control = dev->control;
+ GError *err = NULL;
+ GIOChannel *io;
+
+ if (control->state > AVCTP_STATE_DISCONNECTED)
+ return TRUE;
+
+ avctp_set_state(control, AVCTP_STATE_CONNECTING);
+
+ io = bt_io_connect(BT_IO_L2CAP, avctp_connect_cb, control, NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &dev->src,
+ BT_IO_OPT_DEST_BDADDR, &dev->dst,
+ BT_IO_OPT_PSM, AVCTP_PSM,
+ BT_IO_OPT_INVALID);
+ if (err) {
+ avctp_set_state(control, AVCTP_STATE_DISCONNECTED);
+ error("%s", err->message);
+ g_error_free(err);
+ return FALSE;
+ }
+
+ control->io = io;
+
+ return TRUE;
+}
+
+void avrcp_disconnect(struct audio_device *dev)
+{
+ struct control *control = dev->control;
+
+ if (!(control && control->io))
+ return;
+
+ avctp_set_state(control, AVCTP_STATE_DISCONNECTED);
+}
+
+int avrcp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config)
+{
+ sdp_record_t *record;
+ gboolean tmp, master = TRUE;
+ GError *err = NULL;
+ struct avctp_server *server;
+
+ if (config) {
+ tmp = g_key_file_get_boolean(config, "General",
+ "Master", &err);
+ if (err) {
+ DBG("audio.conf: %s", err->message);
+ g_error_free(err);
+ } else
+ master = tmp;
+ }
+
+ server = g_new0(struct avctp_server, 1);
+ if (!server)
+ return -ENOMEM;
+
+ if (!connection)
+ connection = dbus_connection_ref(conn);
+
+ record = avrcp_tg_record();
+ if (!record) {
+ error("Unable to allocate new service record");
+ g_free(server);
+ return -1;
+ }
+
+ if (add_record_to_server(src, record) < 0) {
+ error("Unable to register AVRCP target service record");
+ g_free(server);
+ sdp_record_free(record);
+ return -1;
+ }
+ server->tg_record_id = record->handle;
+
+ record = avrcp_ct_record();
+ if (!record) {
+ error("Unable to allocate new service record");
+ g_free(server);
+ return -1;
+ }
+
+ if (add_record_to_server(src, record) < 0) {
+ error("Unable to register AVRCP controller service record");
+ sdp_record_free(record);
+ g_free(server);
+ return -1;
+ }
+ server->ct_record_id = record->handle;
+
+ server->io = avctp_server_socket(src, master);
+ if (!server->io) {
+ remove_record_from_server(server->ct_record_id);
+ remove_record_from_server(server->tg_record_id);
+ g_free(server);
+ return -1;
+ }
+
+ bacpy(&server->src, src);
+
+ servers = g_slist_append(servers, server);
+
+ return 0;
+}
+
+static struct avctp_server *find_server(GSList *list, const bdaddr_t *src)
+{
+ GSList *l;
+
+ for (l = list; l; l = l->next) {
+ struct avctp_server *server = l->data;
+
+ if (bacmp(&server->src, src) == 0)
+ return server;
+ }
+
+ return NULL;
+}
+
+void avrcp_unregister(const bdaddr_t *src)
+{
+ struct avctp_server *server;
+
+ server = find_server(servers, src);
+ if (!server)
+ return;
+
+ servers = g_slist_remove(servers, server);
+
+ remove_record_from_server(server->ct_record_id);
+ remove_record_from_server(server->tg_record_id);
+
+ g_io_channel_shutdown(server->io, TRUE, NULL);
+ g_io_channel_unref(server->io);
+ g_free(server);
+
+ if (servers)
+ return;
+
+ dbus_connection_unref(connection);
+ connection = NULL;
+}
+
+static DBusMessage *control_is_connected(DBusConnection *conn,
+ DBusMessage *msg,
+ void *data)
+{
+ struct audio_device *device = data;
+ struct control *control = device->control;
+ DBusMessage *reply;
+ dbus_bool_t connected;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ connected = (control->state == AVCTP_STATE_CONNECTED);
+
+ dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &connected,
+ DBUS_TYPE_INVALID);
+
+ return reply;
+}
+
+static int avctp_send_passthrough(struct control *control, uint8_t op)
+{
+ unsigned char buf[AVCTP_HEADER_LENGTH + AVRCP_HEADER_LENGTH + 2];
+ struct avctp_header *avctp = (void *) buf;
+ struct avrcp_header *avrcp = (void *) &buf[AVCTP_HEADER_LENGTH];
+ uint8_t *operands = &buf[AVCTP_HEADER_LENGTH + AVRCP_HEADER_LENGTH];
+ int sk = g_io_channel_unix_get_fd(control->io);
+ static uint8_t transaction = 0;
+
+ memset(buf, 0, sizeof(buf));
+
+ avctp->transaction = transaction++;
+ avctp->packet_type = AVCTP_PACKET_SINGLE;
+ avctp->cr = AVCTP_COMMAND;
+ avctp->pid = htons(AV_REMOTE_SVCLASS_ID);
+
+ avrcp->code = CTYPE_CONTROL;
+ avrcp->subunit_type = SUBUNIT_PANEL;
+ avrcp->opcode = OP_PASSTHROUGH;
+
+ operands[0] = op & 0x7f;
+ operands[1] = 0;
+
+ if (write(sk, buf, sizeof(buf)) < 0)
+ return -errno;
+
+ /* Button release */
+ avctp->transaction = transaction++;
+ operands[0] |= 0x80;
+
+ if (write(sk, buf, sizeof(buf)) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static DBusMessage *volume_up(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct audio_device *device = data;
+ struct control *control = device->control;
+ DBusMessage *reply;
+ int err;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ if (control->state != AVCTP_STATE_CONNECTED)
+ return btd_error_not_connected(msg);
+
+ if (!control->target)
+ return btd_error_not_supported(msg);
+
+ err = avctp_send_passthrough(control, VOL_UP_OP);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *volume_down(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct audio_device *device = data;
+ struct control *control = device->control;
+ DBusMessage *reply;
+ int err;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ if (control->state != AVCTP_STATE_CONNECTED)
+ return btd_error_not_connected(msg);
+
+ if (!control->target)
+ return btd_error_not_supported(msg);
+
+ err = avctp_send_passthrough(control, VOL_DOWN_OP);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *control_get_properties(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct audio_device *device = data;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ gboolean value;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ /* Connected */
+ value = (device->control->state == AVCTP_STATE_CONNECTED);
+ dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &value);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static GDBusMethodTable control_methods[] = {
+ { "IsConnected", "", "b", control_is_connected,
+ G_DBUS_METHOD_FLAG_DEPRECATED },
+ { "GetProperties", "", "a{sv}",control_get_properties },
+ { "VolumeUp", "", "", volume_up },
+ { "VolumeDown", "", "", volume_down },
+ { NULL, NULL, NULL, NULL }
+};
+
+static GDBusSignalTable control_signals[] = {
+ { "Connected", "", G_DBUS_SIGNAL_FLAG_DEPRECATED},
+ { "Disconnected", "", G_DBUS_SIGNAL_FLAG_DEPRECATED},
+ { "PropertyChanged", "sv" },
+ { NULL, NULL }
+};
+
+static void path_unregister(void *data)
+{
+ struct audio_device *dev = data;
+ struct control *control = dev->control;
+
+ DBG("Unregistered interface %s on path %s",
+ AUDIO_CONTROL_INTERFACE, dev->path);
+
+ if (control->state != AVCTP_STATE_DISCONNECTED)
+ avctp_disconnected(dev);
+
+ g_free(control);
+ dev->control = NULL;
+}
+
+void control_unregister(struct audio_device *dev)
+{
+ g_dbus_unregister_interface(dev->conn, dev->path,
+ AUDIO_CONTROL_INTERFACE);
+}
+
+void control_update(struct audio_device *dev, uint16_t uuid16)
+{
+ struct control *control = dev->control;
+
+ if (uuid16 == AV_REMOTE_TARGET_SVCLASS_ID)
+ control->target = TRUE;
+}
+
+struct control *control_init(struct audio_device *dev, uint16_t uuid16)
+{
+ struct control *control;
+
+ if (!g_dbus_register_interface(dev->conn, dev->path,
+ AUDIO_CONTROL_INTERFACE,
+ control_methods, control_signals, NULL,
+ dev, path_unregister))
+ return NULL;
+
+ DBG("Registered interface %s on path %s",
+ AUDIO_CONTROL_INTERFACE, dev->path);
+
+ control = g_new0(struct control, 1);
+ control->dev = dev;
+ control->state = AVCTP_STATE_DISCONNECTED;
+ control->uinput = -1;
+
+ if (uuid16 == AV_REMOTE_TARGET_SVCLASS_ID)
+ control->target = TRUE;
+
+ return control;
+}
+
+gboolean control_is_active(struct audio_device *dev)
+{
+ struct control *control = dev->control;
+
+ if (control && control->state != AVCTP_STATE_DISCONNECTED)
+ return TRUE;
+
+ return FALSE;
+}
+
+unsigned int avctp_add_state_cb(avctp_state_cb cb, void *user_data)
+{
+ struct avctp_state_callback *state_cb;
+ static unsigned int id = 0;
+
+ state_cb = g_new(struct avctp_state_callback, 1);
+ state_cb->cb = cb;
+ state_cb->user_data = user_data;
+ state_cb->id = ++id;
+
+ avctp_callbacks = g_slist_append(avctp_callbacks, state_cb);
+
+ return state_cb->id;
+}
+
+gboolean avctp_remove_state_cb(unsigned int id)
+{
+ GSList *l;
+
+ for (l = avctp_callbacks; l != NULL; l = l->next) {
+ struct avctp_state_callback *cb = l->data;
+ if (cb && cb->id == id) {
+ avctp_callbacks = g_slist_remove(avctp_callbacks, cb);
+ g_free(cb);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
diff --git a/audio/control.h b/audio/control.h
new file mode 100644
index 0000000..49f25c2
--- /dev/null
+++ b/audio/control.h
@@ -0,0 +1,50 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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
+ *
+ */
+
+#define AUDIO_CONTROL_INTERFACE "org.bluez.Control"
+
+typedef enum {
+ AVCTP_STATE_DISCONNECTED = 0,
+ AVCTP_STATE_CONNECTING,
+ AVCTP_STATE_CONNECTED
+} avctp_state_t;
+
+typedef void (*avctp_state_cb) (struct audio_device *dev,
+ avctp_state_t old_state,
+ avctp_state_t new_state,
+ void *user_data);
+
+unsigned int avctp_add_state_cb(avctp_state_cb cb, void *user_data);
+gboolean avctp_remove_state_cb(unsigned int id);
+
+int avrcp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config);
+void avrcp_unregister(const bdaddr_t *src);
+
+gboolean avrcp_connect(struct audio_device *dev);
+void avrcp_disconnect(struct audio_device *dev);
+
+struct control *control_init(struct audio_device *dev, uint16_t uuid16);
+void control_update(struct audio_device *dev, uint16_t uuid16);
+void control_unregister(struct audio_device *dev);
+gboolean control_is_active(struct audio_device *dev);
diff --git a/audio/ctl_bluetooth.c b/audio/ctl_bluetooth.c
new file mode 100644
index 0000000..2981385
--- /dev/null
+++ b/audio/ctl_bluetooth.c
@@ -0,0 +1,384 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 <sys/socket.h>
+#include <sys/un.h>
+
+#include <alsa/asoundlib.h>
+#include <alsa/control_external.h>
+
+#include <bluetooth/bluetooth.h>
+
+#include "ipc.h"
+
+#ifdef ENABLE_DEBUG
+#define DBG(fmt, arg...) printf("DEBUG: %s: " fmt "\n" , __FUNCTION__ , ## arg)
+#else
+#define DBG(fmt, arg...)
+#endif
+
+#define BLUETOOTH_MINVOL 0
+#define BLUETOOTH_MAXVOL 15
+
+struct bluetooth_data {
+ snd_ctl_ext_t ext;
+ int sock;
+};
+
+enum {
+ BLUETOOTH_PLAYBACK,
+ BLUETOOTH_CAPTURE,
+};
+
+static const char *vol_devices[2] = {
+ [BLUETOOTH_PLAYBACK] = "Playback volume",
+ [BLUETOOTH_CAPTURE] = "Capture volume",
+};
+
+static void bluetooth_exit(struct bluetooth_data *data)
+{
+ if (data == NULL)
+ return;
+
+ if (data->sock >= 0)
+ bt_audio_service_close(data->sock);
+
+ free(data);
+}
+
+static void bluetooth_close(snd_ctl_ext_t *ext)
+{
+ struct bluetooth_data *data = ext->private_data;
+
+ DBG("ext %p", ext);
+
+ bluetooth_exit(data);
+}
+
+static int bluetooth_elem_count(snd_ctl_ext_t *ext)
+{
+ DBG("ext %p", ext);
+
+ return 2;
+}
+
+static int bluetooth_elem_list(snd_ctl_ext_t *ext,
+ unsigned int offset, snd_ctl_elem_id_t *id)
+{
+ DBG("ext %p offset %d id %p", ext, offset, id);
+
+ snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
+
+ if (offset > 1)
+ return -EINVAL;
+
+ snd_ctl_elem_id_set_name(id, vol_devices[offset]);
+
+ return 0;
+}
+
+static snd_ctl_ext_key_t bluetooth_find_elem(snd_ctl_ext_t *ext,
+ const snd_ctl_elem_id_t *id)
+{
+ const char *name = snd_ctl_elem_id_get_name(id);
+ int i;
+
+ DBG("ext %p id %p name '%s'", ext, id, name);
+
+ for (i = 0; i < 2; i++)
+ if (strcmp(name, vol_devices[i]) == 0)
+ return i;
+
+ return SND_CTL_EXT_KEY_NOT_FOUND;
+}
+
+static int bluetooth_get_attribute(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key,
+ int *type, unsigned int *acc, unsigned int *count)
+{
+ DBG("ext %p key %ld", ext, key);
+
+ *type = SND_CTL_ELEM_TYPE_INTEGER;
+ *acc = SND_CTL_EXT_ACCESS_READWRITE;
+ *count = 1;
+
+ return 0;
+}
+
+static int bluetooth_get_integer_info(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key,
+ long *imin, long *imax, long *istep)
+{
+ DBG("ext %p key %ld", ext, key);
+
+ *istep = 1;
+ *imin = BLUETOOTH_MINVOL;
+ *imax = BLUETOOTH_MAXVOL;
+
+ return 0;
+}
+
+static int bluetooth_send_ctl(struct bluetooth_data *data,
+ uint8_t mode, uint8_t key, struct bt_control_rsp *rsp)
+{
+ int ret;
+ struct bt_control_req *req = (void *) rsp;
+ bt_audio_error_t *err = (void *) rsp;
+ const char *type, *name;
+
+ memset(req, 0, BT_SUGGESTED_BUFFER_SIZE);
+ req->h.type = BT_REQUEST;
+ req->h.name = BT_CONTROL;
+ req->h.length = sizeof(*req);
+
+ req->mode = mode;
+ req->key = key;
+
+ ret = send(data->sock, req, BT_SUGGESTED_BUFFER_SIZE, MSG_NOSIGNAL);
+ if (ret <= 0) {
+ SYSERR("Unable to request new volume value to server");
+ return -errno;
+ }
+
+ ret = recv(data->sock, rsp, BT_SUGGESTED_BUFFER_SIZE, 0);
+ if (ret <= 0) {
+ SNDERR("Unable to receive new volume value from server");
+ return -errno;
+ }
+
+ if (rsp->h.type == BT_ERROR) {
+ SNDERR("BT_CONTROL failed : %s (%d)",
+ strerror(err->posix_errno),
+ err->posix_errno);
+ return -err->posix_errno;
+ }
+
+ type = bt_audio_strtype(rsp->h.type);
+ if (!type) {
+ SNDERR("Bogus message type %d "
+ "received from audio service",
+ rsp->h.type);
+ return -EINVAL;
+ }
+
+ name = bt_audio_strname(rsp->h.name);
+ if (!name) {
+ SNDERR("Bogus message name %d "
+ "received from audio service",
+ rsp->h.name);
+ return -EINVAL;
+ }
+
+ if (rsp->h.name != BT_CONTROL) {
+ SNDERR("Unexpected message %s received", type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int bluetooth_read_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key,
+ long *value)
+{
+ struct bluetooth_data *data = ext->private_data;
+ int ret;
+ char buf[BT_SUGGESTED_BUFFER_SIZE];
+ struct bt_control_rsp *rsp = (void *) buf;
+
+ DBG("ext %p key %ld", ext, key);
+
+ memset(buf, 0, sizeof(buf));
+ *value = 0;
+
+ ret = bluetooth_send_ctl(data, key, 0, rsp);
+ if (ret < 0)
+ goto done;
+
+ *value = rsp->key;
+done:
+ return ret;
+}
+
+static int bluetooth_write_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key,
+ long *value)
+{
+ struct bluetooth_data *data = ext->private_data;
+ char buf[BT_SUGGESTED_BUFFER_SIZE];
+ struct bt_control_rsp *rsp = (void *) buf;
+ long current;
+ int ret, keyvalue;
+
+ DBG("ext %p key %ld", ext, key);
+
+ ret = bluetooth_read_integer(ext, key, &current);
+ if (ret < 0)
+ return ret;
+
+ if (*value == current)
+ return 0;
+
+ while (*value != current) {
+ keyvalue = (*value > current) ? BT_CONTROL_KEY_VOL_UP :
+ BT_CONTROL_KEY_VOL_DOWN;
+
+ ret = bluetooth_send_ctl(data, key, keyvalue, rsp);
+ if (ret < 0)
+ break;
+
+ current = keyvalue;
+ }
+
+ return ret;
+}
+
+static int bluetooth_read_event(snd_ctl_ext_t *ext, snd_ctl_elem_id_t *id,
+ unsigned int *event_mask)
+{
+ struct bluetooth_data *data = ext->private_data;
+ char buf[BT_SUGGESTED_BUFFER_SIZE];
+ struct bt_control_ind *ind = (void *) buf;
+ int ret;
+ const char *type, *name;
+
+ DBG("ext %p id %p", ext, id);
+
+ memset(buf, 0, sizeof(buf));
+
+ ret = recv(data->sock, ind, BT_SUGGESTED_BUFFER_SIZE, MSG_DONTWAIT);
+ if (ret < 0) {
+ SNDERR("Failed while receiving data: %s (%d)",
+ strerror(errno), errno);
+ return -errno;
+ }
+
+ type = bt_audio_strtype(ind->h.type);
+ if (!type) {
+ SNDERR("Bogus message type %d "
+ "received from audio service",
+ ind->h.type);
+ return -EAGAIN;
+ }
+
+ name = bt_audio_strname(ind->h.name);
+ if (!name) {
+ SNDERR("Bogus message name %d "
+ "received from audio service",
+ ind->h.name);
+ return -EAGAIN;
+ }
+
+ if (ind->h.name != BT_CONTROL) {
+ SNDERR("Unexpected message %s received", name);
+ return -EAGAIN;
+ }
+
+ snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
+ snd_ctl_elem_id_set_name(id, ind->mode == BLUETOOTH_PLAYBACK ?
+ vol_devices[BLUETOOTH_PLAYBACK] :
+ vol_devices[BLUETOOTH_CAPTURE]);
+ *event_mask = SND_CTL_EVENT_MASK_VALUE;
+
+ return 1;
+}
+
+static snd_ctl_ext_callback_t bluetooth_callback = {
+ .close = bluetooth_close,
+ .elem_count = bluetooth_elem_count,
+ .elem_list = bluetooth_elem_list,
+ .find_elem = bluetooth_find_elem,
+ .get_attribute = bluetooth_get_attribute,
+ .get_integer_info = bluetooth_get_integer_info,
+ .read_integer = bluetooth_read_integer,
+ .write_integer = bluetooth_write_integer,
+ .read_event = bluetooth_read_event,
+};
+
+static int bluetooth_init(struct bluetooth_data *data)
+{
+ int sk;
+
+ if (!data)
+ return -EINVAL;
+
+ memset(data, 0, sizeof(struct bluetooth_data));
+
+ data->sock = -1;
+
+ sk = bt_audio_service_open();
+ if (sk < 0)
+ return -errno;
+
+ data->sock = sk;
+
+ return 0;
+}
+
+SND_CTL_PLUGIN_DEFINE_FUNC(bluetooth);
+
+SND_CTL_PLUGIN_DEFINE_FUNC(bluetooth)
+{
+ struct bluetooth_data *data;
+ int err;
+
+ DBG("Bluetooth Control plugin");
+
+ data = malloc(sizeof(struct bluetooth_data));
+ if (!data) {
+ err = -ENOMEM;
+ goto error;
+ }
+
+ err = bluetooth_init(data);
+ if (err < 0)
+ goto error;
+
+ data->ext.version = SND_CTL_EXT_VERSION;
+ data->ext.card_idx = -1;
+
+ strncpy(data->ext.id, "bluetooth", sizeof(data->ext.id) - 1);
+ strncpy(data->ext.driver, "Bluetooth-Audio", sizeof(data->ext.driver) - 1);
+ strncpy(data->ext.name, "Bluetooth Audio", sizeof(data->ext.name) - 1);
+ strncpy(data->ext.longname, "Bluetooth Audio", sizeof(data->ext.longname) - 1);
+ strncpy(data->ext.mixername, "Bluetooth Audio", sizeof(data->ext.mixername) - 1);
+
+ data->ext.callback = &bluetooth_callback;
+ data->ext.poll_fd = data->sock;
+ data->ext.private_data = data;
+
+ err = snd_ctl_ext_create(&data->ext, name, mode);
+ if (err < 0)
+ goto error;
+
+ *handlep = data->ext.handle;
+
+ return 0;
+
+error:
+ bluetooth_exit(data);
+
+ return err;
+}
+
+SND_CTL_PLUGIN_SYMBOL(bluetooth);
diff --git a/audio/device.c b/audio/device.c
new file mode 100644
index 0000000..e38e598
--- /dev/null
+++ b/audio/device.c
@@ -0,0 +1,862 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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 <sys/stat.h>
+#include <sys/param.h>
+#include <netinet/in.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+#include "textfile.h"
+#include "../src/adapter.h"
+#include "../src/device.h"
+
+#include "error.h"
+#include "ipc.h"
+#include "dbus-common.h"
+#include "device.h"
+#include "unix.h"
+#include "avdtp.h"
+#include "control.h"
+#include "headset.h"
+#include "gateway.h"
+#include "sink.h"
+#include "source.h"
+
+#define AUDIO_INTERFACE "org.bluez.Audio"
+
+#define CONTROL_CONNECT_TIMEOUT 2
+#define AVDTP_CONNECT_TIMEOUT 1
+#define HEADSET_CONNECT_TIMEOUT 1
+
+typedef enum {
+ AUDIO_STATE_DISCONNECTED,
+ AUDIO_STATE_CONNECTING,
+ AUDIO_STATE_CONNECTED,
+} audio_state_t;
+
+struct service_auth {
+ service_auth_cb cb;
+ void *user_data;
+};
+
+struct dev_priv {
+ audio_state_t state;
+
+ headset_state_t hs_state;
+ sink_state_t sink_state;
+ avctp_state_t avctp_state;
+ GSList *auths;
+
+ DBusMessage *conn_req;
+ DBusMessage *dc_req;
+
+ guint control_timer;
+ guint avdtp_timer;
+ guint headset_timer;
+ guint dc_id;
+
+ gboolean disconnecting;
+ gboolean authorized;
+ guint auth_idle_id;
+};
+
+static unsigned int sink_callback_id = 0;
+static unsigned int avctp_callback_id = 0;
+static unsigned int avdtp_callback_id = 0;
+static unsigned int headset_callback_id = 0;
+
+static void device_free(struct audio_device *dev)
+{
+ struct dev_priv *priv = dev->priv;
+
+ if (dev->conn)
+ dbus_connection_unref(dev->conn);
+
+ btd_device_unref(dev->btd_dev);
+
+ if (priv) {
+ if (priv->auths)
+ audio_device_cancel_authorization(dev, NULL, NULL);
+ if (priv->control_timer)
+ g_source_remove(priv->control_timer);
+ if (priv->avdtp_timer)
+ g_source_remove(priv->avdtp_timer);
+ if (priv->headset_timer)
+ g_source_remove(priv->headset_timer);
+ if (priv->dc_req)
+ dbus_message_unref(priv->dc_req);
+ if (priv->conn_req)
+ dbus_message_unref(priv->conn_req);
+ if (priv->dc_id)
+ device_remove_disconnect_watch(dev->btd_dev,
+ priv->dc_id);
+ g_free(priv);
+ }
+
+ g_free(dev->path);
+ g_free(dev);
+}
+
+static const char *state2str(audio_state_t state)
+{
+ switch (state) {
+ case AUDIO_STATE_DISCONNECTED:
+ return "disconnected";
+ case AUDIO_STATE_CONNECTING:
+ return "connecting";
+ case AUDIO_STATE_CONNECTED:
+ return "connected";
+ default:
+ error("Invalid audio state %d", state);
+ return NULL;
+ }
+}
+
+static gboolean control_connect_timeout(gpointer user_data)
+{
+ struct audio_device *dev = user_data;
+
+ dev->priv->control_timer = 0;
+
+ if (dev->control)
+ avrcp_connect(dev);
+
+ return FALSE;
+}
+
+static gboolean device_set_control_timer(struct audio_device *dev)
+{
+ struct dev_priv *priv = dev->priv;
+
+ if (!dev->control)
+ return FALSE;
+
+ if (priv->control_timer)
+ return FALSE;
+
+ priv->control_timer = g_timeout_add_seconds(CONTROL_CONNECT_TIMEOUT,
+ control_connect_timeout,
+ dev);
+
+ return TRUE;
+}
+
+static void device_remove_control_timer(struct audio_device *dev)
+{
+ if (dev->priv->control_timer)
+ g_source_remove(dev->priv->control_timer);
+ dev->priv->control_timer = 0;
+}
+
+static void device_remove_avdtp_timer(struct audio_device *dev)
+{
+ if (dev->priv->avdtp_timer)
+ g_source_remove(dev->priv->avdtp_timer);
+ dev->priv->avdtp_timer = 0;
+}
+
+static void device_remove_headset_timer(struct audio_device *dev)
+{
+ if (dev->priv->headset_timer)
+ g_source_remove(dev->priv->headset_timer);
+ dev->priv->headset_timer = 0;
+}
+
+static void disconnect_cb(struct btd_device *btd_dev, gboolean removal,
+ void *user_data)
+{
+ struct audio_device *dev = user_data;
+ struct dev_priv *priv = dev->priv;
+
+ if (priv->state == AUDIO_STATE_DISCONNECTED)
+ return;
+
+ if (priv->disconnecting)
+ return;
+
+ priv->disconnecting = TRUE;
+
+ device_remove_control_timer(dev);
+ device_remove_avdtp_timer(dev);
+ device_remove_headset_timer(dev);
+
+ if (dev->control)
+ avrcp_disconnect(dev);
+
+ if (dev->sink && priv->sink_state != SINK_STATE_DISCONNECTED)
+ sink_shutdown(dev->sink);
+ else if (priv->hs_state != HEADSET_STATE_DISCONNECTED)
+ headset_shutdown(dev);
+ else
+ priv->disconnecting = FALSE;
+}
+
+static void device_set_state(struct audio_device *dev, audio_state_t new_state)
+{
+ struct dev_priv *priv = dev->priv;
+ const char *state_str;
+ DBusMessage *reply = NULL;
+
+ state_str = state2str(new_state);
+ if (!state_str)
+ return;
+
+ if (new_state == AUDIO_STATE_DISCONNECTED) {
+ priv->authorized = FALSE;
+
+ if (priv->dc_id) {
+ device_remove_disconnect_watch(dev->btd_dev,
+ priv->dc_id);
+ priv->dc_id = 0;
+ }
+ } else if (new_state == AUDIO_STATE_CONNECTING)
+ priv->dc_id = device_add_disconnect_watch(dev->btd_dev,
+ disconnect_cb, dev, NULL);
+
+ if (dev->priv->state == new_state) {
+ DBG("state change attempted from %s to %s",
+ state_str, state_str);
+ return;
+ }
+
+ dev->priv->state = new_state;
+
+ if (new_state == AUDIO_STATE_DISCONNECTED) {
+ if (priv->dc_req) {
+ reply = dbus_message_new_method_return(priv->dc_req);
+ dbus_message_unref(priv->dc_req);
+ priv->dc_req = NULL;
+ g_dbus_send_message(dev->conn, reply);
+ }
+ priv->disconnecting = FALSE;
+ }
+
+ if (priv->conn_req && new_state != AUDIO_STATE_CONNECTING) {
+ if (new_state == AUDIO_STATE_CONNECTED)
+ reply = dbus_message_new_method_return(priv->conn_req);
+ else
+ reply = btd_error_failed(priv->conn_req,
+ "Connect Failed");
+
+ dbus_message_unref(priv->conn_req);
+ priv->conn_req = NULL;
+ g_dbus_send_message(dev->conn, reply);
+ }
+
+ emit_property_changed(dev->conn, dev->path,
+ AUDIO_INTERFACE, "State",
+ DBUS_TYPE_STRING, &state_str);
+}
+
+static gboolean avdtp_connect_timeout(gpointer user_data)
+{
+ struct audio_device *dev = user_data;
+
+ dev->priv->avdtp_timer = 0;
+
+ if (dev->sink) {
+ struct avdtp *session = avdtp_get(&dev->src, &dev->dst);
+
+ if (!session)
+ return FALSE;
+
+ sink_setup_stream(dev->sink, session);
+ avdtp_unref(session);
+ }
+
+ return FALSE;
+}
+
+static gboolean device_set_avdtp_timer(struct audio_device *dev)
+{
+ struct dev_priv *priv = dev->priv;
+
+ if (!dev->sink)
+ return FALSE;
+
+ if (priv->avdtp_timer)
+ return FALSE;
+
+ priv->avdtp_timer = g_timeout_add_seconds(AVDTP_CONNECT_TIMEOUT,
+ avdtp_connect_timeout,
+ dev);
+
+ return TRUE;
+}
+
+static gboolean headset_connect_timeout(gpointer user_data)
+{
+ struct audio_device *dev = user_data;
+ struct dev_priv *priv = dev->priv;
+
+ dev->priv->headset_timer = 0;
+
+ if (dev->headset == NULL)
+ return FALSE;
+
+ if (headset_config_stream(dev, FALSE, NULL, NULL) == 0) {
+ if (priv->state != AUDIO_STATE_CONNECTED &&
+ (priv->sink_state == SINK_STATE_CONNECTED ||
+ priv->sink_state == SINK_STATE_PLAYING))
+ device_set_state(dev, AUDIO_STATE_CONNECTED);
+ }
+
+ return FALSE;
+}
+
+static gboolean device_set_headset_timer(struct audio_device *dev)
+{
+ struct dev_priv *priv = dev->priv;
+
+ if (!dev->headset)
+ return FALSE;
+
+ if (priv->headset_timer)
+ return FALSE;
+
+ priv->headset_timer = g_timeout_add_seconds(HEADSET_CONNECT_TIMEOUT,
+ headset_connect_timeout, dev);
+
+ return TRUE;
+}
+
+static void device_avdtp_cb(struct audio_device *dev, struct avdtp *session,
+ avdtp_session_state_t old_state,
+ avdtp_session_state_t new_state,
+ void *user_data)
+{
+ if (!dev->sink || !dev->control)
+ return;
+
+ if (new_state == AVDTP_SESSION_STATE_CONNECTED) {
+ if (avdtp_stream_setup_active(session))
+ device_set_control_timer(dev);
+ else
+ avrcp_connect(dev);
+ }
+}
+
+static void device_sink_cb(struct audio_device *dev,
+ sink_state_t old_state,
+ sink_state_t new_state,
+ void *user_data)
+{
+ struct dev_priv *priv = dev->priv;
+
+ if (!dev->sink)
+ return;
+
+ priv->sink_state = new_state;
+
+ switch (new_state) {
+ case SINK_STATE_DISCONNECTED:
+ if (dev->control) {
+ device_remove_control_timer(dev);
+ avrcp_disconnect(dev);
+ }
+ if (priv->hs_state != HEADSET_STATE_DISCONNECTED &&
+ (priv->dc_req || priv->disconnecting)) {
+ headset_shutdown(dev);
+ break;
+ }
+ if (priv->hs_state == HEADSET_STATE_DISCONNECTED)
+ device_set_state(dev, AUDIO_STATE_DISCONNECTED);
+ else if (old_state == SINK_STATE_CONNECTING) {
+ switch (priv->hs_state) {
+ case HEADSET_STATE_CONNECTED:
+ case HEADSET_STATE_PLAY_IN_PROGRESS:
+ case HEADSET_STATE_PLAYING:
+ device_set_state(dev, AUDIO_STATE_CONNECTED);
+ default:
+ break;
+ }
+ }
+ break;
+ case SINK_STATE_CONNECTING:
+ device_remove_avdtp_timer(dev);
+ if (priv->hs_state == HEADSET_STATE_DISCONNECTED)
+ device_set_state(dev, AUDIO_STATE_CONNECTING);
+ break;
+ case SINK_STATE_CONNECTED:
+ if (old_state == SINK_STATE_PLAYING)
+ break;
+ if (dev->auto_connect) {
+ if (!dev->headset)
+ device_set_state(dev, AUDIO_STATE_CONNECTED);
+ else if (priv->hs_state == HEADSET_STATE_DISCONNECTED)
+ device_set_headset_timer(dev);
+ else if (priv->hs_state == HEADSET_STATE_CONNECTED ||
+ priv->hs_state == HEADSET_STATE_PLAY_IN_PROGRESS ||
+ priv->hs_state == HEADSET_STATE_PLAYING)
+ device_set_state(dev, AUDIO_STATE_CONNECTED);
+ } else if (priv->hs_state == HEADSET_STATE_DISCONNECTED ||
+ priv->hs_state == HEADSET_STATE_CONNECTING)
+ device_set_state(dev, AUDIO_STATE_CONNECTED);
+ break;
+ case SINK_STATE_PLAYING:
+ break;
+ }
+}
+
+static void device_avctp_cb(struct audio_device *dev,
+ avctp_state_t old_state,
+ avctp_state_t new_state,
+ void *user_data)
+{
+ if (!dev->control)
+ return;
+
+ dev->priv->avctp_state = new_state;
+
+ switch (new_state) {
+ case AVCTP_STATE_DISCONNECTED:
+ break;
+ case AVCTP_STATE_CONNECTING:
+ device_remove_control_timer(dev);
+ break;
+ case AVCTP_STATE_CONNECTED:
+ break;
+ }
+}
+
+static void device_headset_cb(struct audio_device *dev,
+ headset_state_t old_state,
+ headset_state_t new_state,
+ void *user_data)
+{
+ struct dev_priv *priv = dev->priv;
+
+ if (!dev->headset)
+ return;
+
+ priv->hs_state = new_state;
+
+ switch (new_state) {
+ case HEADSET_STATE_DISCONNECTED:
+ device_remove_avdtp_timer(dev);
+ if (priv->sink_state != SINK_STATE_DISCONNECTED && dev->sink &&
+ (priv->dc_req || priv->disconnecting)) {
+ sink_shutdown(dev->sink);
+ break;
+ }
+ if (priv->sink_state == SINK_STATE_DISCONNECTED)
+ device_set_state(dev, AUDIO_STATE_DISCONNECTED);
+ else if (old_state == HEADSET_STATE_CONNECTING &&
+ (priv->sink_state == SINK_STATE_CONNECTED ||
+ priv->sink_state == SINK_STATE_PLAYING))
+ device_set_state(dev, AUDIO_STATE_CONNECTED);
+ break;
+ case HEADSET_STATE_CONNECTING:
+ device_remove_headset_timer(dev);
+ if (priv->sink_state == SINK_STATE_DISCONNECTED)
+ device_set_state(dev, AUDIO_STATE_CONNECTING);
+ break;
+ case HEADSET_STATE_CONNECTED:
+ if (old_state == HEADSET_STATE_CONNECTED ||
+ old_state == HEADSET_STATE_PLAY_IN_PROGRESS ||
+ old_state == HEADSET_STATE_PLAYING)
+ break;
+ if (dev->auto_connect) {
+ if (!dev->sink)
+ device_set_state(dev, AUDIO_STATE_CONNECTED);
+ else if (priv->sink_state == SINK_STATE_DISCONNECTED)
+ device_set_avdtp_timer(dev);
+ else if (priv->sink_state == SINK_STATE_CONNECTED ||
+ priv->sink_state == SINK_STATE_PLAYING)
+ device_set_state(dev, AUDIO_STATE_CONNECTED);
+ } else if (priv->sink_state == SINK_STATE_DISCONNECTED ||
+ priv->sink_state == SINK_STATE_CONNECTING)
+ device_set_state(dev, AUDIO_STATE_CONNECTED);
+ break;
+ case HEADSET_STATE_PLAY_IN_PROGRESS:
+ break;
+ case HEADSET_STATE_PLAYING:
+ break;
+ }
+}
+
+static DBusMessage *dev_connect(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct audio_device *dev = data;
+ struct dev_priv *priv = dev->priv;
+
+ if (priv->state == AUDIO_STATE_CONNECTING)
+ return btd_error_in_progress(msg);
+ else if (priv->state == AUDIO_STATE_CONNECTED)
+ return btd_error_already_connected(msg);
+
+ dev->auto_connect = TRUE;
+
+ if (dev->headset)
+ headset_config_stream(dev, FALSE, NULL, NULL);
+
+ if (priv->state != AUDIO_STATE_CONNECTING && dev->sink) {
+ struct avdtp *session = avdtp_get(&dev->src, &dev->dst);
+
+ if (!session)
+ return btd_error_failed(msg,
+ "Failed to get AVDTP session");
+
+ sink_setup_stream(dev->sink, session);
+ avdtp_unref(session);
+ }
+
+ /* The previous calls should cause a call to the state callback to
+ * indicate AUDIO_STATE_CONNECTING */
+ if (priv->state != AUDIO_STATE_CONNECTING)
+ return btd_error_failed(msg, "Connect Failed");
+
+ priv->conn_req = dbus_message_ref(msg);
+
+ return NULL;
+}
+
+static DBusMessage *dev_disconnect(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct audio_device *dev = data;
+ struct dev_priv *priv = dev->priv;
+
+ if (priv->state == AUDIO_STATE_DISCONNECTED)
+ return btd_error_not_connected(msg);
+
+ if (priv->dc_req)
+ return dbus_message_new_method_return(msg);
+
+ priv->dc_req = dbus_message_ref(msg);
+
+ if (dev->control) {
+ device_remove_control_timer(dev);
+ avrcp_disconnect(dev);
+ }
+
+ if (dev->sink && priv->sink_state != SINK_STATE_DISCONNECTED)
+ sink_shutdown(dev->sink);
+ else if (priv->hs_state != HEADSET_STATE_DISCONNECTED)
+ headset_shutdown(dev);
+ else {
+ dbus_message_unref(priv->dc_req);
+ priv->dc_req = NULL;
+ return dbus_message_new_method_return(msg);
+ }
+
+ return NULL;
+}
+
+static DBusMessage *dev_get_properties(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct audio_device *device = data;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ const char *state;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ /* State */
+ state = state2str(device->priv->state);
+ if (state)
+ dict_append_entry(&dict, "State", DBUS_TYPE_STRING, &state);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static GDBusMethodTable dev_methods[] = {
+ { "Connect", "", "", dev_connect,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "Disconnect", "", "", dev_disconnect },
+ { "GetProperties", "", "a{sv}",dev_get_properties },
+ { NULL, NULL, NULL, NULL }
+};
+
+static GDBusSignalTable dev_signals[] = {
+ { "PropertyChanged", "sv" },
+ { NULL, NULL }
+};
+
+struct audio_device *audio_device_register(DBusConnection *conn,
+ struct btd_device *device,
+ const char *path, const bdaddr_t *src,
+ const bdaddr_t *dst)
+{
+ struct audio_device *dev;
+
+ if (!conn || !path)
+ return NULL;
+
+ dev = g_new0(struct audio_device, 1);
+
+ dev->btd_dev = btd_device_ref(device);
+ dev->path = g_strdup(path);
+ bacpy(&dev->dst, dst);
+ bacpy(&dev->src, src);
+ dev->conn = dbus_connection_ref(conn);
+ dev->priv = g_new0(struct dev_priv, 1);
+ dev->priv->state = AUDIO_STATE_DISCONNECTED;
+
+ if (!g_dbus_register_interface(dev->conn, dev->path,
+ AUDIO_INTERFACE,
+ dev_methods, dev_signals, NULL,
+ dev, NULL)) {
+ error("Unable to register %s on %s", AUDIO_INTERFACE,
+ dev->path);
+ device_free(dev);
+ return NULL;
+ }
+
+ DBG("Registered interface %s on path %s", AUDIO_INTERFACE,
+ dev->path);
+
+ if (sink_callback_id == 0)
+ sink_callback_id = sink_add_state_cb(device_sink_cb, NULL);
+
+ if (avdtp_callback_id == 0)
+ avdtp_callback_id = avdtp_add_state_cb(device_avdtp_cb, NULL);
+ if (avctp_callback_id == 0)
+ avctp_callback_id = avctp_add_state_cb(device_avctp_cb, NULL);
+
+ if (headset_callback_id == 0)
+ headset_callback_id = headset_add_state_cb(device_headset_cb,
+ NULL);
+
+ return dev;
+}
+
+gboolean audio_device_is_active(struct audio_device *dev,
+ const char *interface)
+{
+ if (!interface) {
+ if ((dev->sink || dev->source) &&
+ avdtp_is_connected(&dev->src, &dev->dst))
+ return TRUE;
+ if (dev->headset && headset_is_active(dev))
+ return TRUE;
+ } else if (!strcmp(interface, AUDIO_SINK_INTERFACE) && dev->sink &&
+ avdtp_is_connected(&dev->src, &dev->dst))
+ return TRUE;
+ else if (!strcmp(interface, AUDIO_SOURCE_INTERFACE) && dev->source &&
+ avdtp_is_connected(&dev->src, &dev->dst))
+ return TRUE;
+ else if (!strcmp(interface, AUDIO_HEADSET_INTERFACE) && dev->headset &&
+ headset_is_active(dev))
+ return TRUE;
+ else if (!strcmp(interface, AUDIO_CONTROL_INTERFACE) && dev->control &&
+ control_is_active(dev))
+ return TRUE;
+ else if (!strcmp(interface, AUDIO_GATEWAY_INTERFACE) && dev->gateway &&
+ gateway_is_connected(dev))
+ return TRUE;
+
+ return FALSE;
+}
+
+void audio_device_unregister(struct audio_device *device)
+{
+ unix_device_removed(device);
+
+ if (device->hs_preauth_id) {
+ g_source_remove(device->hs_preauth_id);
+ device->hs_preauth_id = 0;
+ }
+
+ if (device->headset)
+ headset_unregister(device);
+
+ if (device->gateway)
+ gateway_unregister(device);
+
+ if (device->sink)
+ sink_unregister(device);
+
+ if (device->source)
+ source_unregister(device);
+
+ if (device->control)
+ control_unregister(device);
+
+ g_dbus_unregister_interface(device->conn, device->path,
+ AUDIO_INTERFACE);
+
+ device_free(device);
+}
+
+static void auth_cb(DBusError *derr, void *user_data)
+{
+ struct audio_device *dev = user_data;
+ struct dev_priv *priv = dev->priv;
+
+ if (derr == NULL)
+ priv->authorized = TRUE;
+
+ while (priv->auths) {
+ struct service_auth *auth = priv->auths->data;
+
+ auth->cb(derr, auth->user_data);
+ priv->auths = g_slist_remove(priv->auths, auth);
+ g_free(auth);
+ }
+}
+
+static gboolean auth_idle_cb(gpointer user_data)
+{
+ struct audio_device *dev = user_data;
+ struct dev_priv *priv = dev->priv;
+
+ priv->auth_idle_id = 0;
+
+ auth_cb(NULL, dev);
+
+ return FALSE;
+}
+
+static gboolean audio_device_is_connected(struct audio_device *dev)
+{
+ if (dev->headset) {
+ headset_state_t state = headset_get_state(dev);
+
+ if (state == HEADSET_STATE_CONNECTED ||
+ state == HEADSET_STATE_PLAY_IN_PROGRESS ||
+ state == HEADSET_STATE_PLAYING)
+ return TRUE;
+ }
+
+ if (dev->sink) {
+ sink_state_t state = sink_get_state(dev);
+
+ if (state == SINK_STATE_CONNECTED ||
+ state == SINK_STATE_PLAYING)
+ return TRUE;
+ }
+
+ if (dev->source) {
+ source_state_t state = source_get_state(dev);
+
+ if (state == SOURCE_STATE_CONNECTED ||
+ state == SOURCE_STATE_PLAYING)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+int audio_device_request_authorization(struct audio_device *dev,
+ const char *uuid, service_auth_cb cb,
+ void *user_data)
+{
+ struct dev_priv *priv = dev->priv;
+ struct service_auth *auth;
+ int err;
+
+ auth = g_try_new0(struct service_auth, 1);
+ if (!auth)
+ return -ENOMEM;
+
+ auth->cb = cb;
+ auth->user_data = user_data;
+
+ priv->auths = g_slist_append(priv->auths, auth);
+ if (g_slist_length(priv->auths) > 1)
+ return 0;
+
+ if (priv->authorized || audio_device_is_connected(dev)) {
+ priv->auth_idle_id = g_idle_add(auth_idle_cb, dev);
+ return 0;
+ }
+
+ err = btd_request_authorization(&dev->src, &dev->dst, uuid, auth_cb,
+ dev);
+ if (err < 0) {
+ priv->auths = g_slist_remove(priv->auths, auth);
+ g_free(auth);
+ }
+
+ return err;
+}
+
+int audio_device_cancel_authorization(struct audio_device *dev,
+ authorization_cb cb, void *user_data)
+{
+ struct dev_priv *priv = dev->priv;
+ GSList *l, *next;
+
+ for (l = priv->auths; l != NULL; l = next) {
+ struct service_auth *auth = l->data;
+
+ next = g_slist_next(l);
+
+ if (cb && auth->cb != cb)
+ continue;
+
+ if (user_data && auth->user_data != user_data)
+ continue;
+
+ priv->auths = g_slist_remove(priv->auths, auth);
+ g_free(auth);
+ }
+
+ if (g_slist_length(priv->auths) == 0) {
+ if (priv->auth_idle_id > 0) {
+ g_source_remove(priv->auth_idle_id);
+ priv->auth_idle_id = 0;
+ } else
+ btd_cancel_authorization(&dev->src, &dev->dst);
+ }
+
+ return 0;
+}
+
+void audio_device_set_authorized(struct audio_device *dev, gboolean auth)
+{
+ struct dev_priv *priv = dev->priv;
+
+ priv->authorized = auth;
+}
diff --git a/audio/device.h b/audio/device.h
new file mode 100644
index 0000000..35af788
--- /dev/null
+++ b/audio/device.h
@@ -0,0 +1,94 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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
+ *
+ */
+
+#define GENERIC_AUDIO_UUID "00001203-0000-1000-8000-00805f9b34fb"
+
+#define HSP_HS_UUID "00001108-0000-1000-8000-00805f9b34fb"
+#define HSP_AG_UUID "00001112-0000-1000-8000-00805f9b34fb"
+
+#define HFP_HS_UUID "0000111e-0000-1000-8000-00805f9b34fb"
+#define HFP_AG_UUID "0000111f-0000-1000-8000-00805f9b34fb"
+
+#define ADVANCED_AUDIO_UUID "0000110d-0000-1000-8000-00805f9b34fb"
+
+#define A2DP_SOURCE_UUID "0000110a-0000-1000-8000-00805f9b34fb"
+#define A2DP_SINK_UUID "0000110b-0000-1000-8000-00805f9b34fb"
+
+#define AVRCP_REMOTE_UUID "0000110e-0000-1000-8000-00805f9b34fb"
+#define AVRCP_TARGET_UUID "0000110c-0000-1000-8000-00805f9b34fb"
+
+/* Move these to respective .h files once they exist */
+#define AUDIO_SOURCE_INTERFACE "org.bluez.AudioSource"
+#define AUDIO_CONTROL_INTERFACE "org.bluez.Control"
+
+struct source;
+struct control;
+struct target;
+struct sink;
+struct headset;
+struct gateway;
+struct dev_priv;
+
+struct audio_device {
+ struct btd_device *btd_dev;
+
+ DBusConnection *conn;
+ char *path;
+ bdaddr_t src;
+ bdaddr_t dst;
+
+ gboolean auto_connect;
+
+ struct headset *headset;
+ struct gateway *gateway;
+ struct sink *sink;
+ struct source *source;
+ struct control *control;
+ struct target *target;
+
+ guint hs_preauth_id;
+
+ struct dev_priv *priv;
+};
+
+struct audio_device *audio_device_register(DBusConnection *conn,
+ struct btd_device *device,
+ const char *path, const bdaddr_t *src,
+ const bdaddr_t *dst);
+
+void audio_device_unregister(struct audio_device *device);
+
+gboolean audio_device_is_active(struct audio_device *dev,
+ const char *interface);
+
+typedef void (*authorization_cb) (DBusError *derr, void *user_data);
+
+int audio_device_cancel_authorization(struct audio_device *dev,
+ authorization_cb cb, void *user_data);
+
+int audio_device_request_authorization(struct audio_device *dev,
+ const char *uuid, authorization_cb cb,
+ void *user_data);
+
+void audio_device_set_authorized(struct audio_device *dev, gboolean auth);
diff --git a/audio/gateway.c b/audio/gateway.c
new file mode 100644
index 0000000..ec0ec5d
--- /dev/null
+++ b/audio/gateway.c
@@ -0,0 +1,711 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2008-2009 Leonid Movshovich <event.riga@gmail.org>
+ * Copyright (C) 2010 ProFUSION embedded systems
+ *
+ *
+ * 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 <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include "glib-helper.h"
+#include "device.h"
+#include "gateway.h"
+#include "log.h"
+#include "error.h"
+#include "btio.h"
+#include "dbus-common.h"
+
+#ifndef DBUS_TYPE_UNIX_FD
+#define DBUS_TYPE_UNIX_FD -1
+#endif
+
+struct hf_agent {
+ char *name; /* Bus id */
+ char *path; /* D-Bus path */
+ guint watch; /* Disconnect watch */
+};
+
+struct gateway {
+ gateway_state_t state;
+ GIOChannel *rfcomm;
+ GIOChannel *sco;
+ gateway_stream_cb_t sco_start_cb;
+ void *sco_start_cb_data;
+ struct hf_agent *agent;
+ DBusMessage *msg;
+};
+
+int gateway_close(struct audio_device *device);
+
+static const char *state2str(gateway_state_t state)
+{
+ switch (state) {
+ case GATEWAY_STATE_DISCONNECTED:
+ return "disconnected";
+ case GATEWAY_STATE_CONNECTING:
+ return "connecting";
+ case GATEWAY_STATE_CONNECTED:
+ return "connected";
+ case GATEWAY_STATE_PLAYING:
+ return "playing";
+ default:
+ return "";
+ }
+}
+
+static void agent_free(struct hf_agent *agent)
+{
+ if (!agent)
+ return;
+
+ g_free(agent->name);
+ g_free(agent->path);
+ g_free(agent);
+}
+
+static void change_state(struct audio_device *dev, gateway_state_t new_state)
+{
+ struct gateway *gw = dev->gateway;
+ const char *val;
+
+ if (gw->state == new_state)
+ return;
+
+ val = state2str(new_state);
+ gw->state = new_state;
+
+ emit_property_changed(dev->conn, dev->path,
+ AUDIO_GATEWAY_INTERFACE, "State",
+ DBUS_TYPE_STRING, &val);
+}
+
+static void agent_disconnect(struct audio_device *dev, struct hf_agent *agent)
+{
+ DBusMessage *msg;
+
+ msg = dbus_message_new_method_call(agent->name, agent->path,
+ "org.bluez.HandsfreeAgent", "Release");
+
+ g_dbus_send_message(dev->conn, msg);
+}
+
+static gboolean agent_sendfd(struct hf_agent *agent, int fd,
+ DBusPendingCallNotifyFunction notify, void *data)
+{
+ struct audio_device *dev = data;
+ DBusMessage *msg;
+ DBusPendingCall *call;
+
+ msg = dbus_message_new_method_call(agent->name, agent->path,
+ "org.bluez.HandsfreeAgent", "NewConnection");
+
+ dbus_message_append_args(msg, DBUS_TYPE_UNIX_FD, &fd,
+ DBUS_TYPE_INVALID);
+
+ if (dbus_connection_send_with_reply(dev->conn, msg, &call, -1) == FALSE)
+ return FALSE;
+
+ dbus_pending_call_set_notify(call, notify, dev, NULL);
+ dbus_pending_call_unref(call);
+
+ return TRUE;
+}
+
+static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
+ struct audio_device *dev)
+{
+ struct gateway *gw = dev->gateway;
+
+ if (cond & G_IO_NVAL)
+ return FALSE;
+
+ if (cond & (G_IO_ERR | G_IO_HUP)) {
+ DBG("sco connection is released");
+ g_io_channel_shutdown(gw->sco, TRUE, NULL);
+ g_io_channel_unref(gw->sco);
+ gw->sco = NULL;
+ change_state(dev, GATEWAY_STATE_CONNECTED);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void sco_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+ struct audio_device *dev = (struct audio_device *) user_data;
+ struct gateway *gw = dev->gateway;
+
+ DBG("at the begin of sco_connect_cb() in gateway.c");
+
+ gw->sco = g_io_channel_ref(chan);
+
+ if (gw->sco_start_cb)
+ gw->sco_start_cb(dev, err, gw->sco_start_cb_data);
+
+ if (err) {
+ error("sco_connect_cb(): %s", err->message);
+ gateway_close(dev);
+ return;
+ }
+
+ g_io_add_watch(gw->sco, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ (GIOFunc) sco_io_cb, dev);
+}
+
+static void newconnection_reply(DBusPendingCall *call, void *data)
+{
+ struct audio_device *dev = data;
+ DBusMessage *reply = dbus_pending_call_steal_reply(call);
+ DBusError derr;
+
+ if (!dev->gateway->rfcomm) {
+ DBG("RFCOMM disconnected from server before agent reply");
+ goto done;
+ }
+
+ dbus_error_init(&derr);
+ if (!dbus_set_error_from_message(&derr, reply)) {
+ DBG("Agent reply: file descriptor passed successfully");
+ change_state(dev, GATEWAY_STATE_CONNECTED);
+ goto done;
+ }
+
+ DBG("Agent reply: %s", derr.message);
+
+ dbus_error_free(&derr);
+ gateway_close(dev);
+
+done:
+ dbus_message_unref(reply);
+}
+
+static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
+ gpointer user_data)
+{
+ struct audio_device *dev = user_data;
+ struct gateway *gw = dev->gateway;
+ DBusMessage *reply;
+ int sk, ret;
+
+ if (err) {
+ error("connect(): %s", err->message);
+ if (gw->sco_start_cb)
+ gw->sco_start_cb(dev, err, gw->sco_start_cb_data);
+ goto fail;
+ }
+
+ if (!gw->agent) {
+ error("Handsfree Agent not registered");
+ goto fail;
+ }
+
+ sk = g_io_channel_unix_get_fd(chan);
+
+ gw->rfcomm = g_io_channel_ref(chan);
+
+ ret = agent_sendfd(gw->agent, sk, newconnection_reply, dev);
+
+ if (!gw->msg)
+ return;
+
+ if (ret)
+ reply = dbus_message_new_method_return(gw->msg);
+ else
+ reply = btd_error_failed(gw->msg, "Can't pass file descriptor");
+
+ g_dbus_send_message(dev->conn, reply);
+
+ return;
+
+fail:
+ if (gw->msg) {
+ DBusMessage *reply;
+ reply = btd_error_failed(gw->msg, "Connect failed");
+ g_dbus_send_message(dev->conn, reply);
+ }
+
+ change_state(dev, GATEWAY_STATE_DISCONNECTED);
+}
+
+static void get_record_cb(sdp_list_t *recs, int err, gpointer user_data)
+{
+ struct audio_device *dev = user_data;
+ struct gateway *gw = dev->gateway;
+ int ch;
+ sdp_list_t *protos, *classes;
+ uuid_t uuid;
+ GIOChannel *io;
+ GError *gerr = NULL;
+
+ if (err < 0) {
+ error("Unable to get service record: %s (%d)", strerror(-err),
+ -err);
+ goto fail;
+ }
+
+ if (!recs || !recs->data) {
+ error("No records found");
+ err = -EIO;
+ goto fail;
+ }
+
+ if (sdp_get_service_classes(recs->data, &classes) < 0) {
+ error("Unable to get service classes from record");
+ err = -EINVAL;
+ goto fail;
+ }
+
+ if (sdp_get_access_protos(recs->data, &protos) < 0) {
+ error("Unable to get access protocols from record");
+ err = -ENODATA;
+ goto fail;
+ }
+
+ memcpy(&uuid, classes->data, sizeof(uuid));
+ sdp_list_free(classes, free);
+
+ if (!sdp_uuid128_to_uuid(&uuid) || uuid.type != SDP_UUID16 ||
+ uuid.value.uuid16 != HANDSFREE_AGW_SVCLASS_ID) {
+ sdp_list_free(protos, NULL);
+ error("Invalid service record or not HFP");
+ err = -EIO;
+ goto fail;
+ }
+
+ ch = sdp_get_proto_port(protos, RFCOMM_UUID);
+ sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL);
+ sdp_list_free(protos, NULL);
+ if (ch <= 0) {
+ error("Unable to extract RFCOMM channel from service record");
+ err = -EIO;
+ goto fail;
+ }
+
+ io = bt_io_connect(BT_IO_RFCOMM, rfcomm_connect_cb, dev, NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, &dev->src,
+ BT_IO_OPT_DEST_BDADDR, &dev->dst,
+ BT_IO_OPT_CHANNEL, ch,
+ BT_IO_OPT_INVALID);
+ if (!io) {
+ error("Unable to connect: %s", gerr->message);
+ gateway_close(dev);
+ goto fail;
+ }
+
+ g_io_channel_unref(io);
+
+ change_state(dev, GATEWAY_STATE_CONNECTING);
+ return;
+
+fail:
+ if (gw->msg) {
+ DBusMessage *reply = btd_error_failed(gw->msg,
+ gerr ? gerr->message : strerror(-err));
+ g_dbus_send_message(dev->conn, reply);
+ }
+
+ change_state(dev, GATEWAY_STATE_DISCONNECTED);
+
+ if (!gerr)
+ g_set_error(&gerr, BT_IO_ERROR, BT_IO_ERROR_FAILED,
+ "connect: %s (%d)", strerror(-err), -err);
+
+ if (gw->sco_start_cb)
+ gw->sco_start_cb(dev, gerr, gw->sco_start_cb_data);
+
+ g_error_free(gerr);
+}
+
+static int get_records(struct audio_device *device)
+{
+ uuid_t uuid;
+
+ sdp_uuid16_create(&uuid, HANDSFREE_AGW_SVCLASS_ID);
+ return bt_search_service(&device->src, &device->dst, &uuid,
+ get_record_cb, device, NULL);
+}
+
+static DBusMessage *ag_connect(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct audio_device *au_dev = (struct audio_device *) data;
+ struct gateway *gw = au_dev->gateway;
+ int err;
+
+ if (!gw->agent)
+ return btd_error_agent_not_available(msg);
+
+ err = get_records(au_dev);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+ gw->msg = dbus_message_ref(msg);
+
+ return NULL;
+}
+
+int gateway_close(struct audio_device *device)
+{
+ struct gateway *gw = device->gateway;
+ int sock;
+
+ if (gw->rfcomm) {
+ sock = g_io_channel_unix_get_fd(gw->rfcomm);
+ shutdown(sock, SHUT_RDWR);
+
+ g_io_channel_shutdown(gw->rfcomm, TRUE, NULL);
+ g_io_channel_unref(gw->rfcomm);
+ gw->rfcomm = NULL;
+ }
+
+ if (gw->sco) {
+ g_io_channel_shutdown(gw->sco, TRUE, NULL);
+ g_io_channel_unref(gw->sco);
+ gw->sco = NULL;
+ gw->sco_start_cb = NULL;
+ gw->sco_start_cb_data = NULL;
+ }
+
+ change_state(device, GATEWAY_STATE_DISCONNECTED);
+
+ return 0;
+}
+
+static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct audio_device *device = data;
+ struct gateway *gw = device->gateway;
+ DBusMessage *reply = NULL;
+ char gw_addr[18];
+
+ if (!device->conn)
+ return NULL;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ if (!gw->rfcomm)
+ return btd_error_not_connected(msg);
+
+ gateway_close(device);
+ ba2str(&device->dst, gw_addr);
+ DBG("Disconnected from %s, %s", gw_addr, device->path);
+
+ return reply;
+}
+
+static void agent_exited(DBusConnection *conn, void *data)
+{
+ struct gateway *gateway = data;
+ struct hf_agent *agent = gateway->agent;
+
+ DBG("Agent %s exited", agent->name);
+
+ agent_free(agent);
+ gateway->agent = NULL;
+}
+
+static DBusMessage *ag_get_properties(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct audio_device *device = data;
+ struct gateway *gw = device->gateway;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ const char *value;
+
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ value = state2str(gw->state);
+ dict_append_entry(&dict, "State",
+ DBUS_TYPE_STRING, &value);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static DBusMessage *register_agent(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct audio_device *device = data;
+ struct gateway *gw = device->gateway;
+ struct hf_agent *agent;
+ const char *path, *name;
+
+ if (gw->agent)
+ return btd_error_already_exists(msg);
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ name = dbus_message_get_sender(msg);
+ agent = g_new0(struct hf_agent, 1);
+
+ agent->name = g_strdup(name);
+ agent->path = g_strdup(path);
+
+ agent->watch = g_dbus_add_disconnect_watch(conn, name,
+ agent_exited, gw, NULL);
+
+ gw->agent = agent;
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_agent(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct audio_device *device = data;
+ struct gateway *gw = device->gateway;
+ const char *path;
+
+ if (!gw->agent)
+ goto done;
+
+ if (strcmp(gw->agent->name, dbus_message_get_sender(msg)) != 0)
+ return btd_error_not_authorized(msg);
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ if (strcmp(gw->agent->path, path) != 0)
+ return btd_error_does_not_exist(msg);
+
+ g_dbus_remove_watch(device->conn, gw->agent->watch);
+
+ agent_free(gw->agent);
+ gw->agent = NULL;
+
+done:
+ return dbus_message_new_method_return(msg);
+}
+
+static GDBusMethodTable gateway_methods[] = {
+ { "Connect", "", "", ag_connect, G_DBUS_METHOD_FLAG_ASYNC },
+ { "Disconnect", "", "", ag_disconnect, G_DBUS_METHOD_FLAG_ASYNC },
+ { "GetProperties", "", "a{sv}", ag_get_properties },
+ { "RegisterAgent", "o", "", register_agent },
+ { "UnregisterAgent", "o", "", unregister_agent },
+ { NULL, NULL, NULL, NULL }
+};
+
+static GDBusSignalTable gateway_signals[] = {
+ { "PropertyChanged", "sv" },
+ { NULL, NULL }
+};
+
+static void path_unregister(void *data)
+{
+ struct audio_device *dev = data;
+
+ DBG("Unregistered interface %s on path %s",
+ AUDIO_GATEWAY_INTERFACE, dev->path);
+
+ gateway_close(dev);
+
+ g_free(dev->gateway);
+ dev->gateway = NULL;
+}
+
+void gateway_unregister(struct audio_device *dev)
+{
+ if (dev->gateway->agent)
+ agent_disconnect(dev, dev->gateway->agent);
+
+ g_dbus_unregister_interface(dev->conn, dev->path,
+ AUDIO_GATEWAY_INTERFACE);
+}
+
+struct gateway *gateway_init(struct audio_device *dev)
+{
+ if (DBUS_TYPE_UNIX_FD < 0)
+ return NULL;
+
+ if (!g_dbus_register_interface(dev->conn, dev->path,
+ AUDIO_GATEWAY_INTERFACE,
+ gateway_methods, gateway_signals,
+ NULL, dev, path_unregister))
+ return NULL;
+
+ return g_new0(struct gateway, 1);
+
+}
+
+gboolean gateway_is_connected(struct audio_device *dev)
+{
+ return (dev && dev->gateway &&
+ dev->gateway->state == GATEWAY_STATE_CONNECTED);
+}
+
+int gateway_connect_rfcomm(struct audio_device *dev, GIOChannel *io)
+{
+ if (!io)
+ return -EINVAL;
+
+ dev->gateway->rfcomm = g_io_channel_ref(io);
+
+ return 0;
+}
+
+int gateway_connect_sco(struct audio_device *dev, GIOChannel *io)
+{
+ struct gateway *gw = dev->gateway;
+
+ if (gw->sco)
+ return -EISCONN;
+
+ gw->sco = g_io_channel_ref(io);
+
+ g_io_add_watch(gw->sco, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ (GIOFunc) sco_io_cb, dev);
+
+ change_state(dev, GATEWAY_STATE_PLAYING);
+
+ return 0;
+}
+
+void gateway_start_service(struct audio_device *dev)
+{
+ struct gateway *gw = dev->gateway;
+ GError *err = NULL;
+
+ if (gw->rfcomm == NULL)
+ return;
+
+ if (!bt_io_accept(gw->rfcomm, rfcomm_connect_cb, dev, NULL, &err)) {
+ error("bt_io_accept: %s", err->message);
+ g_error_free(err);
+ }
+}
+
+/* These are functions to be called from unix.c for audio system
+ * ifaces (alsa, gstreamer, etc.) */
+gboolean gateway_request_stream(struct audio_device *dev,
+ gateway_stream_cb_t cb, void *user_data)
+{
+ struct gateway *gw = dev->gateway;
+ GError *err = NULL;
+ GIOChannel *io;
+
+ if (!gw->rfcomm) {
+ gw->sco_start_cb = cb;
+ gw->sco_start_cb_data = user_data;
+ get_records(dev);
+ } else if (!gw->sco) {
+ gw->sco_start_cb = cb;
+ gw->sco_start_cb_data = user_data;
+ io = bt_io_connect(BT_IO_SCO, sco_connect_cb, dev, NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &dev->src,
+ BT_IO_OPT_DEST_BDADDR, &dev->dst,
+ BT_IO_OPT_INVALID);
+ if (!io) {
+ error("%s", err->message);
+ g_error_free(err);
+ return FALSE;
+ }
+ } else if (cb)
+ cb(dev, err, user_data);
+
+ return TRUE;
+}
+
+int gateway_config_stream(struct audio_device *dev, gateway_stream_cb_t sco_cb,
+ void *user_data)
+{
+ struct gateway *gw = dev->gateway;
+
+ if (!gw->rfcomm) {
+ gw->sco_start_cb = sco_cb;
+ gw->sco_start_cb_data = user_data;
+ return get_records(dev);
+ }
+
+ if (sco_cb)
+ sco_cb(dev, NULL, user_data);
+
+ return 0;
+}
+
+gboolean gateway_cancel_stream(struct audio_device *dev, unsigned int id)
+{
+ gateway_close(dev);
+ return TRUE;
+}
+
+int gateway_get_sco_fd(struct audio_device *dev)
+{
+ struct gateway *gw = dev->gateway;
+
+ if (!gw || !gw->sco)
+ return -1;
+
+ return g_io_channel_unix_get_fd(gw->sco);
+}
+
+void gateway_suspend_stream(struct audio_device *dev)
+{
+ struct gateway *gw = dev->gateway;
+
+ if (!gw || !gw->sco)
+ return;
+
+ g_io_channel_shutdown(gw->sco, TRUE, NULL);
+ g_io_channel_unref(gw->sco);
+ gw->sco = NULL;
+ gw->sco_start_cb = NULL;
+ gw->sco_start_cb_data = NULL;
+ change_state(dev, GATEWAY_STATE_CONNECTED);
+}
diff --git a/audio/gateway.h b/audio/gateway.h
new file mode 100644
index 0000000..a45ef82
--- /dev/null
+++ b/audio/gateway.h
@@ -0,0 +1,51 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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
+ *
+ */
+
+#define AUDIO_GATEWAY_INTERFACE "org.bluez.HandsfreeGateway"
+
+#define DEFAULT_HFP_HS_CHANNEL 7
+
+typedef enum {
+ GATEWAY_STATE_DISCONNECTED,
+ GATEWAY_STATE_CONNECTING,
+ GATEWAY_STATE_CONNECTED,
+ GATEWAY_STATE_PLAYING,
+} gateway_state_t;
+
+typedef void (*gateway_stream_cb_t) (struct audio_device *dev, GError *err,
+ void *user_data);
+
+void gateway_unregister(struct audio_device *dev);
+struct gateway *gateway_init(struct audio_device *device);
+gboolean gateway_is_connected(struct audio_device *dev);
+int gateway_connect_rfcomm(struct audio_device *dev, GIOChannel *io);
+int gateway_connect_sco(struct audio_device *dev, GIOChannel *chan);
+void gateway_start_service(struct audio_device *device);
+gboolean gateway_request_stream(struct audio_device *dev,
+ gateway_stream_cb_t cb, void *user_data);
+int gateway_config_stream(struct audio_device *dev, gateway_stream_cb_t cb,
+ void *user_data);
+gboolean gateway_cancel_stream(struct audio_device *dev, unsigned int id);
+int gateway_get_sco_fd(struct audio_device *dev);
+void gateway_suspend_stream(struct audio_device *dev);
diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c
new file mode 100644
index 0000000..930d14e
--- /dev/null
+++ b/audio/gsta2dpsink.c
@@ -0,0 +1,730 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 <unistd.h>
+#include <pthread.h>
+
+#include "gstpragma.h"
+#include "gsta2dpsink.h"
+
+GST_DEBUG_CATEGORY_STATIC(gst_a2dp_sink_debug);
+#define GST_CAT_DEFAULT gst_a2dp_sink_debug
+
+#define A2DP_SBC_RTP_PAYLOAD_TYPE 1
+#define TEMPLATE_MAX_BITPOOL_STR "64"
+
+#define DEFAULT_AUTOCONNECT TRUE
+
+enum {
+ PROP_0,
+ PROP_DEVICE,
+ PROP_AUTOCONNECT,
+ PROP_TRANSPORT
+};
+
+GST_BOILERPLATE(GstA2dpSink, gst_a2dp_sink, GstBin, GST_TYPE_BIN);
+
+static const GstElementDetails gst_a2dp_sink_details =
+ GST_ELEMENT_DETAILS("Bluetooth A2DP sink",
+ "Sink/Audio",
+ "Plays audio to an A2DP device",
+ "Marcel Holtmann <marcel@holtmann.org>");
+
+static GstStaticPadTemplate gst_a2dp_sink_factory =
+ GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
+ GST_STATIC_CAPS("audio/x-sbc, "
+ "rate = (int) { 16000, 32000, 44100, 48000 }, "
+ "channels = (int) [ 1, 2 ], "
+ "mode = (string) { \"mono\", \"dual\", \"stereo\", \"joint\" }, "
+ "blocks = (int) { 4, 8, 12, 16 }, "
+ "subbands = (int) { 4, 8 }, "
+ "allocation = (string) { \"snr\", \"loudness\" }, "
+ "bitpool = (int) [ 2, "
+ TEMPLATE_MAX_BITPOOL_STR " ]; "
+ "audio/mpeg"
+ ));
+
+static gboolean gst_a2dp_sink_handle_event(GstPad *pad, GstEvent *event);
+static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps);
+static GstCaps *gst_a2dp_sink_get_caps(GstPad *pad);
+static gboolean gst_a2dp_sink_init_caps_filter(GstA2dpSink *self);
+static gboolean gst_a2dp_sink_init_fakesink(GstA2dpSink *self);
+static gboolean gst_a2dp_sink_remove_fakesink(GstA2dpSink *self);
+
+static void gst_a2dp_sink_finalize(GObject *obj)
+{
+ GstA2dpSink *self = GST_A2DP_SINK(obj);
+
+ g_mutex_free(self->cb_mutex);
+
+ G_OBJECT_CLASS(parent_class)->finalize(obj);
+}
+
+static GstState gst_a2dp_sink_get_state(GstA2dpSink *self)
+{
+ GstState current, pending;
+
+ gst_element_get_state(GST_ELEMENT(self), &current, &pending, 0);
+ if (pending == GST_STATE_VOID_PENDING)
+ return current;
+
+ return pending;
+}
+
+/*
+ * Helper function to create elements, add to the bin and link it
+ * to another element.
+ */
+static GstElement *gst_a2dp_sink_init_element(GstA2dpSink *self,
+ const gchar *elementname, const gchar *name,
+ GstElement *link_to)
+{
+ GstElement *element;
+ GstState state;
+
+ GST_LOG_OBJECT(self, "Initializing %s", elementname);
+
+ element = gst_element_factory_make(elementname, name);
+ if (element == NULL) {
+ GST_DEBUG_OBJECT(self, "Couldn't create %s", elementname);
+ return NULL;
+ }
+
+ if (!gst_bin_add(GST_BIN(self), element)) {
+ GST_DEBUG_OBJECT(self, "failed to add %s to the bin",
+ elementname);
+ goto cleanup_and_fail;
+ }
+
+ state = gst_a2dp_sink_get_state(self);
+ if (gst_element_set_state(element, state) ==
+ GST_STATE_CHANGE_FAILURE) {
+ GST_DEBUG_OBJECT(self, "%s failed to go to playing",
+ elementname);
+ goto remove_element_and_fail;
+ }
+
+ if (link_to != NULL)
+ if (!gst_element_link(link_to, element)) {
+ GST_DEBUG_OBJECT(self, "couldn't link %s",
+ elementname);
+ goto remove_element_and_fail;
+ }
+
+ return element;
+
+remove_element_and_fail:
+ gst_element_set_state(element, GST_STATE_NULL);
+ gst_bin_remove(GST_BIN(self), element);
+ return NULL;
+
+cleanup_and_fail:
+ g_object_unref(G_OBJECT(element));
+
+ return NULL;
+}
+
+static void gst_a2dp_sink_base_init(gpointer g_class)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
+
+ gst_element_class_set_details(element_class,
+ &gst_a2dp_sink_details);
+ gst_element_class_add_pad_template(element_class,
+ gst_static_pad_template_get(&gst_a2dp_sink_factory));
+}
+
+static void gst_a2dp_sink_set_property(GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ GstA2dpSink *self = GST_A2DP_SINK(object);
+
+ switch (prop_id) {
+ case PROP_DEVICE:
+ if (self->sink != NULL)
+ gst_avdtp_sink_set_device(self->sink,
+ g_value_get_string(value));
+
+ if (self->device != NULL)
+ g_free(self->device);
+ self->device = g_value_dup_string(value);
+ break;
+
+ case PROP_TRANSPORT:
+ if (self->sink != NULL)
+ gst_avdtp_sink_set_transport(self->sink,
+ g_value_get_string(value));
+
+ if (self->transport != NULL)
+ g_free(self->transport);
+ self->transport = g_value_dup_string(value);
+ break;
+
+ case PROP_AUTOCONNECT:
+ self->autoconnect = g_value_get_boolean(value);
+
+ if (self->sink != NULL)
+ g_object_set(G_OBJECT(self->sink), "auto-connect",
+ self->autoconnect, NULL);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
+ }
+}
+
+static void gst_a2dp_sink_get_property(GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GstA2dpSink *self = GST_A2DP_SINK(object);
+ gchar *device, *transport;
+
+ switch (prop_id) {
+ case PROP_DEVICE:
+ if (self->sink != NULL) {
+ device = gst_avdtp_sink_get_device(self->sink);
+ if (device != NULL)
+ g_value_take_string(value, device);
+ }
+ break;
+ case PROP_AUTOCONNECT:
+ if (self->sink != NULL)
+ g_object_get(G_OBJECT(self->sink), "auto-connect",
+ &self->autoconnect, NULL);
+
+ g_value_set_boolean(value, self->autoconnect);
+ break;
+ case PROP_TRANSPORT:
+ if (self->sink != NULL) {
+ transport = gst_avdtp_sink_get_transport(self->sink);
+ if (transport != NULL)
+ g_value_take_string(value, transport);
+ }
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
+ }
+}
+
+static gboolean gst_a2dp_sink_init_ghost_pad(GstA2dpSink *self)
+{
+ GstPad *capsfilter_pad;
+
+ /* we search for the capsfilter sinkpad */
+ capsfilter_pad = gst_element_get_static_pad(self->capsfilter, "sink");
+
+ /* now we add a ghostpad */
+ self->ghostpad = GST_GHOST_PAD(gst_ghost_pad_new("sink",
+ capsfilter_pad));
+ g_object_unref(capsfilter_pad);
+
+ /* the getcaps of our ghostpad must reflect the device caps */
+ gst_pad_set_getcaps_function(GST_PAD(self->ghostpad),
+ gst_a2dp_sink_get_caps);
+ self->ghostpad_setcapsfunc = GST_PAD_SETCAPSFUNC(self->ghostpad);
+ gst_pad_set_setcaps_function(GST_PAD(self->ghostpad),
+ GST_DEBUG_FUNCPTR(gst_a2dp_sink_set_caps));
+
+ /* we need to handle events on our own and we also need the eventfunc
+ * of the ghostpad for forwarding calls */
+ self->ghostpad_eventfunc = GST_PAD_EVENTFUNC(GST_PAD(self->ghostpad));
+ gst_pad_set_event_function(GST_PAD(self->ghostpad),
+ gst_a2dp_sink_handle_event);
+
+ if (!gst_element_add_pad(GST_ELEMENT(self), GST_PAD(self->ghostpad)))
+ GST_ERROR_OBJECT(self, "failed to add ghostpad");
+
+ return TRUE;
+}
+
+static void gst_a2dp_sink_remove_dynamic_elements(GstA2dpSink *self)
+{
+ if (self->rtp) {
+ GST_LOG_OBJECT(self, "removing rtp element from the bin");
+ if (!gst_bin_remove(GST_BIN(self), GST_ELEMENT(self->rtp)))
+ GST_WARNING_OBJECT(self, "failed to remove rtp "
+ "element from bin");
+ else
+ self->rtp = NULL;
+ }
+}
+
+static GstStateChangeReturn gst_a2dp_sink_change_state(GstElement *element,
+ GstStateChange transition)
+{
+ GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+ GstA2dpSink *self = GST_A2DP_SINK(element);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ self->taglist = gst_tag_list_new();
+
+ gst_a2dp_sink_init_fakesink(self);
+ break;
+
+ case GST_STATE_CHANGE_NULL_TO_READY:
+ self->sink_is_in_bin = FALSE;
+ self->sink = GST_AVDTP_SINK(gst_element_factory_make(
+ "avdtpsink", "avdtpsink"));
+ if (self->sink == NULL) {
+ GST_WARNING_OBJECT(self, "failed to create avdtpsink");
+ return GST_STATE_CHANGE_FAILURE;
+ }
+
+ if (self->device != NULL)
+ gst_avdtp_sink_set_device(self->sink,
+ self->device);
+
+ if (self->transport != NULL)
+ gst_avdtp_sink_set_transport(self->sink,
+ self->transport);
+
+ g_object_set(G_OBJECT(self->sink), "auto-connect",
+ self->autoconnect, NULL);
+
+ ret = gst_element_set_state(GST_ELEMENT(self->sink),
+ GST_STATE_READY);
+ break;
+ default:
+ break;
+ }
+
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ return ret;
+
+ ret = GST_ELEMENT_CLASS(parent_class)->change_state(element,
+ transition);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ if (self->taglist) {
+ gst_tag_list_free(self->taglist);
+ self->taglist = NULL;
+ }
+ if (self->newseg_event != NULL) {
+ gst_event_unref(self->newseg_event);
+ self->newseg_event = NULL;
+ }
+ gst_a2dp_sink_remove_fakesink(self);
+ break;
+
+ case GST_STATE_CHANGE_READY_TO_NULL:
+ if (self->sink_is_in_bin) {
+ if (!gst_bin_remove(GST_BIN(self),
+ GST_ELEMENT(self->sink)))
+ GST_WARNING_OBJECT(self, "Failed to remove "
+ "avdtpsink from bin");
+ } else if (self->sink != NULL) {
+ gst_element_set_state(GST_ELEMENT(self->sink),
+ GST_STATE_NULL);
+ g_object_unref(G_OBJECT(self->sink));
+ }
+
+ self->sink = NULL;
+
+ gst_a2dp_sink_remove_dynamic_elements(self);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static void gst_a2dp_sink_class_init(GstA2dpSinkClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+ GstElementClass *element_class = GST_ELEMENT_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ object_class->set_property = GST_DEBUG_FUNCPTR(
+ gst_a2dp_sink_set_property);
+ object_class->get_property = GST_DEBUG_FUNCPTR(
+ gst_a2dp_sink_get_property);
+
+ object_class->finalize = GST_DEBUG_FUNCPTR(
+ gst_a2dp_sink_finalize);
+
+ element_class->change_state = GST_DEBUG_FUNCPTR(
+ gst_a2dp_sink_change_state);
+
+ g_object_class_install_property(object_class, PROP_DEVICE,
+ g_param_spec_string("device", "Device",
+ "Bluetooth remote device address",
+ NULL, G_PARAM_READWRITE));
+
+ g_object_class_install_property(object_class, PROP_AUTOCONNECT,
+ g_param_spec_boolean("auto-connect", "Auto-connect",
+ "Automatically attempt to connect to device",
+ DEFAULT_AUTOCONNECT, G_PARAM_READWRITE));
+
+ g_object_class_install_property(object_class, PROP_TRANSPORT,
+ g_param_spec_string("transport", "Transport",
+ "Use configured transport",
+ NULL, G_PARAM_READWRITE));
+
+ GST_DEBUG_CATEGORY_INIT(gst_a2dp_sink_debug, "a2dpsink", 0,
+ "A2DP sink element");
+}
+
+GstCaps *gst_a2dp_sink_get_device_caps(GstA2dpSink *self)
+{
+ return gst_avdtp_sink_get_device_caps(self->sink);
+}
+
+static GstCaps *gst_a2dp_sink_get_caps(GstPad *pad)
+{
+ GstCaps *caps;
+ GstCaps *caps_aux;
+ GstA2dpSink *self = GST_A2DP_SINK(GST_PAD_PARENT(pad));
+
+ if (self->sink == NULL) {
+ GST_DEBUG_OBJECT(self, "a2dpsink isn't initialized "
+ "returning template caps");
+ caps = gst_static_pad_template_get_caps(
+ &gst_a2dp_sink_factory);
+ } else {
+ GST_LOG_OBJECT(self, "Getting device caps");
+ caps = gst_a2dp_sink_get_device_caps(self);
+ if (caps == NULL)
+ caps = gst_static_pad_template_get_caps(
+ &gst_a2dp_sink_factory);
+ }
+ caps_aux = gst_caps_copy(caps);
+ g_object_set(self->capsfilter, "caps", caps_aux, NULL);
+ gst_caps_unref(caps_aux);
+ return caps;
+}
+
+static gboolean gst_a2dp_sink_init_avdtp_sink(GstA2dpSink *self)
+{
+ GstElement *sink;
+
+ /* check if we don't need a new sink */
+ if (self->sink_is_in_bin)
+ return TRUE;
+
+ if (self->sink == NULL)
+ sink = gst_element_factory_make("avdtpsink", "avdtpsink");
+ else
+ sink = GST_ELEMENT(self->sink);
+
+ if (sink == NULL) {
+ GST_ERROR_OBJECT(self, "Couldn't create avdtpsink");
+ return FALSE;
+ }
+
+ if (!gst_bin_add(GST_BIN(self), sink)) {
+ GST_ERROR_OBJECT(self, "failed to add avdtpsink "
+ "to the bin");
+ goto cleanup_and_fail;
+ }
+
+ if (gst_element_set_state(sink, GST_STATE_READY) ==
+ GST_STATE_CHANGE_FAILURE) {
+ GST_ERROR_OBJECT(self, "avdtpsink failed to go to ready");
+ goto remove_element_and_fail;
+ }
+
+ if (!gst_element_link(GST_ELEMENT(self->rtp), sink)) {
+ GST_ERROR_OBJECT(self, "couldn't link rtpsbcpay "
+ "to avdtpsink");
+ goto remove_element_and_fail;
+ }
+
+ self->sink = GST_AVDTP_SINK(sink);
+ self->sink_is_in_bin = TRUE;
+ g_object_set(G_OBJECT(self->sink), "device", self->device, NULL);
+ g_object_set(G_OBJECT(self->sink), "transport", self->transport, NULL);
+
+ gst_element_set_state(sink, GST_STATE_PAUSED);
+
+ return TRUE;
+
+remove_element_and_fail:
+ gst_element_set_state(sink, GST_STATE_NULL);
+ gst_bin_remove(GST_BIN(self), sink);
+ return FALSE;
+
+cleanup_and_fail:
+ if (sink != NULL)
+ g_object_unref(G_OBJECT(sink));
+
+ return FALSE;
+}
+
+static gboolean gst_a2dp_sink_init_rtp_sbc_element(GstA2dpSink *self)
+{
+ GstElement *rtppay;
+
+ /* if we already have a rtp, we don't need a new one */
+ if (self->rtp != NULL)
+ return TRUE;
+
+ rtppay = gst_a2dp_sink_init_element(self, "rtpsbcpay", "rtp",
+ self->capsfilter);
+ if (rtppay == NULL)
+ return FALSE;
+
+ self->rtp = GST_BASE_RTP_PAYLOAD(rtppay);
+ g_object_set(G_OBJECT(self->rtp), "min-frames", -1, NULL);
+
+ gst_element_set_state(rtppay, GST_STATE_PAUSED);
+
+ return TRUE;
+}
+
+static gboolean gst_a2dp_sink_init_rtp_mpeg_element(GstA2dpSink *self)
+{
+ GstElement *rtppay;
+
+ /* check if we don't need a new rtp */
+ if (self->rtp)
+ return TRUE;
+
+ GST_LOG_OBJECT(self, "Initializing rtp mpeg element");
+ /* if capsfilter is not created then we can't have our rtp element */
+ if (self->capsfilter == NULL)
+ return FALSE;
+
+ rtppay = gst_a2dp_sink_init_element(self, "rtpmpapay", "rtp",
+ self->capsfilter);
+ if (rtppay == NULL)
+ return FALSE;
+
+ self->rtp = GST_BASE_RTP_PAYLOAD(rtppay);
+
+ gst_element_set_state(rtppay, GST_STATE_PAUSED);
+
+ return TRUE;
+}
+
+static gboolean gst_a2dp_sink_init_dynamic_elements(GstA2dpSink *self,
+ GstCaps *caps)
+{
+ GstStructure *structure;
+ GstEvent *event;
+ GstPad *capsfilterpad;
+ gboolean crc;
+ gchar *mode = NULL;
+
+ structure = gst_caps_get_structure(caps, 0);
+
+ /* before everything we need to remove fakesink */
+ gst_a2dp_sink_remove_fakesink(self);
+
+ /* first, we need to create our rtp payloader */
+ if (gst_structure_has_name(structure, "audio/x-sbc")) {
+ GST_LOG_OBJECT(self, "sbc media received");
+ if (!gst_a2dp_sink_init_rtp_sbc_element(self))
+ return FALSE;
+ } else if (gst_structure_has_name(structure, "audio/mpeg")) {
+ GST_LOG_OBJECT(self, "mp3 media received");
+ if (!gst_a2dp_sink_init_rtp_mpeg_element(self))
+ return FALSE;
+ } else {
+ GST_ERROR_OBJECT(self, "Unexpected media type");
+ return FALSE;
+ }
+
+ if (!gst_a2dp_sink_init_avdtp_sink(self))
+ return FALSE;
+
+ /* check if we should push the taglist FIXME should we push this?
+ * we can send the tags directly if needed */
+ if (self->taglist != NULL &&
+ gst_structure_has_name(structure, "audio/mpeg")) {
+
+ event = gst_event_new_tag(self->taglist);
+
+ /* send directly the crc */
+ if (gst_tag_list_get_boolean(self->taglist, "has-crc", &crc))
+ gst_avdtp_sink_set_crc(self->sink, crc);
+
+ if (gst_tag_list_get_string(self->taglist, "channel-mode",
+ &mode))
+ gst_avdtp_sink_set_channel_mode(self->sink, mode);
+
+ capsfilterpad = gst_ghost_pad_get_target(self->ghostpad);
+ gst_pad_send_event(capsfilterpad, event);
+ self->taglist = NULL;
+ g_free(mode);
+ }
+
+ if (!gst_avdtp_sink_set_device_caps(self->sink, caps))
+ return FALSE;
+
+ g_object_set(G_OBJECT(self->rtp), "mtu",
+ gst_avdtp_sink_get_link_mtu(self->sink), NULL);
+
+ /* we forward our new segment here if we have one */
+ if (self->newseg_event) {
+ gst_pad_send_event(GST_BASE_RTP_PAYLOAD_SINKPAD(self->rtp),
+ self->newseg_event);
+ self->newseg_event = NULL;
+ }
+
+ return TRUE;
+}
+
+static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps)
+{
+ GstA2dpSink *self;
+
+ self = GST_A2DP_SINK(GST_PAD_PARENT(pad));
+ GST_INFO_OBJECT(self, "setting caps");
+
+ /* now we know the caps */
+ gst_a2dp_sink_init_dynamic_elements(self, caps);
+
+ return self->ghostpad_setcapsfunc(GST_PAD(self->ghostpad), caps);
+}
+
+/* used for catching newsegment events while we don't have a sink, for
+ * later forwarding it to the sink */
+static gboolean gst_a2dp_sink_handle_event(GstPad *pad, GstEvent *event)
+{
+ GstA2dpSink *self;
+ GstTagList *taglist = NULL;
+ GstObject *parent;
+
+ self = GST_A2DP_SINK(GST_PAD_PARENT(pad));
+ parent = gst_element_get_parent(GST_ELEMENT(self->sink));
+
+ if (GST_EVENT_TYPE(event) == GST_EVENT_NEWSEGMENT &&
+ parent != GST_OBJECT_CAST(self)) {
+ if (self->newseg_event != NULL)
+ gst_event_unref(self->newseg_event);
+ self->newseg_event = gst_event_ref(event);
+
+ } else if (GST_EVENT_TYPE(event) == GST_EVENT_TAG &&
+ parent != GST_OBJECT_CAST(self)) {
+ if (self->taglist == NULL)
+ gst_event_parse_tag(event, &self->taglist);
+ else {
+ gst_event_parse_tag(event, &taglist);
+ gst_tag_list_insert(self->taglist, taglist,
+ GST_TAG_MERGE_REPLACE);
+ }
+ }
+
+ if (parent != NULL)
+ gst_object_unref(GST_OBJECT(parent));
+
+ return self->ghostpad_eventfunc(GST_PAD(self->ghostpad), event);
+}
+
+static gboolean gst_a2dp_sink_init_caps_filter(GstA2dpSink *self)
+{
+ GstElement *element;
+
+ element = gst_element_factory_make("capsfilter", "filter");
+ if (element == NULL)
+ goto failed;
+
+ if (!gst_bin_add(GST_BIN(self), element))
+ goto failed;
+
+ self->capsfilter = element;
+ return TRUE;
+
+failed:
+ GST_ERROR_OBJECT(self, "Failed to initialize caps filter");
+ return FALSE;
+}
+
+static gboolean gst_a2dp_sink_init_fakesink(GstA2dpSink *self)
+{
+ if (self->fakesink != NULL)
+ return TRUE;
+
+ g_mutex_lock(self->cb_mutex);
+ self->fakesink = gst_a2dp_sink_init_element(self, "fakesink",
+ "fakesink", self->capsfilter);
+ g_mutex_unlock(self->cb_mutex);
+
+ if (!self->fakesink)
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean gst_a2dp_sink_remove_fakesink(GstA2dpSink *self)
+{
+ g_mutex_lock(self->cb_mutex);
+
+ if (self->fakesink != NULL) {
+ gst_element_set_locked_state(self->fakesink, TRUE);
+ gst_element_set_state(self->fakesink, GST_STATE_NULL);
+
+ gst_bin_remove(GST_BIN(self), self->fakesink);
+ self->fakesink = NULL;
+ }
+
+ g_mutex_unlock(self->cb_mutex);
+
+ return TRUE;
+}
+
+static void gst_a2dp_sink_init(GstA2dpSink *self,
+ GstA2dpSinkClass *klass)
+{
+ self->sink = NULL;
+ self->fakesink = NULL;
+ self->rtp = NULL;
+ self->device = NULL;
+ self->transport = NULL;
+ self->autoconnect = DEFAULT_AUTOCONNECT;
+ self->capsfilter = NULL;
+ self->newseg_event = NULL;
+ self->taglist = NULL;
+ self->ghostpad = NULL;
+ self->sink_is_in_bin = FALSE;
+
+ self->cb_mutex = g_mutex_new();
+
+ /* we initialize our capsfilter */
+ gst_a2dp_sink_init_caps_filter(self);
+ g_object_set(self->capsfilter, "caps",
+ gst_static_pad_template_get_caps(&gst_a2dp_sink_factory),
+ NULL);
+
+ gst_a2dp_sink_init_fakesink(self);
+
+ gst_a2dp_sink_init_ghost_pad(self);
+
+}
+
+gboolean gst_a2dp_sink_plugin_init(GstPlugin *plugin)
+{
+ return gst_element_register(plugin, "a2dpsink",
+ GST_RANK_MARGINAL, GST_TYPE_A2DP_SINK);
+}
+
diff --git a/audio/gsta2dpsink.h b/audio/gsta2dpsink.h
new file mode 100644
index 0000000..5f96a3e
--- /dev/null
+++ b/audio/gsta2dpsink.h
@@ -0,0 +1,85 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef __GST_A2DP_SINK_H__
+#define __GST_A2DP_SINK_H__
+
+#include <gst/gst.h>
+#include <gst/rtp/gstbasertppayload.h>
+#include "gstavdtpsink.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_A2DP_SINK \
+ (gst_a2dp_sink_get_type())
+#define GST_A2DP_SINK(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_A2DP_SINK,GstA2dpSink))
+#define GST_A2DP_SINK_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_A2DP_SINK,GstA2dpSinkClass))
+#define GST_IS_A2DP_SINK(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_A2DP_SINK))
+#define GST_IS_A2DP_SINK_CLASS(obj) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_A2DP_SINK))
+
+typedef struct _GstA2dpSink GstA2dpSink;
+typedef struct _GstA2dpSinkClass GstA2dpSinkClass;
+
+struct _GstA2dpSink {
+ GstBin bin;
+
+ GstBaseRTPPayload *rtp;
+ GstAvdtpSink *sink;
+ GstElement *capsfilter;
+ GstElement *fakesink;
+
+ gchar *device;
+ gchar *transport;
+ gboolean autoconnect;
+ gboolean sink_is_in_bin;
+
+ GstGhostPad *ghostpad;
+ GstPadSetCapsFunction ghostpad_setcapsfunc;
+ GstPadEventFunction ghostpad_eventfunc;
+
+ GstEvent *newseg_event;
+ /* Store the tags received before the a2dpsender sink is created
+ * when it is created we forward this to it */
+ GstTagList *taglist;
+
+ GMutex *cb_mutex;
+};
+
+struct _GstA2dpSinkClass {
+ GstBinClass parent_class;
+};
+
+GType gst_a2dp_sink_get_type(void);
+
+gboolean gst_a2dp_sink_plugin_init (GstPlugin * plugin);
+
+GstCaps *gst_a2dp_sink_get_device_caps(GstA2dpSink *self);
+
+G_END_DECLS
+
+#endif
+
diff --git a/audio/gstavdtpsink.c b/audio/gstavdtpsink.c
new file mode 100644
index 0000000..ce1365a
--- /dev/null
+++ b/audio/gstavdtpsink.c
@@ -0,0 +1,2029 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 <unistd.h>
+#include <sys/un.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <pthread.h>
+
+#include <netinet/in.h>
+
+#include <bluetooth/bluetooth.h>
+
+#include <gst/rtp/gstrtpbuffer.h>
+
+#include <dbus/dbus.h>
+
+#include "ipc.h"
+#include "rtp.h"
+#include "a2dp-codecs.h"
+
+#include "gstpragma.h"
+#include "gstavdtpsink.h"
+
+GST_DEBUG_CATEGORY_STATIC(avdtp_sink_debug);
+#define GST_CAT_DEFAULT avdtp_sink_debug
+
+#define BUFFER_SIZE 2048
+#define TEMPLATE_MAX_BITPOOL 64
+#define CRC_PROTECTED 1
+#define CRC_UNPROTECTED 0
+
+#define DEFAULT_AUTOCONNECT TRUE
+
+#define GST_AVDTP_SINK_MUTEX_LOCK(s) G_STMT_START { \
+ g_mutex_lock(s->sink_lock); \
+ } G_STMT_END
+
+#define GST_AVDTP_SINK_MUTEX_UNLOCK(s) G_STMT_START { \
+ g_mutex_unlock(s->sink_lock); \
+ } G_STMT_END
+
+#ifndef DBUS_TYPE_UNIX_FD
+#define DBUS_TYPE_UNIX_FD -1
+#endif
+
+struct bluetooth_data {
+ struct bt_get_capabilities_rsp *caps; /* Bluetooth device caps */
+ guint link_mtu;
+
+ DBusConnection *conn;
+ guint8 codec; /* Bluetooth transport configuration */
+ gchar *uuid;
+ guint8 *config;
+ gint config_size;
+
+ gchar buffer[BUFFER_SIZE]; /* Codec transfer buffer */
+};
+
+#define IS_SBC(n) (strcmp((n), "audio/x-sbc") == 0)
+#define IS_MPEG_AUDIO(n) (strcmp((n), "audio/mpeg") == 0)
+
+enum {
+ PROP_0,
+ PROP_DEVICE,
+ PROP_AUTOCONNECT,
+ PROP_TRANSPORT
+};
+
+GST_BOILERPLATE(GstAvdtpSink, gst_avdtp_sink, GstBaseSink,
+ GST_TYPE_BASE_SINK);
+
+static const GstElementDetails avdtp_sink_details =
+ GST_ELEMENT_DETAILS("Bluetooth AVDTP sink",
+ "Sink/Audio",
+ "Plays audio to an A2DP device",
+ "Marcel Holtmann <marcel@holtmann.org>");
+
+static GstStaticPadTemplate avdtp_sink_factory =
+ GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
+ GST_STATIC_CAPS("application/x-rtp, "
+ "media = (string) \"audio\","
+ "payload = (int) "
+ GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
+ "clock-rate = (int) { 16000, 32000, "
+ "44100, 48000 }, "
+ "encoding-name = (string) \"SBC\"; "
+ "application/x-rtp, "
+ "media = (string) \"audio\", "
+ "payload = (int) "
+ GST_RTP_PAYLOAD_MPA_STRING ", "
+ "clock-rate = (int) 90000; "
+ "application/x-rtp, "
+ "media = (string) \"audio\", "
+ "payload = (int) "
+ GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
+ "clock-rate = (int) 90000, "
+ "encoding-name = (string) \"MPA\""
+ ));
+
+static int gst_avdtp_sink_audioservice_send(GstAvdtpSink *self,
+ const bt_audio_msg_header_t *msg);
+static int gst_avdtp_sink_audioservice_expect(GstAvdtpSink *self,
+ bt_audio_msg_header_t *outmsg,
+ guint8 expected_name);
+
+
+static void gst_avdtp_sink_base_init(gpointer g_class)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
+
+ gst_element_class_add_pad_template(element_class,
+ gst_static_pad_template_get(&avdtp_sink_factory));
+
+ gst_element_class_set_details(element_class, &avdtp_sink_details);
+}
+
+static void gst_avdtp_sink_transport_release(GstAvdtpSink *self)
+{
+ DBusMessage *msg;
+ const char *access_type = "w";
+
+ msg = dbus_message_new_method_call("org.bluez", self->transport,
+ "org.bluez.MediaTransport",
+ "Release");
+
+ dbus_message_append_args(msg, DBUS_TYPE_STRING, &access_type,
+ DBUS_TYPE_INVALID);
+
+ dbus_connection_send(self->data->conn, msg, NULL);
+
+ dbus_message_unref(msg);
+}
+
+static gboolean gst_avdtp_sink_stop(GstBaseSink *basesink)
+{
+ GstAvdtpSink *self = GST_AVDTP_SINK(basesink);
+
+ GST_INFO_OBJECT(self, "stop");
+
+ if (self->watch_id != 0) {
+ g_source_remove(self->watch_id);
+ self->watch_id = 0;
+ }
+
+ if (self->server) {
+ bt_audio_service_close(g_io_channel_unix_get_fd(self->server));
+ g_io_channel_unref(self->server);
+ self->server = NULL;
+ }
+
+ if (self->stream) {
+ g_io_channel_shutdown(self->stream, TRUE, NULL);
+ g_io_channel_unref(self->stream);
+ self->stream = NULL;
+ }
+
+ if (self->data) {
+ if (self->transport)
+ gst_avdtp_sink_transport_release(self);
+ if (self->data->conn)
+ dbus_connection_unref(self->data->conn);
+ g_free(self->data);
+ self->data = NULL;
+ }
+
+ if (self->stream_caps) {
+ gst_caps_unref(self->stream_caps);
+ self->stream_caps = NULL;
+ }
+
+ if (self->dev_caps) {
+ gst_caps_unref(self->dev_caps);
+ self->dev_caps = NULL;
+ }
+
+ return TRUE;
+}
+
+static void gst_avdtp_sink_finalize(GObject *object)
+{
+ GstAvdtpSink *self = GST_AVDTP_SINK(object);
+
+ if (self->data)
+ gst_avdtp_sink_stop(GST_BASE_SINK(self));
+
+ if (self->device)
+ g_free(self->device);
+
+ if (self->transport)
+ g_free(self->transport);
+
+ g_mutex_free(self->sink_lock);
+
+ G_OBJECT_CLASS(parent_class)->finalize(object);
+}
+
+static void gst_avdtp_sink_set_property(GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ GstAvdtpSink *sink = GST_AVDTP_SINK(object);
+
+ switch (prop_id) {
+ case PROP_DEVICE:
+ if (sink->device)
+ g_free(sink->device);
+ sink->device = g_value_dup_string(value);
+ break;
+
+ case PROP_AUTOCONNECT:
+ sink->autoconnect = g_value_get_boolean(value);
+ break;
+
+ case PROP_TRANSPORT:
+ if (sink->transport)
+ g_free(sink->transport);
+ sink->transport = g_value_dup_string(value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
+ }
+}
+
+static void gst_avdtp_sink_get_property(GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GstAvdtpSink *sink = GST_AVDTP_SINK(object);
+
+ switch (prop_id) {
+ case PROP_DEVICE:
+ g_value_set_string(value, sink->device);
+ break;
+
+ case PROP_AUTOCONNECT:
+ g_value_set_boolean(value, sink->autoconnect);
+ break;
+
+ case PROP_TRANSPORT:
+ g_value_set_string(value, sink->transport);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
+ }
+}
+
+static gint gst_avdtp_sink_bluetooth_recvmsg_fd(GstAvdtpSink *sink)
+{
+ int err, ret;
+
+ ret = bt_audio_service_get_data_fd(
+ g_io_channel_unix_get_fd(sink->server));
+
+ if (ret < 0) {
+ err = errno;
+ GST_ERROR_OBJECT(sink, "Unable to receive fd: %s (%d)",
+ strerror(err), err);
+ return -err;
+ }
+
+ sink->stream = g_io_channel_unix_new(ret);
+ g_io_channel_set_encoding(sink->stream, NULL, NULL);
+ GST_DEBUG_OBJECT(sink, "stream_fd=%d", ret);
+
+ return 0;
+}
+
+static codec_capabilities_t *gst_avdtp_find_caps(GstAvdtpSink *sink,
+ uint8_t codec_type)
+{
+ struct bt_get_capabilities_rsp *rsp = sink->data->caps;
+ codec_capabilities_t *codec = (void *) rsp->data;
+ int bytes_left = rsp->h.length - sizeof(*rsp);
+
+ while (bytes_left > 0) {
+ if ((codec->type == codec_type) &&
+ !(codec->lock & BT_WRITE_LOCK))
+ break;
+
+ bytes_left -= codec->length;
+ codec = (void *) codec + codec->length;
+ }
+
+ if (bytes_left <= 0)
+ return NULL;
+
+ return codec;
+}
+
+static gboolean gst_avdtp_sink_init_sbc_pkt_conf(GstAvdtpSink *sink,
+ GstCaps *caps,
+ sbc_capabilities_t *pkt)
+{
+ sbc_capabilities_t *cfg;
+ const GValue *value = NULL;
+ const char *pref, *name;
+ gint rate, subbands, blocks;
+ GstStructure *structure = gst_caps_get_structure(caps, 0);
+
+ cfg = (void *) gst_avdtp_find_caps(sink, BT_A2DP_SBC_SINK);
+ name = gst_structure_get_name(structure);
+
+ if (!(IS_SBC(name))) {
+ GST_ERROR_OBJECT(sink, "Unexpected format %s, "
+ "was expecting sbc", name);
+ return FALSE;
+ }
+
+ value = gst_structure_get_value(structure, "rate");
+ rate = g_value_get_int(value);
+ if (rate == 44100)
+ cfg->frequency = BT_SBC_SAMPLING_FREQ_44100;
+ else if (rate == 48000)
+ cfg->frequency = BT_SBC_SAMPLING_FREQ_48000;
+ else if (rate == 32000)
+ cfg->frequency = BT_SBC_SAMPLING_FREQ_32000;
+ else if (rate == 16000)
+ cfg->frequency = BT_SBC_SAMPLING_FREQ_16000;
+ else {
+ GST_ERROR_OBJECT(sink, "Invalid rate while setting caps");
+ return FALSE;
+ }
+
+ value = gst_structure_get_value(structure, "mode");
+ pref = g_value_get_string(value);
+ if (strcmp(pref, "mono") == 0)
+ cfg->channel_mode = BT_A2DP_CHANNEL_MODE_MONO;
+ else if (strcmp(pref, "dual") == 0)
+ cfg->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL;
+ else if (strcmp(pref, "stereo") == 0)
+ cfg->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO;
+ else if (strcmp(pref, "joint") == 0)
+ cfg->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO;
+ else {
+ GST_ERROR_OBJECT(sink, "Invalid mode %s", pref);
+ return FALSE;
+ }
+
+ value = gst_structure_get_value(structure, "allocation");
+ pref = g_value_get_string(value);
+ if (strcmp(pref, "loudness") == 0)
+ cfg->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS;
+ else if (strcmp(pref, "snr") == 0)
+ cfg->allocation_method = BT_A2DP_ALLOCATION_SNR;
+ else {
+ GST_ERROR_OBJECT(sink, "Invalid allocation: %s", pref);
+ return FALSE;
+ }
+
+ value = gst_structure_get_value(structure, "subbands");
+ subbands = g_value_get_int(value);
+ if (subbands == 8)
+ cfg->subbands = BT_A2DP_SUBBANDS_8;
+ else if (subbands == 4)
+ cfg->subbands = BT_A2DP_SUBBANDS_4;
+ else {
+ GST_ERROR_OBJECT(sink, "Invalid subbands %d", subbands);
+ return FALSE;
+ }
+
+ value = gst_structure_get_value(structure, "blocks");
+ blocks = g_value_get_int(value);
+ if (blocks == 16)
+ cfg->block_length = BT_A2DP_BLOCK_LENGTH_16;
+ else if (blocks == 12)
+ cfg->block_length = BT_A2DP_BLOCK_LENGTH_12;
+ else if (blocks == 8)
+ cfg->block_length = BT_A2DP_BLOCK_LENGTH_8;
+ else if (blocks == 4)
+ cfg->block_length = BT_A2DP_BLOCK_LENGTH_4;
+ else {
+ GST_ERROR_OBJECT(sink, "Invalid blocks %d", blocks);
+ return FALSE;
+ }
+
+ value = gst_structure_get_value(structure, "bitpool");
+ cfg->max_bitpool = cfg->min_bitpool = g_value_get_int(value);
+
+ memcpy(pkt, cfg, sizeof(*pkt));
+
+ return TRUE;
+}
+
+static gboolean gst_avdtp_sink_conf_recv_stream_fd(
+ GstAvdtpSink *self)
+{
+ struct bluetooth_data *data = self->data;
+ gint ret;
+ GError *gerr = NULL;
+ GIOStatus status;
+ GIOFlags flags;
+ int fd;
+
+ /* Proceed if stream was already acquired */
+ if (self->stream != NULL)
+ goto proceed;
+
+ ret = gst_avdtp_sink_bluetooth_recvmsg_fd(self);
+ if (ret < 0)
+ return FALSE;
+
+ if (!self->stream) {
+ GST_ERROR_OBJECT(self, "Error while configuring device: "
+ "could not acquire audio socket");
+ return FALSE;
+ }
+
+proceed:
+ /* set stream socket to nonblock */
+ GST_LOG_OBJECT(self, "setting stream socket to nonblock");
+ flags = g_io_channel_get_flags(self->stream);
+ flags |= G_IO_FLAG_NONBLOCK;
+ status = g_io_channel_set_flags(self->stream, flags, &gerr);
+ if (status != G_IO_STATUS_NORMAL) {
+ if (gerr)
+ GST_WARNING_OBJECT(self, "Error while "
+ "setting server socket to nonblock: "
+ "%s", gerr->message);
+ else
+ GST_WARNING_OBJECT(self, "Error while "
+ "setting server "
+ "socket to nonblock");
+ }
+
+ fd = g_io_channel_unix_get_fd(self->stream);
+
+ /* It is possible there is some outstanding
+ data in the pipe - we have to empty it */
+ GST_LOG_OBJECT(self, "emptying stream pipe");
+ while (1) {
+ ssize_t bread = read(fd, data->buffer, data->link_mtu);
+ if (bread <= 0)
+ break;
+ }
+
+ /* set stream socket to block */
+ GST_LOG_OBJECT(self, "setting stream socket to block");
+ flags = g_io_channel_get_flags(self->stream);
+ flags &= ~G_IO_FLAG_NONBLOCK;
+ status = g_io_channel_set_flags(self->stream, flags, &gerr);
+ if (status != G_IO_STATUS_NORMAL) {
+ if (gerr)
+ GST_WARNING_OBJECT(self, "Error while "
+ "setting server socket to block:"
+ "%s", gerr->message);
+ else
+ GST_WARNING_OBJECT(self, "Error while "
+ "setting server "
+ "socket to block");
+ }
+
+ memset(data->buffer, 0, sizeof(data->buffer));
+
+ return TRUE;
+}
+
+static gboolean server_callback(GIOChannel *chan,
+ GIOCondition cond, gpointer data)
+{
+ if (cond & G_IO_HUP || cond & G_IO_NVAL)
+ return FALSE;
+ else if (cond & G_IO_ERR)
+ GST_WARNING_OBJECT(GST_AVDTP_SINK(data),
+ "Untreated callback G_IO_ERR");
+
+ return TRUE;
+}
+
+static GstStructure *gst_avdtp_sink_parse_sbc_caps(
+ GstAvdtpSink *self, sbc_capabilities_t *sbc)
+{
+ GstStructure *structure;
+ GValue *value;
+ GValue *list;
+ gboolean mono, stereo;
+
+ if (sbc == NULL)
+ return NULL;
+
+ structure = gst_structure_empty_new("audio/x-sbc");
+ value = g_value_init(g_new0(GValue, 1), G_TYPE_STRING);
+
+ /* mode */
+ list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+ if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) {
+ g_value_set_static_string(value, "mono");
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) {
+ g_value_set_static_string(value, "stereo");
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) {
+ g_value_set_static_string(value, "dual");
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO) {
+ g_value_set_static_string(value, "joint");
+ gst_value_list_prepend_value(list, value);
+ }
+ g_value_unset(value);
+ if (list) {
+ gst_structure_set_value(structure, "mode", list);
+ g_free(list);
+ list = NULL;
+ }
+
+ /* subbands */
+ list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+ value = g_value_init(value, G_TYPE_INT);
+ if (sbc->subbands & BT_A2DP_SUBBANDS_4) {
+ g_value_set_int(value, 4);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->subbands & BT_A2DP_SUBBANDS_8) {
+ g_value_set_int(value, 8);
+ gst_value_list_prepend_value(list, value);
+ }
+ g_value_unset(value);
+ if (list) {
+ gst_structure_set_value(structure, "subbands", list);
+ g_free(list);
+ list = NULL;
+ }
+
+ /* blocks */
+ value = g_value_init(value, G_TYPE_INT);
+ list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+ if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_16) {
+ g_value_set_int(value, 16);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_12) {
+ g_value_set_int(value, 12);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_8) {
+ g_value_set_int(value, 8);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_4) {
+ g_value_set_int(value, 4);
+ gst_value_list_prepend_value(list, value);
+ }
+ g_value_unset(value);
+ if (list) {
+ gst_structure_set_value(structure, "blocks", list);
+ g_free(list);
+ list = NULL;
+ }
+
+ /* allocation */
+ g_value_init(value, G_TYPE_STRING);
+ list = g_value_init(g_new0(GValue,1), GST_TYPE_LIST);
+ if (sbc->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS) {
+ g_value_set_static_string(value, "loudness");
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->allocation_method & BT_A2DP_ALLOCATION_SNR) {
+ g_value_set_static_string(value, "snr");
+ gst_value_list_prepend_value(list, value);
+ }
+ g_value_unset(value);
+ if (list) {
+ gst_structure_set_value(structure, "allocation", list);
+ g_free(list);
+ list = NULL;
+ }
+
+ /* rate */
+ g_value_init(value, G_TYPE_INT);
+ list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+ if (sbc->frequency & BT_SBC_SAMPLING_FREQ_48000) {
+ g_value_set_int(value, 48000);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->frequency & BT_SBC_SAMPLING_FREQ_44100) {
+ g_value_set_int(value, 44100);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->frequency & BT_SBC_SAMPLING_FREQ_32000) {
+ g_value_set_int(value, 32000);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->frequency & BT_SBC_SAMPLING_FREQ_16000) {
+ g_value_set_int(value, 16000);
+ gst_value_list_prepend_value(list, value);
+ }
+ g_value_unset(value);
+ if (list) {
+ gst_structure_set_value(structure, "rate", list);
+ g_free(list);
+ list = NULL;
+ }
+
+ /* bitpool */
+ value = g_value_init(value, GST_TYPE_INT_RANGE);
+ gst_value_set_int_range(value,
+ MIN(sbc->min_bitpool, TEMPLATE_MAX_BITPOOL),
+ MIN(sbc->max_bitpool, TEMPLATE_MAX_BITPOOL));
+ gst_structure_set_value(structure, "bitpool", value);
+ g_value_unset(value);
+
+ /* channels */
+ mono = FALSE;
+ stereo = FALSE;
+ if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_MONO)
+ mono = TRUE;
+ if ((sbc->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) ||
+ (sbc->channel_mode &
+ BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) ||
+ (sbc->channel_mode &
+ BT_A2DP_CHANNEL_MODE_JOINT_STEREO))
+ stereo = TRUE;
+
+ if (mono && stereo) {
+ g_value_init(value, GST_TYPE_INT_RANGE);
+ gst_value_set_int_range(value, 1, 2);
+ } else {
+ g_value_init(value, G_TYPE_INT);
+ if (mono)
+ g_value_set_int(value, 1);
+ else if (stereo)
+ g_value_set_int(value, 2);
+ else {
+ GST_ERROR_OBJECT(self,
+ "Unexpected number of channels");
+ g_value_set_int(value, 0);
+ }
+ }
+
+ gst_structure_set_value(structure, "channels", value);
+ g_free(value);
+
+ return structure;
+}
+
+static GstStructure *gst_avdtp_sink_parse_mpeg_caps(
+ GstAvdtpSink *self, mpeg_capabilities_t *mpeg)
+{
+ GstStructure *structure;
+ GValue *value;
+ GValue *list;
+ gboolean valid_layer = FALSE;
+ gboolean mono, stereo;
+
+ if (!mpeg)
+ return NULL;
+
+ GST_LOG_OBJECT(self, "parsing mpeg caps");
+
+ structure = gst_structure_empty_new("audio/mpeg");
+ value = g_new0(GValue, 1);
+ g_value_init(value, G_TYPE_INT);
+
+ list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+ g_value_set_int(value, 1);
+ gst_value_list_prepend_value(list, value);
+ g_value_set_int(value, 2);
+ gst_value_list_prepend_value(list, value);
+ gst_structure_set_value(structure, "mpegversion", list);
+ g_free(list);
+
+ /* layer */
+ GST_LOG_OBJECT(self, "setting mpeg layer");
+ list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+ if (mpeg->layer & BT_MPEG_LAYER_1) {
+ g_value_set_int(value, 1);
+ gst_value_list_prepend_value(list, value);
+ valid_layer = TRUE;
+ }
+ if (mpeg->layer & BT_MPEG_LAYER_2) {
+ g_value_set_int(value, 2);
+ gst_value_list_prepend_value(list, value);
+ valid_layer = TRUE;
+ }
+ if (mpeg->layer & BT_MPEG_LAYER_3) {
+ g_value_set_int(value, 3);
+ gst_value_list_prepend_value(list, value);
+ valid_layer = TRUE;
+ }
+ if (list) {
+ gst_structure_set_value(structure, "layer", list);
+ g_free(list);
+ list = NULL;
+ }
+
+ if (!valid_layer) {
+ gst_structure_free(structure);
+ g_free(value);
+ return NULL;
+ }
+
+ /* rate */
+ GST_LOG_OBJECT(self, "setting mpeg rate");
+ list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+ if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_48000) {
+ g_value_set_int(value, 48000);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_44100) {
+ g_value_set_int(value, 44100);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_32000) {
+ g_value_set_int(value, 32000);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_24000) {
+ g_value_set_int(value, 24000);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_22050) {
+ g_value_set_int(value, 22050);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_16000) {
+ g_value_set_int(value, 16000);
+ gst_value_list_prepend_value(list, value);
+ }
+ g_value_unset(value);
+ if (list) {
+ gst_structure_set_value(structure, "rate", list);
+ g_free(list);
+ list = NULL;
+ }
+
+ /* channels */
+ GST_LOG_OBJECT(self, "setting mpeg channels");
+ mono = FALSE;
+ stereo = FALSE;
+ if (mpeg->channel_mode & BT_A2DP_CHANNEL_MODE_MONO)
+ mono = TRUE;
+ if ((mpeg->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) ||
+ (mpeg->channel_mode &
+ BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) ||
+ (mpeg->channel_mode &
+ BT_A2DP_CHANNEL_MODE_JOINT_STEREO))
+ stereo = TRUE;
+
+ if (mono && stereo) {
+ g_value_init(value, GST_TYPE_INT_RANGE);
+ gst_value_set_int_range(value, 1, 2);
+ } else {
+ g_value_init(value, G_TYPE_INT);
+ if (mono)
+ g_value_set_int(value, 1);
+ else if (stereo)
+ g_value_set_int(value, 2);
+ else {
+ GST_ERROR_OBJECT(self,
+ "Unexpected number of channels");
+ g_value_set_int(value, 0);
+ }
+ }
+ gst_structure_set_value(structure, "channels", value);
+ g_free(value);
+
+ return structure;
+}
+
+static GstStructure *gst_avdtp_sink_parse_sbc_raw(GstAvdtpSink *self)
+{
+ a2dp_sbc_t *sbc = (a2dp_sbc_t *) self->data->config;
+ GstStructure *structure;
+ GValue *value;
+ GValue *list;
+ gboolean mono, stereo;
+
+ structure = gst_structure_empty_new("audio/x-sbc");
+ value = g_value_init(g_new0(GValue, 1), G_TYPE_STRING);
+
+ /* mode */
+ list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+ if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) {
+ g_value_set_static_string(value, "mono");
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) {
+ g_value_set_static_string(value, "stereo");
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) {
+ g_value_set_static_string(value, "dual");
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO) {
+ g_value_set_static_string(value, "joint");
+ gst_value_list_prepend_value(list, value);
+ }
+ g_value_unset(value);
+ if (list) {
+ gst_structure_set_value(structure, "mode", list);
+ g_free(list);
+ list = NULL;
+ }
+
+ /* subbands */
+ list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+ value = g_value_init(value, G_TYPE_INT);
+ if (sbc->subbands & BT_A2DP_SUBBANDS_4) {
+ g_value_set_int(value, 4);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->subbands & BT_A2DP_SUBBANDS_8) {
+ g_value_set_int(value, 8);
+ gst_value_list_prepend_value(list, value);
+ }
+ g_value_unset(value);
+ if (list) {
+ gst_structure_set_value(structure, "subbands", list);
+ g_free(list);
+ list = NULL;
+ }
+
+ /* blocks */
+ value = g_value_init(value, G_TYPE_INT);
+ list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+ if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_16) {
+ g_value_set_int(value, 16);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_12) {
+ g_value_set_int(value, 12);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_8) {
+ g_value_set_int(value, 8);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_4) {
+ g_value_set_int(value, 4);
+ gst_value_list_prepend_value(list, value);
+ }
+ g_value_unset(value);
+ if (list) {
+ gst_structure_set_value(structure, "blocks", list);
+ g_free(list);
+ list = NULL;
+ }
+
+ /* allocation */
+ g_value_init(value, G_TYPE_STRING);
+ list = g_value_init(g_new0(GValue,1), GST_TYPE_LIST);
+ if (sbc->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS) {
+ g_value_set_static_string(value, "loudness");
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->allocation_method & BT_A2DP_ALLOCATION_SNR) {
+ g_value_set_static_string(value, "snr");
+ gst_value_list_prepend_value(list, value);
+ }
+ g_value_unset(value);
+ if (list) {
+ gst_structure_set_value(structure, "allocation", list);
+ g_free(list);
+ list = NULL;
+ }
+
+ /* rate */
+ g_value_init(value, G_TYPE_INT);
+ list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+ if (sbc->frequency & BT_SBC_SAMPLING_FREQ_48000) {
+ g_value_set_int(value, 48000);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->frequency & BT_SBC_SAMPLING_FREQ_44100) {
+ g_value_set_int(value, 44100);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->frequency & BT_SBC_SAMPLING_FREQ_32000) {
+ g_value_set_int(value, 32000);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->frequency & BT_SBC_SAMPLING_FREQ_16000) {
+ g_value_set_int(value, 16000);
+ gst_value_list_prepend_value(list, value);
+ }
+ g_value_unset(value);
+ if (list) {
+ gst_structure_set_value(structure, "rate", list);
+ g_free(list);
+ list = NULL;
+ }
+
+ /* bitpool */
+ value = g_value_init(value, GST_TYPE_INT_RANGE);
+ gst_value_set_int_range(value,
+ MIN(sbc->min_bitpool, TEMPLATE_MAX_BITPOOL),
+ MIN(sbc->max_bitpool, TEMPLATE_MAX_BITPOOL));
+ gst_structure_set_value(structure, "bitpool", value);
+ g_value_unset(value);
+
+ /* channels */
+ mono = FALSE;
+ stereo = FALSE;
+ if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_MONO)
+ mono = TRUE;
+ if ((sbc->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) ||
+ (sbc->channel_mode &
+ BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) ||
+ (sbc->channel_mode &
+ BT_A2DP_CHANNEL_MODE_JOINT_STEREO))
+ stereo = TRUE;
+
+ if (mono && stereo) {
+ g_value_init(value, GST_TYPE_INT_RANGE);
+ gst_value_set_int_range(value, 1, 2);
+ } else {
+ g_value_init(value, G_TYPE_INT);
+ if (mono)
+ g_value_set_int(value, 1);
+ else if (stereo)
+ g_value_set_int(value, 2);
+ else {
+ GST_ERROR_OBJECT(self,
+ "Unexpected number of channels");
+ g_value_set_int(value, 0);
+ }
+ }
+
+ gst_structure_set_value(structure, "channels", value);
+ g_free(value);
+
+ return structure;
+}
+
+static GstStructure *gst_avdtp_sink_parse_mpeg_raw(GstAvdtpSink *self)
+{
+ a2dp_mpeg_t *mpeg = (a2dp_mpeg_t *) self->data->config;
+ GstStructure *structure;
+ GValue *value;
+ GValue *list;
+ gboolean valid_layer = FALSE;
+ gboolean mono, stereo;
+
+ GST_LOG_OBJECT(self, "parsing mpeg caps");
+
+ structure = gst_structure_empty_new("audio/mpeg");
+ value = g_new0(GValue, 1);
+ g_value_init(value, G_TYPE_INT);
+
+ list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+ g_value_set_int(value, 1);
+ gst_value_list_prepend_value(list, value);
+ g_value_set_int(value, 2);
+ gst_value_list_prepend_value(list, value);
+ gst_structure_set_value(structure, "mpegversion", list);
+ g_free(list);
+
+ /* layer */
+ GST_LOG_OBJECT(self, "setting mpeg layer");
+ list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+ if (mpeg->layer & BT_MPEG_LAYER_1) {
+ g_value_set_int(value, 1);
+ gst_value_list_prepend_value(list, value);
+ valid_layer = TRUE;
+ }
+ if (mpeg->layer & BT_MPEG_LAYER_2) {
+ g_value_set_int(value, 2);
+ gst_value_list_prepend_value(list, value);
+ valid_layer = TRUE;
+ }
+ if (mpeg->layer & BT_MPEG_LAYER_3) {
+ g_value_set_int(value, 3);
+ gst_value_list_prepend_value(list, value);
+ valid_layer = TRUE;
+ }
+ if (list) {
+ gst_structure_set_value(structure, "layer", list);
+ g_free(list);
+ list = NULL;
+ }
+
+ if (!valid_layer) {
+ gst_structure_free(structure);
+ g_free(value);
+ return NULL;
+ }
+
+ /* rate */
+ GST_LOG_OBJECT(self, "setting mpeg rate");
+ list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+ if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_48000) {
+ g_value_set_int(value, 48000);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_44100) {
+ g_value_set_int(value, 44100);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_32000) {
+ g_value_set_int(value, 32000);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_24000) {
+ g_value_set_int(value, 24000);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_22050) {
+ g_value_set_int(value, 22050);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_16000) {
+ g_value_set_int(value, 16000);
+ gst_value_list_prepend_value(list, value);
+ }
+ g_value_unset(value);
+ if (list) {
+ gst_structure_set_value(structure, "rate", list);
+ g_free(list);
+ list = NULL;
+ }
+
+ /* channels */
+ GST_LOG_OBJECT(self, "setting mpeg channels");
+ mono = FALSE;
+ stereo = FALSE;
+ if (mpeg->channel_mode & BT_A2DP_CHANNEL_MODE_MONO)
+ mono = TRUE;
+ if ((mpeg->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) ||
+ (mpeg->channel_mode &
+ BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) ||
+ (mpeg->channel_mode &
+ BT_A2DP_CHANNEL_MODE_JOINT_STEREO))
+ stereo = TRUE;
+
+ if (mono && stereo) {
+ g_value_init(value, GST_TYPE_INT_RANGE);
+ gst_value_set_int_range(value, 1, 2);
+ } else {
+ g_value_init(value, G_TYPE_INT);
+ if (mono)
+ g_value_set_int(value, 1);
+ else if (stereo)
+ g_value_set_int(value, 2);
+ else {
+ GST_ERROR_OBJECT(self,
+ "Unexpected number of channels");
+ g_value_set_int(value, 0);
+ }
+ }
+ gst_structure_set_value(structure, "channels", value);
+ g_free(value);
+
+ return structure;
+}
+
+static gboolean gst_avdtp_sink_update_config(GstAvdtpSink *self)
+{
+ GstStructure *structure;
+ gchar *tmp;
+
+ switch (self->data->codec) {
+ case A2DP_CODEC_SBC:
+ structure = gst_avdtp_sink_parse_sbc_raw(self);
+ break;
+ case A2DP_CODEC_MPEG12:
+ structure = gst_avdtp_sink_parse_mpeg_raw(self);
+ break;
+ default:
+ GST_ERROR_OBJECT(self, "Unsupported configuration");
+ return FALSE;
+ }
+
+ if (structure == NULL)
+ return FALSE;
+
+ if (self->dev_caps != NULL)
+ gst_caps_unref(self->dev_caps);
+
+ self->dev_caps = gst_caps_new_full(structure, NULL);
+
+ tmp = gst_caps_to_string(self->dev_caps);
+ GST_DEBUG_OBJECT(self, "Transport configuration: %s", tmp);
+ g_free(tmp);
+
+ return TRUE;
+}
+
+static gboolean gst_avdtp_sink_update_caps(GstAvdtpSink *self)
+{
+ sbc_capabilities_t *sbc;
+ mpeg_capabilities_t *mpeg;
+ GstStructure *sbc_structure;
+ GstStructure *mpeg_structure;
+ gchar *tmp;
+
+ GST_LOG_OBJECT(self, "updating device caps");
+
+ if (self->data->config_size != 0 && self->data->config != NULL)
+ return gst_avdtp_sink_update_config(self);
+
+ sbc = (void *) gst_avdtp_find_caps(self, BT_A2DP_SBC_SINK);
+ mpeg = (void *) gst_avdtp_find_caps(self, BT_A2DP_MPEG12_SINK);
+
+ sbc_structure = gst_avdtp_sink_parse_sbc_caps(self, sbc);
+ mpeg_structure = gst_avdtp_sink_parse_mpeg_caps(self, mpeg);
+
+ if (self->dev_caps != NULL)
+ gst_caps_unref(self->dev_caps);
+ self->dev_caps = gst_caps_new_full(sbc_structure, NULL);
+ if (mpeg_structure != NULL)
+ gst_caps_append_structure(self->dev_caps, mpeg_structure);
+
+ tmp = gst_caps_to_string(self->dev_caps);
+ GST_DEBUG_OBJECT(self, "Device capabilities: %s", tmp);
+ g_free(tmp);
+
+ return TRUE;
+}
+
+static gboolean gst_avdtp_sink_get_capabilities(GstAvdtpSink *self)
+{
+ gchar *buf[BT_SUGGESTED_BUFFER_SIZE];
+ struct bt_get_capabilities_req *req = (void *) buf;
+ struct bt_get_capabilities_rsp *rsp = (void *) buf;
+ int err;
+
+ memset(req, 0, BT_SUGGESTED_BUFFER_SIZE);
+
+ req->h.type = BT_REQUEST;
+ req->h.name = BT_GET_CAPABILITIES;
+ req->h.length = sizeof(*req);
+
+ if (self->device == NULL)
+ return FALSE;
+ strncpy(req->destination, self->device, 18);
+ if (self->autoconnect)
+ req->flags |= BT_FLAG_AUTOCONNECT;
+
+ err = gst_avdtp_sink_audioservice_send(self, &req->h);
+ if (err < 0) {
+ GST_ERROR_OBJECT(self, "Error while asking device caps");
+ return FALSE;
+ }
+
+ rsp->h.length = 0;
+ err = gst_avdtp_sink_audioservice_expect(self,
+ &rsp->h, BT_GET_CAPABILITIES);
+ if (err < 0) {
+ GST_ERROR_OBJECT(self, "Error while getting device caps");
+ return FALSE;
+ }
+
+ self->data->caps = g_malloc0(rsp->h.length);
+ memcpy(self->data->caps, rsp, rsp->h.length);
+ if (!gst_avdtp_sink_update_caps(self)) {
+ GST_WARNING_OBJECT(self, "failed to update capabilities");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gint gst_avdtp_sink_get_channel_mode(const gchar *mode)
+{
+ if (strcmp(mode, "stereo") == 0)
+ return BT_A2DP_CHANNEL_MODE_STEREO;
+ else if (strcmp(mode, "joint-stereo") == 0)
+ return BT_A2DP_CHANNEL_MODE_JOINT_STEREO;
+ else if (strcmp(mode, "dual-channel") == 0)
+ return BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL;
+ else if (strcmp(mode, "mono") == 0)
+ return BT_A2DP_CHANNEL_MODE_MONO;
+ else
+ return -1;
+}
+
+static void gst_avdtp_sink_tag(const GstTagList *taglist,
+ const gchar *tag, gpointer user_data)
+{
+ gboolean crc;
+ gchar *channel_mode = NULL;
+ GstAvdtpSink *self = GST_AVDTP_SINK(user_data);
+
+ if (strcmp(tag, "has-crc") == 0) {
+
+ if (!gst_tag_list_get_boolean(taglist, tag, &crc)) {
+ GST_WARNING_OBJECT(self, "failed to get crc tag");
+ return;
+ }
+
+ gst_avdtp_sink_set_crc(self, crc);
+
+ } else if (strcmp(tag, "channel-mode") == 0) {
+
+ if (!gst_tag_list_get_string(taglist, tag, &channel_mode)) {
+ GST_WARNING_OBJECT(self,
+ "failed to get channel-mode tag");
+ return;
+ }
+
+ self->channel_mode = gst_avdtp_sink_get_channel_mode(
+ channel_mode);
+ if (self->channel_mode == -1)
+ GST_WARNING_OBJECT(self, "Received invalid channel "
+ "mode: %s", channel_mode);
+ g_free(channel_mode);
+
+ } else
+ GST_DEBUG_OBJECT(self, "received unused tag: %s", tag);
+}
+
+static gboolean gst_avdtp_sink_event(GstBaseSink *basesink,
+ GstEvent *event)
+{
+ GstAvdtpSink *self = GST_AVDTP_SINK(basesink);
+ GstTagList *taglist = NULL;
+
+ if (GST_EVENT_TYPE(event) == GST_EVENT_TAG) {
+ /* we check the tags, mp3 has tags that are importants and
+ * are outside caps */
+ gst_event_parse_tag(event, &taglist);
+ gst_tag_list_foreach(taglist, gst_avdtp_sink_tag, self);
+ }
+
+ return TRUE;
+}
+
+static gboolean gst_avdtp_sink_transport_parse_property(GstAvdtpSink *self,
+ DBusMessageIter *i)
+{
+ const char *key;
+ DBusMessageIter variant_i;
+
+ if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) {
+ GST_ERROR_OBJECT(self, "Property name not a string.");
+ return FALSE;
+ }
+
+ dbus_message_iter_get_basic(i, &key);
+
+ if (!dbus_message_iter_next(i)) {
+ GST_ERROR_OBJECT(self, "Property value missing");
+ return FALSE;
+ }
+
+ if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_VARIANT) {
+ GST_ERROR_OBJECT(self, "Property value not a variant.");
+ return FALSE;
+ }
+
+ dbus_message_iter_recurse(i, &variant_i);
+
+ switch (dbus_message_iter_get_arg_type(&variant_i)) {
+ case DBUS_TYPE_BYTE: {
+ uint8_t value;
+ dbus_message_iter_get_basic(&variant_i, &value);
+
+ if (g_str_equal(key, "Codec") == TRUE)
+ self->data->codec = value;
+
+ break;
+ }
+ case DBUS_TYPE_STRING: {
+ const char *value;
+ dbus_message_iter_get_basic(&variant_i, &value);
+
+ if (g_str_equal(key, "UUID") == TRUE) {
+ g_free(self->data->uuid);
+ self->data->uuid = g_strdup(value);
+ }
+
+ break;
+ }
+ case DBUS_TYPE_ARRAY: {
+ DBusMessageIter array_i;
+ char *value;
+ int size;
+
+ dbus_message_iter_recurse(&variant_i, &array_i);
+ dbus_message_iter_get_fixed_array(&array_i, &value, &size);
+
+ if (g_str_equal(key, "Configuration")) {
+ g_free(self->data->config);
+ self->data->config = g_new0(guint8, size);
+ self->data->config_size = size;
+ memcpy(self->data->config, value, size);
+ }
+
+ break;
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean gst_avdtp_sink_transport_acquire(GstAvdtpSink *self)
+{
+ DBusMessage *msg, *reply;
+ DBusError err;
+ const char *access_type = "w";
+ int fd;
+ uint16_t imtu, omtu;
+
+ dbus_error_init(&err);
+
+ if (self->data->conn == NULL)
+ self->data->conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
+
+ msg = dbus_message_new_method_call("org.bluez", self->transport,
+ "org.bluez.MediaTransport",
+ "Acquire");
+
+ dbus_message_append_args(msg, DBUS_TYPE_STRING, &access_type,
+ DBUS_TYPE_INVALID);
+
+ reply = dbus_connection_send_with_reply_and_block(self->data->conn,
+ msg, -1, &err);
+
+ if (dbus_error_is_set(&err))
+ goto fail;
+
+ if (dbus_message_get_args(reply, &err, DBUS_TYPE_UNIX_FD, &fd,
+ DBUS_TYPE_UINT16, &imtu,
+ DBUS_TYPE_UINT16, &omtu,
+ DBUS_TYPE_INVALID) == FALSE)
+ goto fail;
+
+ dbus_message_unref(reply);
+
+ self->stream = g_io_channel_unix_new(fd);
+ g_io_channel_set_encoding(self->stream, NULL, NULL);
+ g_io_channel_set_close_on_unref(self->stream, TRUE);
+ self->data->link_mtu = omtu;
+ GST_DEBUG_OBJECT(self, "stream_fd=%d mtu=%d", fd, omtu);
+
+ return TRUE;
+
+fail:
+ GST_ERROR_OBJECT(self, "Failed to acquire transport stream: %s",
+ err.message);
+
+ dbus_error_free(&err);
+
+ if (reply)
+ dbus_message_unref(msg);
+
+ return FALSE;
+}
+
+static gboolean gst_avdtp_sink_transport_get_properties(GstAvdtpSink *self)
+{
+ DBusMessage *msg, *reply;
+ DBusMessageIter arg_i, ele_i;
+ DBusError err;
+
+ dbus_error_init(&err);
+
+ /* Transport need to be acquire first to make sure the MTUs are
+ available */
+ if (gst_avdtp_sink_transport_acquire(self) == FALSE)
+ return FALSE;
+
+ msg = dbus_message_new_method_call("org.bluez", self->transport,
+ "org.bluez.MediaTransport",
+ "GetProperties");
+ reply = dbus_connection_send_with_reply_and_block(self->data->conn,
+ msg, -1, &err);
+
+ if (dbus_error_is_set(&err) || reply == NULL) {
+ GST_ERROR_OBJECT(self, "Failed to get transport properties: %s",
+ err.message);
+ goto fail;
+ }
+
+ if (!dbus_message_iter_init(reply, &arg_i)) {
+ GST_ERROR_OBJECT(self, "GetProperties reply has no arguments.");
+ goto fail;
+ }
+
+ if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) {
+ GST_ERROR_OBJECT(self, "GetProperties argument is not an array.");
+ goto fail;
+ }
+
+ dbus_message_iter_recurse(&arg_i, &ele_i);
+ while (dbus_message_iter_get_arg_type(&ele_i) != DBUS_TYPE_INVALID) {
+
+ if (dbus_message_iter_get_arg_type(&ele_i) ==
+ DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter dict_i;
+
+ dbus_message_iter_recurse(&ele_i, &dict_i);
+
+ gst_avdtp_sink_transport_parse_property(self, &dict_i);
+ }
+
+ if (!dbus_message_iter_next(&ele_i))
+ break;
+ }
+
+ return gst_avdtp_sink_update_caps(self);
+
+fail:
+ dbus_message_unref(msg);
+ dbus_message_unref(reply);
+ return FALSE;
+
+}
+
+static gboolean gst_avdtp_sink_start(GstBaseSink *basesink)
+{
+ GstAvdtpSink *self = GST_AVDTP_SINK(basesink);
+ gint sk;
+ gint err;
+
+ GST_INFO_OBJECT(self, "start");
+
+ self->data = g_new0(struct bluetooth_data, 1);
+
+ self->stream = NULL;
+ self->stream_caps = NULL;
+ self->mp3_using_crc = -1;
+ self->channel_mode = -1;
+
+ if (self->transport != NULL)
+ return gst_avdtp_sink_transport_get_properties(self);
+
+ self->watch_id = 0;
+
+ sk = bt_audio_service_open();
+ if (sk <= 0) {
+ err = errno;
+ GST_ERROR_OBJECT(self, "Cannot open connection to bt "
+ "audio service: %s %d", strerror(err), err);
+ goto failed;
+ }
+
+ self->server = g_io_channel_unix_new(sk);
+ g_io_channel_set_encoding(self->server, NULL, NULL);
+ self->watch_id = g_io_add_watch(self->server, G_IO_HUP | G_IO_ERR |
+ G_IO_NVAL, server_callback, self);
+
+ if (!gst_avdtp_sink_get_capabilities(self)) {
+ GST_ERROR_OBJECT(self, "failed to get capabilities "
+ "from device");
+ goto failed;
+ }
+
+ return TRUE;
+
+failed:
+ bt_audio_service_close(sk);
+ return FALSE;
+}
+
+static gboolean gst_avdtp_sink_stream_start(GstAvdtpSink *self)
+{
+ gchar buf[BT_SUGGESTED_BUFFER_SIZE];
+ struct bt_start_stream_req *req = (void *) buf;
+ struct bt_start_stream_rsp *rsp = (void *) buf;
+ struct bt_new_stream_ind *ind = (void *) buf;
+ int err;
+
+ if (self->transport != NULL)
+ return gst_avdtp_sink_conf_recv_stream_fd(self);
+
+ memset(req, 0, sizeof(buf));
+ req->h.type = BT_REQUEST;
+ req->h.name = BT_START_STREAM;
+ req->h.length = sizeof(*req);
+
+ err = gst_avdtp_sink_audioservice_send(self, &req->h);
+ if (err < 0) {
+ GST_ERROR_OBJECT(self, "Error ocurred while sending "
+ "start packet");
+ return FALSE;
+ }
+
+ rsp->h.length = sizeof(*rsp);
+ err = gst_avdtp_sink_audioservice_expect(self, &rsp->h,
+ BT_START_STREAM);
+ if (err < 0) {
+ GST_ERROR_OBJECT(self, "Error while stream "
+ "start confirmation");
+ return FALSE;
+ }
+
+ ind->h.length = sizeof(*ind);
+ err = gst_avdtp_sink_audioservice_expect(self, &ind->h,
+ BT_NEW_STREAM);
+ if (err < 0) {
+ GST_ERROR_OBJECT(self, "Error while receiving "
+ "stream filedescriptor");
+ return FALSE;
+ }
+
+ if (!gst_avdtp_sink_conf_recv_stream_fd(self))
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean gst_avdtp_sink_init_mp3_pkt_conf(
+ GstAvdtpSink *self, GstCaps *caps,
+ mpeg_capabilities_t *pkt)
+{
+ const GValue *value = NULL;
+ gint rate, layer;
+ const gchar *name;
+ GstStructure *structure = gst_caps_get_structure(caps, 0);
+
+ name = gst_structure_get_name(structure);
+
+ if (!(IS_MPEG_AUDIO(name))) {
+ GST_ERROR_OBJECT(self, "Unexpected format %s, "
+ "was expecting mp3", name);
+ return FALSE;
+ }
+
+ /* layer */
+ value = gst_structure_get_value(structure, "layer");
+ layer = g_value_get_int(value);
+ if (layer == 1)
+ pkt->layer = BT_MPEG_LAYER_1;
+ else if (layer == 2)
+ pkt->layer = BT_MPEG_LAYER_2;
+ else if (layer == 3)
+ pkt->layer = BT_MPEG_LAYER_3;
+ else {
+ GST_ERROR_OBJECT(self, "Unexpected layer: %d", layer);
+ return FALSE;
+ }
+
+ /* crc */
+ if (self->mp3_using_crc != -1)
+ pkt->crc = self->mp3_using_crc;
+ else {
+ GST_ERROR_OBJECT(self, "No info about crc was received, "
+ " can't proceed");
+ return FALSE;
+ }
+
+ /* channel mode */
+ if (self->channel_mode != -1)
+ pkt->channel_mode = self->channel_mode;
+ else {
+ GST_ERROR_OBJECT(self, "No info about channel mode "
+ "received, can't proceed");
+ return FALSE;
+ }
+
+ /* mpf - we will only use the mandatory one */
+ pkt->mpf = 0;
+
+ value = gst_structure_get_value(structure, "rate");
+ rate = g_value_get_int(value);
+ if (rate == 44100)
+ pkt->frequency = BT_MPEG_SAMPLING_FREQ_44100;
+ else if (rate == 48000)
+ pkt->frequency = BT_MPEG_SAMPLING_FREQ_48000;
+ else if (rate == 32000)
+ pkt->frequency = BT_MPEG_SAMPLING_FREQ_32000;
+ else if (rate == 24000)
+ pkt->frequency = BT_MPEG_SAMPLING_FREQ_24000;
+ else if (rate == 22050)
+ pkt->frequency = BT_MPEG_SAMPLING_FREQ_22050;
+ else if (rate == 16000)
+ pkt->frequency = BT_MPEG_SAMPLING_FREQ_16000;
+ else {
+ GST_ERROR_OBJECT(self, "Invalid rate while setting caps");
+ return FALSE;
+ }
+
+ /* vbr - we always say its vbr, we don't have how to know it */
+ pkt->bitrate = 0x8000;
+
+ return TRUE;
+}
+
+static gboolean gst_avdtp_sink_configure(GstAvdtpSink *self,
+ GstCaps *caps)
+{
+ gchar buf[BT_SUGGESTED_BUFFER_SIZE];
+ struct bt_open_req *open_req = (void *) buf;
+ struct bt_open_rsp *open_rsp = (void *) buf;
+ struct bt_set_configuration_req *req = (void *) buf;
+ struct bt_set_configuration_rsp *rsp = (void *) buf;
+ gboolean ret;
+ gchar *temp;
+ GstStructure *structure;
+ codec_capabilities_t *codec = NULL;
+ int err;
+
+ temp = gst_caps_to_string(caps);
+ GST_DEBUG_OBJECT(self, "configuring device with caps: %s", temp);
+ g_free(temp);
+
+ /* Transport already configured */
+ if (self->transport != NULL)
+ return TRUE;
+
+ structure = gst_caps_get_structure(caps, 0);
+
+ if (gst_structure_has_name(structure, "audio/x-sbc"))
+ codec = (void *) gst_avdtp_find_caps(self, BT_A2DP_SBC_SINK);
+ else if (gst_structure_has_name(structure, "audio/mpeg"))
+ codec = (void *) gst_avdtp_find_caps(self, BT_A2DP_MPEG12_SINK);
+
+ if (codec == NULL) {
+ GST_ERROR_OBJECT(self, "Couldn't parse caps "
+ "to packet configuration");
+ return FALSE;
+ }
+
+ memset(req, 0, BT_SUGGESTED_BUFFER_SIZE);
+ open_req->h.type = BT_REQUEST;
+ open_req->h.name = BT_OPEN;
+ open_req->h.length = sizeof(*open_req);
+
+ strncpy(open_req->destination, self->device, 18);
+ open_req->seid = codec->seid;
+ open_req->lock = BT_WRITE_LOCK;
+
+ err = gst_avdtp_sink_audioservice_send(self, &open_req->h);
+ if (err < 0) {
+ GST_ERROR_OBJECT(self, "Error ocurred while sending "
+ "open packet");
+ return FALSE;
+ }
+
+ open_rsp->h.length = sizeof(*open_rsp);
+ err = gst_avdtp_sink_audioservice_expect(self, &open_rsp->h,
+ BT_OPEN);
+ if (err < 0) {
+ GST_ERROR_OBJECT(self, "Error while receiving device "
+ "confirmation");
+ return FALSE;
+ }
+
+ memset(req, 0, sizeof(buf));
+ req->h.type = BT_REQUEST;
+ req->h.name = BT_SET_CONFIGURATION;
+ req->h.length = sizeof(*req);
+ memcpy(&req->codec, codec, sizeof(req->codec));
+
+ if (codec->type == BT_A2DP_SBC_SINK)
+ ret = gst_avdtp_sink_init_sbc_pkt_conf(self, caps,
+ (void *) &req->codec);
+ else
+ ret = gst_avdtp_sink_init_mp3_pkt_conf(self, caps,
+ (void *) &req->codec);
+
+ if (!ret) {
+ GST_ERROR_OBJECT(self, "Couldn't parse caps "
+ "to packet configuration");
+ return FALSE;
+ }
+
+ req->h.length += req->codec.length - sizeof(req->codec);
+ err = gst_avdtp_sink_audioservice_send(self, &req->h);
+ if (err < 0) {
+ GST_ERROR_OBJECT(self, "Error ocurred while sending "
+ "configurarion packet");
+ return FALSE;
+ }
+
+ rsp->h.length = sizeof(*rsp);
+ err = gst_avdtp_sink_audioservice_expect(self, &rsp->h,
+ BT_SET_CONFIGURATION);
+ if (err < 0) {
+ GST_ERROR_OBJECT(self, "Error while receiving device "
+ "confirmation");
+ return FALSE;
+ }
+
+ self->data->link_mtu = rsp->link_mtu;
+
+ return TRUE;
+}
+
+static GstFlowReturn gst_avdtp_sink_preroll(GstBaseSink *basesink,
+ GstBuffer *buffer)
+{
+ GstAvdtpSink *sink = GST_AVDTP_SINK(basesink);
+ gboolean ret;
+
+ GST_AVDTP_SINK_MUTEX_LOCK(sink);
+
+ ret = gst_avdtp_sink_stream_start(sink);
+
+ GST_AVDTP_SINK_MUTEX_UNLOCK(sink);
+
+ if (!ret)
+ return GST_FLOW_ERROR;
+
+ return GST_FLOW_OK;
+}
+
+static GstFlowReturn gst_avdtp_sink_render(GstBaseSink *basesink,
+ GstBuffer *buffer)
+{
+ GstAvdtpSink *self = GST_AVDTP_SINK(basesink);
+ ssize_t ret;
+ int fd;
+
+ fd = g_io_channel_unix_get_fd(self->stream);
+
+ ret = write(fd, GST_BUFFER_DATA(buffer), GST_BUFFER_SIZE(buffer));
+ if (ret < 0) {
+ GST_ERROR_OBJECT(self, "Error while writting to socket: %s",
+ strerror(errno));
+ return GST_FLOW_ERROR;
+ }
+
+ return GST_FLOW_OK;
+}
+
+static gboolean gst_avdtp_sink_unlock(GstBaseSink *basesink)
+{
+ GstAvdtpSink *self = GST_AVDTP_SINK(basesink);
+
+ if (self->stream != NULL)
+ g_io_channel_flush(self->stream, NULL);
+
+ return TRUE;
+}
+
+static GstFlowReturn gst_avdtp_sink_buffer_alloc(GstBaseSink *basesink,
+ guint64 offset, guint size, GstCaps *caps,
+ GstBuffer **buf)
+{
+ GstAvdtpSink *self = GST_AVDTP_SINK(basesink);
+
+ *buf = gst_buffer_new_and_alloc(size);
+ if (!(*buf)) {
+ GST_ERROR_OBJECT(self, "buffer allocation failed");
+ return GST_FLOW_ERROR;
+ }
+
+ gst_buffer_set_caps(*buf, caps);
+
+ GST_BUFFER_OFFSET(*buf) = offset;
+
+ return GST_FLOW_OK;
+}
+
+static void gst_avdtp_sink_class_init(GstAvdtpSinkClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+ GstBaseSinkClass *basesink_class = GST_BASE_SINK_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ object_class->finalize = GST_DEBUG_FUNCPTR(
+ gst_avdtp_sink_finalize);
+ object_class->set_property = GST_DEBUG_FUNCPTR(
+ gst_avdtp_sink_set_property);
+ object_class->get_property = GST_DEBUG_FUNCPTR(
+ gst_avdtp_sink_get_property);
+
+ basesink_class->start = GST_DEBUG_FUNCPTR(gst_avdtp_sink_start);
+ basesink_class->stop = GST_DEBUG_FUNCPTR(gst_avdtp_sink_stop);
+ basesink_class->render = GST_DEBUG_FUNCPTR(
+ gst_avdtp_sink_render);
+ basesink_class->preroll = GST_DEBUG_FUNCPTR(
+ gst_avdtp_sink_preroll);
+ basesink_class->unlock = GST_DEBUG_FUNCPTR(
+ gst_avdtp_sink_unlock);
+ basesink_class->event = GST_DEBUG_FUNCPTR(
+ gst_avdtp_sink_event);
+
+ basesink_class->buffer_alloc =
+ GST_DEBUG_FUNCPTR(gst_avdtp_sink_buffer_alloc);
+
+ g_object_class_install_property(object_class, PROP_DEVICE,
+ g_param_spec_string("device", "Device",
+ "Bluetooth remote device address",
+ NULL, G_PARAM_READWRITE));
+
+ g_object_class_install_property(object_class, PROP_AUTOCONNECT,
+ g_param_spec_boolean("auto-connect",
+ "Auto-connect",
+ "Automatically attempt to connect "
+ "to device", DEFAULT_AUTOCONNECT,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property(object_class, PROP_TRANSPORT,
+ g_param_spec_string("transport",
+ "Transport",
+ "Use configured transport",
+ NULL, G_PARAM_READWRITE));
+
+ GST_DEBUG_CATEGORY_INIT(avdtp_sink_debug, "avdtpsink", 0,
+ "A2DP headset sink element");
+}
+
+static void gst_avdtp_sink_init(GstAvdtpSink *self,
+ GstAvdtpSinkClass *klass)
+{
+ self->device = NULL;
+ self->transport = NULL;
+ self->data = NULL;
+
+ self->stream = NULL;
+
+ self->dev_caps = NULL;
+
+ self->autoconnect = DEFAULT_AUTOCONNECT;
+
+ self->sink_lock = g_mutex_new();
+
+ /* FIXME this is for not synchronizing with clock, should be tested
+ * with devices to see the behaviour
+ gst_base_sink_set_sync(GST_BASE_SINK(self), FALSE);
+ */
+}
+
+static int gst_avdtp_sink_audioservice_send(GstAvdtpSink *self,
+ const bt_audio_msg_header_t *msg)
+{
+ ssize_t written;
+ const char *type, *name;
+ uint16_t length;
+ int fd;
+
+ length = msg->length ? msg->length : BT_SUGGESTED_BUFFER_SIZE;
+
+ fd = g_io_channel_unix_get_fd(self->server);
+
+ written = write(fd, msg, length);
+ if (written < 0) {
+ GST_ERROR_OBJECT(self, "Error sending data to audio service:"
+ " %s", strerror(errno));
+ return -errno;
+ }
+
+ type = bt_audio_strtype(msg->type);
+ name = bt_audio_strname(msg->name);
+
+ GST_DEBUG_OBJECT(self, "sent: %s -> %s", type, name);
+
+ return 0;
+}
+
+static int gst_avdtp_sink_audioservice_recv(GstAvdtpSink *self,
+ bt_audio_msg_header_t *inmsg)
+{
+ ssize_t bytes_read;
+ const char *type, *name;
+ uint16_t length;
+ int fd, err;
+
+ length = inmsg->length ? inmsg->length : BT_SUGGESTED_BUFFER_SIZE;
+
+ fd = g_io_channel_unix_get_fd(self->server);
+
+ bytes_read = read(fd, inmsg, length);
+ if (bytes_read < 0) {
+ GST_ERROR_OBJECT(self, "Error receiving data from "
+ "audio service: %s", strerror(errno));
+ return -errno;
+ }
+
+ type = bt_audio_strtype(inmsg->type);
+ if (!type) {
+ err = -EINVAL;
+ GST_ERROR_OBJECT(self, "Bogus message type %d "
+ "received from audio service",
+ inmsg->type);
+ }
+
+ name = bt_audio_strname(inmsg->name);
+ if (!name) {
+ err = -EINVAL;
+ GST_ERROR_OBJECT(self, "Bogus message name %d "
+ "received from audio service",
+ inmsg->name);
+ }
+
+ if (inmsg->type == BT_ERROR) {
+ bt_audio_error_t *msg = (void *) inmsg;
+ err = -EINVAL;
+ GST_ERROR_OBJECT(self, "%s failed : "
+ "%s(%d)",
+ name,
+ strerror(msg->posix_errno),
+ msg->posix_errno);
+ }
+
+ GST_DEBUG_OBJECT(self, "received: %s <- %s", type, name);
+
+ return err;
+}
+
+static int gst_avdtp_sink_audioservice_expect(GstAvdtpSink *self,
+ bt_audio_msg_header_t *outmsg,
+ guint8 expected_name)
+{
+ int err;
+
+ err = gst_avdtp_sink_audioservice_recv(self, outmsg);
+ if (err < 0)
+ return err;
+
+ if (outmsg->name != expected_name)
+ return -EINVAL;
+
+ return 0;
+}
+
+gboolean gst_avdtp_sink_plugin_init(GstPlugin *plugin)
+{
+ return gst_element_register(plugin, "avdtpsink", GST_RANK_NONE,
+ GST_TYPE_AVDTP_SINK);
+}
+
+
+/* public functions */
+GstCaps *gst_avdtp_sink_get_device_caps(GstAvdtpSink *sink)
+{
+ if (sink->dev_caps == NULL)
+ return NULL;
+
+ return gst_caps_copy(sink->dev_caps);
+}
+
+gboolean gst_avdtp_sink_set_device_caps(GstAvdtpSink *self,
+ GstCaps *caps)
+{
+ gboolean ret;
+
+ GST_DEBUG_OBJECT(self, "setting device caps");
+ GST_AVDTP_SINK_MUTEX_LOCK(self);
+ ret = gst_avdtp_sink_configure(self, caps);
+
+ if (self->stream_caps)
+ gst_caps_unref(self->stream_caps);
+ self->stream_caps = gst_caps_ref(caps);
+
+ GST_AVDTP_SINK_MUTEX_UNLOCK(self);
+
+ return ret;
+}
+
+guint gst_avdtp_sink_get_link_mtu(GstAvdtpSink *sink)
+{
+ return sink->data->link_mtu;
+}
+
+void gst_avdtp_sink_set_device(GstAvdtpSink *self, const gchar *dev)
+{
+ if (self->device != NULL)
+ g_free(self->device);
+
+ GST_LOG_OBJECT(self, "Setting device: %s", dev);
+ self->device = g_strdup(dev);
+}
+
+void gst_avdtp_sink_set_transport(GstAvdtpSink *self, const gchar *trans)
+{
+ if (self->transport != NULL)
+ g_free(self->transport);
+
+ GST_LOG_OBJECT(self, "Setting transport: %s", trans);
+ self->transport = g_strdup(trans);
+}
+
+gchar *gst_avdtp_sink_get_device(GstAvdtpSink *self)
+{
+ return g_strdup(self->device);
+}
+
+gchar *gst_avdtp_sink_get_transport(GstAvdtpSink *self)
+{
+ return g_strdup(self->transport);
+}
+
+void gst_avdtp_sink_set_crc(GstAvdtpSink *self, gboolean crc)
+{
+ gint new_crc;
+
+ new_crc = crc ? CRC_PROTECTED : CRC_UNPROTECTED;
+
+ /* test if we already received a different crc */
+ if (self->mp3_using_crc != -1 && new_crc != self->mp3_using_crc) {
+ GST_WARNING_OBJECT(self, "crc changed during stream");
+ return;
+ }
+ self->mp3_using_crc = new_crc;
+
+}
+
+void gst_avdtp_sink_set_channel_mode(GstAvdtpSink *self,
+ const gchar *mode)
+{
+ gint new_mode;
+
+ new_mode = gst_avdtp_sink_get_channel_mode(mode);
+
+ if (self->channel_mode != -1 && new_mode != self->channel_mode) {
+ GST_WARNING_OBJECT(self, "channel mode changed during stream");
+ return;
+ }
+
+ self->channel_mode = new_mode;
+ if (self->channel_mode == -1)
+ GST_WARNING_OBJECT(self, "Received invalid channel "
+ "mode: %s", mode);
+}
diff --git a/audio/gstavdtpsink.h b/audio/gstavdtpsink.h
new file mode 100644
index 0000000..c4e5645
--- /dev/null
+++ b/audio/gstavdtpsink.h
@@ -0,0 +1,107 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef __GST_AVDTP_SINK_H
+#define __GST_AVDTP_SINK_H
+
+#include <gst/gst.h>
+#include <gst/base/gstbasesink.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_AVDTP_SINK \
+ (gst_avdtp_sink_get_type())
+#define GST_AVDTP_SINK(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AVDTP_SINK,\
+ GstAvdtpSink))
+#define GST_AVDTP_SINK_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AVDTP_SINK,\
+ GstAvdtpSinkClass))
+#define GST_IS_AVDTP_SINK(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AVDTP_SINK))
+#define GST_IS_AVDTP_SINK_CLASS(obj) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AVDTP_SINK))
+
+typedef struct _GstAvdtpSink GstAvdtpSink;
+typedef struct _GstAvdtpSinkClass GstAvdtpSinkClass;
+
+struct bluetooth_data;
+
+struct _GstAvdtpSink {
+ GstBaseSink sink;
+
+ gchar *device;
+ gchar *transport;
+ GIOChannel *stream;
+
+ struct bluetooth_data *data;
+ gboolean autoconnect;
+ GIOChannel *server;
+
+ /* mp3 stream data (outside caps data)*/
+ gint mp3_using_crc;
+ gint channel_mode;
+
+ /* stream connection data */
+ GstCaps *stream_caps;
+
+ GstCaps *dev_caps;
+
+ GMutex *sink_lock;
+
+ guint watch_id;
+};
+
+struct _GstAvdtpSinkClass {
+ GstBaseSinkClass parent_class;
+};
+
+GType gst_avdtp_sink_get_type(void);
+
+GstCaps *gst_avdtp_sink_get_device_caps(GstAvdtpSink *sink);
+gboolean gst_avdtp_sink_set_device_caps(GstAvdtpSink *sink,
+ GstCaps *caps);
+
+guint gst_avdtp_sink_get_link_mtu(GstAvdtpSink *sink);
+
+void gst_avdtp_sink_set_device(GstAvdtpSink *sink,
+ const gchar* device);
+
+void gst_avdtp_sink_set_transport(GstAvdtpSink *sink,
+ const gchar *transport);
+
+gchar *gst_avdtp_sink_get_device(GstAvdtpSink *sink);
+
+gchar *gst_avdtp_sink_get_transport(GstAvdtpSink *sink);
+
+gboolean gst_avdtp_sink_plugin_init(GstPlugin *plugin);
+
+void gst_avdtp_sink_set_crc(GstAvdtpSink *self, gboolean crc);
+
+void gst_avdtp_sink_set_channel_mode(GstAvdtpSink *self,
+ const gchar *mode);
+
+
+G_END_DECLS
+
+#endif /* __GST_AVDTP_SINK_H */
diff --git a/audio/gstbluetooth.c b/audio/gstbluetooth.c
new file mode 100644
index 0000000..9930820
--- /dev/null
+++ b/audio/gstbluetooth.c
@@ -0,0 +1,109 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 <string.h>
+
+#include <gst/gst.h>
+
+#include "gstsbcutil.h"
+#include <sbc.h>
+
+#include "gstsbcenc.h"
+#include "gstsbcdec.h"
+#include "gstsbcparse.h"
+#include "gstavdtpsink.h"
+#include "gsta2dpsink.h"
+#include "gstrtpsbcpay.h"
+
+static GstStaticCaps sbc_caps = GST_STATIC_CAPS("audio/x-sbc");
+
+#define SBC_CAPS (gst_static_caps_get(&sbc_caps))
+
+static void sbc_typefind(GstTypeFind *tf, gpointer ignore)
+{
+ GstCaps *caps;
+ guint8 *aux;
+ sbc_t sbc;
+ guint8 *data = gst_type_find_peek(tf, 0, 32);
+
+ if (data == NULL)
+ return;
+
+ if (sbc_init(&sbc, 0) < 0)
+ return;
+
+ aux = g_new(guint8, 32);
+ memcpy(aux, data, 32);
+ if (sbc_parse(&sbc, aux, 32) < 0)
+ goto done;
+
+ caps = gst_sbc_parse_caps_from_sbc(&sbc);
+ gst_type_find_suggest(tf, GST_TYPE_FIND_POSSIBLE, caps);
+ gst_caps_unref(caps);
+
+done:
+ g_free(aux);
+ sbc_finish(&sbc);
+}
+
+static gchar *sbc_exts[] = { "sbc", NULL };
+
+static gboolean plugin_init(GstPlugin *plugin)
+{
+ GST_INFO("Bluetooth plugin %s", VERSION);
+
+ if (gst_type_find_register(plugin, "sbc",
+ GST_RANK_PRIMARY, sbc_typefind, sbc_exts,
+ SBC_CAPS, NULL, NULL) == FALSE)
+ return FALSE;
+
+ if (!gst_sbc_enc_plugin_init(plugin))
+ return FALSE;
+
+ if (!gst_sbc_dec_plugin_init(plugin))
+ return FALSE;
+
+ if (!gst_sbc_parse_plugin_init(plugin))
+ return FALSE;
+
+ if (!gst_avdtp_sink_plugin_init(plugin))
+ return FALSE;
+
+ if (!gst_a2dp_sink_plugin_init(plugin))
+ return FALSE;
+
+ if (!gst_rtp_sbc_pay_plugin_init(plugin))
+ return FALSE;
+
+ return TRUE;
+}
+
+extern GstPluginDesc gst_plugin_desc __attribute__ ((visibility("default")));
+
+GST_PLUGIN_DEFINE(GST_VERSION_MAJOR, GST_VERSION_MINOR,
+ "bluetooth", "Bluetooth plugin library",
+ plugin_init, VERSION, "LGPL", "BlueZ", "http://www.bluez.org/")
diff --git a/audio/gstpragma.h b/audio/gstpragma.h
new file mode 100644
index 0000000..626311c
--- /dev/null
+++ b/audio/gstpragma.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+//#pragma GCC diagnostic warning "-Wmissing-declarations"
diff --git a/audio/gstrtpsbcpay.c b/audio/gstrtpsbcpay.c
new file mode 100644
index 0000000..1159bfe
--- /dev/null
+++ b/audio/gstrtpsbcpay.c
@@ -0,0 +1,352 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 "gstpragma.h"
+#include "gstrtpsbcpay.h"
+#include <math.h>
+#include <string.h>
+
+#define RTP_SBC_PAYLOAD_HEADER_SIZE 1
+#define DEFAULT_MIN_FRAMES 0
+#define RTP_SBC_HEADER_TOTAL (12 + RTP_SBC_PAYLOAD_HEADER_SIZE)
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct rtp_payload {
+ guint8 frame_count:4;
+ guint8 rfa0:1;
+ guint8 is_last_fragment:1;
+ guint8 is_first_fragment:1;
+ guint8 is_fragmented:1;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct rtp_payload {
+ guint8 is_fragmented:1;
+ guint8 is_first_fragment:1;
+ guint8 is_last_fragment:1;
+ guint8 rfa0:1;
+ guint8 frame_count:4;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+enum {
+ PROP_0,
+ PROP_MIN_FRAMES
+};
+
+GST_DEBUG_CATEGORY_STATIC(gst_rtp_sbc_pay_debug);
+#define GST_CAT_DEFAULT gst_rtp_sbc_pay_debug
+
+GST_BOILERPLATE(GstRtpSBCPay, gst_rtp_sbc_pay, GstBaseRTPPayload,
+ GST_TYPE_BASE_RTP_PAYLOAD);
+
+static const GstElementDetails gst_rtp_sbc_pay_details =
+ GST_ELEMENT_DETAILS("RTP packet payloader",
+ "Codec/Payloader/Network",
+ "Payload SBC audio as RTP packets",
+ "Thiago Sousa Santos "
+ "<thiagoss@lcc.ufcg.edu.br>");
+
+static GstStaticPadTemplate gst_rtp_sbc_pay_sink_factory =
+ GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
+ GST_STATIC_CAPS("audio/x-sbc, "
+ "rate = (int) { 16000, 32000, 44100, 48000 }, "
+ "channels = (int) [ 1, 2 ], "
+ "mode = (string) { \"mono\", \"dual\", \"stereo\", \"joint\" }, "
+ "blocks = (int) { 4, 8, 12, 16 }, "
+ "subbands = (int) { 4, 8 }, "
+ "allocation = (string) { \"snr\", \"loudness\" }, "
+ "bitpool = (int) [ 2, 64 ]")
+ );
+
+static GstStaticPadTemplate gst_rtp_sbc_pay_src_factory =
+ GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, GST_PAD_ALWAYS,
+ GST_STATIC_CAPS(
+ "application/x-rtp, "
+ "media = (string) \"audio\","
+ "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
+ "clock-rate = (int) { 16000, 32000, 44100, 48000 },"
+ "encoding-name = (string) \"SBC\"")
+ );
+
+static void gst_rtp_sbc_pay_set_property(GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec);
+static void gst_rtp_sbc_pay_get_property(GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec);
+
+static gint gst_rtp_sbc_pay_get_frame_len(gint subbands, gint channels,
+ gint blocks, gint bitpool, const gchar *channel_mode)
+{
+ gint len;
+ gint join;
+
+ len = 4 + (4 * subbands * channels)/8;
+
+ if (strcmp(channel_mode, "mono") == 0 ||
+ strcmp(channel_mode, "dual") == 0)
+ len += ((blocks * channels * bitpool) + 7) / 8;
+ else {
+ join = strcmp(channel_mode, "joint") == 0 ? 1 : 0;
+ len += ((join * subbands + blocks * bitpool) + 7) / 8;
+ }
+
+ return len;
+}
+
+static gboolean gst_rtp_sbc_pay_set_caps(GstBaseRTPPayload *payload,
+ GstCaps *caps)
+{
+ GstRtpSBCPay *sbcpay;
+ gint rate, subbands, channels, blocks, bitpool;
+ gint frame_len;
+ const gchar *channel_mode;
+ GstStructure *structure;
+
+ sbcpay = GST_RTP_SBC_PAY(payload);
+
+ structure = gst_caps_get_structure(caps, 0);
+ if (!gst_structure_get_int(structure, "rate", &rate))
+ return FALSE;
+ if (!gst_structure_get_int(structure, "channels", &channels))
+ return FALSE;
+ if (!gst_structure_get_int(structure, "blocks", &blocks))
+ return FALSE;
+ if (!gst_structure_get_int(structure, "bitpool", &bitpool))
+ return FALSE;
+ if (!gst_structure_get_int(structure, "subbands", &subbands))
+ return FALSE;
+
+ channel_mode = gst_structure_get_string(structure, "mode");
+ if (!channel_mode)
+ return FALSE;
+
+ frame_len = gst_rtp_sbc_pay_get_frame_len(subbands, channels, blocks,
+ bitpool, channel_mode);
+
+ sbcpay->frame_length = frame_len;
+
+ gst_basertppayload_set_options(payload, "audio", TRUE, "SBC", rate);
+
+ GST_DEBUG_OBJECT(payload, "calculated frame length: %d ", frame_len);
+
+ return gst_basertppayload_set_outcaps(payload, NULL);
+}
+
+static GstFlowReturn gst_rtp_sbc_pay_flush_buffers(GstRtpSBCPay *sbcpay)
+{
+ guint available;
+ guint max_payload;
+ GstBuffer *outbuf;
+ guint8 *payload_data;
+ guint frame_count;
+ guint payload_length;
+ struct rtp_payload *payload;
+
+ if (sbcpay->frame_length == 0) {
+ GST_ERROR_OBJECT(sbcpay, "Frame length is 0");
+ return GST_FLOW_ERROR;
+ }
+
+ available = gst_adapter_available(sbcpay->adapter);
+
+ max_payload = gst_rtp_buffer_calc_payload_len(
+ GST_BASE_RTP_PAYLOAD_MTU(sbcpay) - RTP_SBC_PAYLOAD_HEADER_SIZE,
+ 0, 0);
+
+ max_payload = MIN(max_payload, available);
+ frame_count = max_payload / sbcpay->frame_length;
+ payload_length = frame_count * sbcpay->frame_length;
+ if (payload_length == 0) /* Nothing to send */
+ return GST_FLOW_OK;
+
+ outbuf = gst_rtp_buffer_new_allocate(payload_length +
+ RTP_SBC_PAYLOAD_HEADER_SIZE, 0, 0);
+
+ gst_rtp_buffer_set_payload_type(outbuf,
+ GST_BASE_RTP_PAYLOAD_PT(sbcpay));
+
+ payload_data = gst_rtp_buffer_get_payload(outbuf);
+ payload = (struct rtp_payload *) payload_data;
+ memset(payload, 0, sizeof(struct rtp_payload));
+ payload->frame_count = frame_count;
+
+ gst_adapter_copy(sbcpay->adapter, payload_data +
+ RTP_SBC_PAYLOAD_HEADER_SIZE, 0, payload_length);
+ gst_adapter_flush(sbcpay->adapter, payload_length);
+
+ GST_BUFFER_TIMESTAMP(outbuf) = sbcpay->timestamp;
+ GST_DEBUG_OBJECT(sbcpay, "Pushing %d bytes", payload_length);
+
+ return gst_basertppayload_push(GST_BASE_RTP_PAYLOAD(sbcpay), outbuf);
+}
+
+static GstFlowReturn gst_rtp_sbc_pay_handle_buffer(GstBaseRTPPayload *payload,
+ GstBuffer *buffer)
+{
+ GstRtpSBCPay *sbcpay;
+ guint available;
+
+ /* FIXME check for negotiation */
+
+ sbcpay = GST_RTP_SBC_PAY(payload);
+ sbcpay->timestamp = GST_BUFFER_TIMESTAMP(buffer);
+
+ gst_adapter_push(sbcpay->adapter, buffer);
+
+ available = gst_adapter_available(sbcpay->adapter);
+ if (available + RTP_SBC_HEADER_TOTAL >=
+ GST_BASE_RTP_PAYLOAD_MTU(sbcpay) ||
+ (available >
+ (sbcpay->min_frames * sbcpay->frame_length)))
+ return gst_rtp_sbc_pay_flush_buffers(sbcpay);
+
+ return GST_FLOW_OK;
+}
+
+static gboolean gst_rtp_sbc_pay_handle_event(GstPad *pad,
+ GstEvent *event)
+{
+ GstRtpSBCPay *sbcpay = GST_RTP_SBC_PAY(GST_PAD_PARENT(pad));
+
+ switch (GST_EVENT_TYPE(event)) {
+ case GST_EVENT_EOS:
+ gst_rtp_sbc_pay_flush_buffers(sbcpay);
+ break;
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static void gst_rtp_sbc_pay_base_init(gpointer g_class)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
+
+ gst_element_class_add_pad_template(element_class,
+ gst_static_pad_template_get(&gst_rtp_sbc_pay_sink_factory));
+ gst_element_class_add_pad_template(element_class,
+ gst_static_pad_template_get(&gst_rtp_sbc_pay_src_factory));
+
+ gst_element_class_set_details(element_class, &gst_rtp_sbc_pay_details);
+}
+
+static void gst_rtp_sbc_pay_finalize(GObject *object)
+{
+ GstRtpSBCPay *sbcpay = GST_RTP_SBC_PAY(object);
+ g_object_unref(sbcpay->adapter);
+
+ GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object));
+}
+
+static void gst_rtp_sbc_pay_class_init(GstRtpSBCPayClass *klass)
+{
+ GObjectClass *gobject_class;
+ GstBaseRTPPayloadClass *payload_class =
+ GST_BASE_RTP_PAYLOAD_CLASS(klass);
+
+ gobject_class = G_OBJECT_CLASS(klass);
+ parent_class = g_type_class_peek_parent(klass);
+
+ gobject_class->finalize = GST_DEBUG_FUNCPTR(gst_rtp_sbc_pay_finalize);
+ gobject_class->set_property = GST_DEBUG_FUNCPTR(
+ gst_rtp_sbc_pay_set_property);
+ gobject_class->get_property = GST_DEBUG_FUNCPTR(
+ gst_rtp_sbc_pay_get_property);
+
+ payload_class->set_caps = GST_DEBUG_FUNCPTR(gst_rtp_sbc_pay_set_caps);
+ payload_class->handle_buffer = GST_DEBUG_FUNCPTR(
+ gst_rtp_sbc_pay_handle_buffer);
+ payload_class->handle_event = GST_DEBUG_FUNCPTR(
+ gst_rtp_sbc_pay_handle_event);
+
+ /* properties */
+ g_object_class_install_property(G_OBJECT_CLASS(klass),
+ PROP_MIN_FRAMES,
+ g_param_spec_int("min-frames", "minimum frame number",
+ "Minimum quantity of frames to send in one packet "
+ "(-1 for maximum allowed by the mtu)",
+ -1, G_MAXINT, DEFAULT_MIN_FRAMES, G_PARAM_READWRITE));
+
+ GST_DEBUG_CATEGORY_INIT(gst_rtp_sbc_pay_debug, "rtpsbcpay", 0,
+ "RTP SBC payloader");
+}
+
+static void gst_rtp_sbc_pay_set_property(GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ GstRtpSBCPay *sbcpay;
+
+ sbcpay = GST_RTP_SBC_PAY(object);
+
+ switch (prop_id) {
+ case PROP_MIN_FRAMES:
+ sbcpay->min_frames = g_value_get_int(value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
+ }
+}
+
+static void gst_rtp_sbc_pay_get_property(GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GstRtpSBCPay *sbcpay;
+
+ sbcpay = GST_RTP_SBC_PAY(object);
+
+ switch (prop_id) {
+ case PROP_MIN_FRAMES:
+ g_value_set_int(value, sbcpay->min_frames);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
+ }
+}
+
+static void gst_rtp_sbc_pay_init(GstRtpSBCPay *self, GstRtpSBCPayClass *klass)
+{
+ self->adapter = gst_adapter_new();
+ self->frame_length = 0;
+ self->timestamp = 0;
+
+ self->min_frames = DEFAULT_MIN_FRAMES;
+}
+
+gboolean gst_rtp_sbc_pay_plugin_init(GstPlugin *plugin)
+{
+ return gst_element_register(plugin, "rtpsbcpay", GST_RANK_NONE,
+ GST_TYPE_RTP_SBC_PAY);
+}
+
diff --git a/audio/gstrtpsbcpay.h b/audio/gstrtpsbcpay.h
new file mode 100644
index 0000000..398de82
--- /dev/null
+++ b/audio/gstrtpsbcpay.h
@@ -0,0 +1,66 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <gst/gst.h>
+#include <gst/rtp/gstbasertppayload.h>
+#include <gst/base/gstadapter.h>
+#include <gst/rtp/gstrtpbuffer.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_RTP_SBC_PAY \
+ (gst_rtp_sbc_pay_get_type())
+#define GST_RTP_SBC_PAY(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_SBC_PAY,\
+ GstRtpSBCPay))
+#define GST_RTP_SBC_PAY_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_SBC_PAY,\
+ GstRtpSBCPayClass))
+#define GST_IS_RTP_SBC_PAY(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_SBC_PAY))
+#define GST_IS_RTP_SBC_PAY_CLASS(obj) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_SBC_PAY))
+
+typedef struct _GstRtpSBCPay GstRtpSBCPay;
+typedef struct _GstRtpSBCPayClass GstRtpSBCPayClass;
+
+struct _GstRtpSBCPay {
+ GstBaseRTPPayload base;
+
+ GstAdapter *adapter;
+ GstClockTime timestamp;
+
+ guint frame_length;
+
+ guint min_frames;
+};
+
+struct _GstRtpSBCPayClass {
+ GstBaseRTPPayloadClass parent_class;
+};
+
+GType gst_rtp_sbc_pay_get_type(void);
+
+gboolean gst_rtp_sbc_pay_plugin_init (GstPlugin * plugin);
+
+G_END_DECLS
diff --git a/audio/gstsbcdec.c b/audio/gstsbcdec.c
new file mode 100644
index 0000000..2e5cb0a
--- /dev/null
+++ b/audio/gstsbcdec.c
@@ -0,0 +1,223 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 <string.h>
+
+#include "gstpragma.h"
+#include "gstsbcutil.h"
+#include "gstsbcdec.h"
+
+GST_DEBUG_CATEGORY_STATIC(sbc_dec_debug);
+#define GST_CAT_DEFAULT sbc_dec_debug
+
+GST_BOILERPLATE(GstSbcDec, gst_sbc_dec, GstElement, GST_TYPE_ELEMENT);
+
+static const GstElementDetails sbc_dec_details =
+ GST_ELEMENT_DETAILS("Bluetooth SBC decoder",
+ "Codec/Decoder/Audio",
+ "Decode a SBC audio stream",
+ "Marcel Holtmann <marcel@holtmann.org>");
+
+static GstStaticPadTemplate sbc_dec_sink_factory =
+ GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
+ GST_STATIC_CAPS("audio/x-sbc"));
+
+static GstStaticPadTemplate sbc_dec_src_factory =
+ GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, GST_PAD_ALWAYS,
+ GST_STATIC_CAPS("audio/x-raw-int, "
+ "rate = (int) { 16000, 32000, 44100, 48000 }, "
+ "channels = (int) [ 1, 2 ], "
+ "endianness = (int) BYTE_ORDER, "
+ "signed = (boolean) true, "
+ "width = (int) 16, "
+ "depth = (int) 16"));
+
+static GstFlowReturn sbc_dec_chain(GstPad *pad, GstBuffer *buffer)
+{
+ GstSbcDec *dec = GST_SBC_DEC(gst_pad_get_parent(pad));
+ GstFlowReturn res = GST_FLOW_OK;
+ guint size, codesize, offset = 0;
+ guint8 *data;
+
+ codesize = sbc_get_codesize(&dec->sbc);
+
+ if (dec->buffer) {
+ GstBuffer *temp = buffer;
+ buffer = gst_buffer_span(dec->buffer, 0, buffer,
+ GST_BUFFER_SIZE(dec->buffer) + GST_BUFFER_SIZE(buffer));
+ gst_buffer_unref(temp);
+ gst_buffer_unref(dec->buffer);
+ dec->buffer = NULL;
+ }
+
+ data = GST_BUFFER_DATA(buffer);
+ size = GST_BUFFER_SIZE(buffer);
+
+ while (offset < size) {
+ GstBuffer *output;
+ GstPadTemplate *template;
+ GstCaps *caps;
+ int consumed;
+
+ res = gst_pad_alloc_buffer_and_set_caps(dec->srcpad,
+ GST_BUFFER_OFFSET_NONE,
+ codesize, NULL, &output);
+
+ if (res != GST_FLOW_OK)
+ goto done;
+
+ consumed = sbc_decode(&dec->sbc, data + offset, size - offset,
+ GST_BUFFER_DATA(output), codesize,
+ NULL);
+ if (consumed <= 0)
+ break;
+
+ /* we will reuse the same caps object */
+ if (dec->outcaps == NULL) {
+ caps = gst_caps_new_simple("audio/x-raw-int",
+ "rate", G_TYPE_INT,
+ gst_sbc_parse_rate_from_sbc(
+ dec->sbc.frequency),
+ "channels", G_TYPE_INT,
+ gst_sbc_get_channel_number(
+ dec->sbc.mode),
+ NULL);
+
+ template = gst_static_pad_template_get(&sbc_dec_src_factory);
+
+ dec->outcaps = gst_caps_intersect(caps,
+ gst_pad_template_get_caps(template));
+
+ gst_caps_unref(caps);
+ }
+
+ gst_buffer_set_caps(output, dec->outcaps);
+
+ /* FIXME get a real timestamp */
+ GST_BUFFER_TIMESTAMP(output) = GST_CLOCK_TIME_NONE;
+
+ res = gst_pad_push(dec->srcpad, output);
+ if (res != GST_FLOW_OK)
+ goto done;
+
+ offset += consumed;
+ }
+
+ if (offset < size)
+ dec->buffer = gst_buffer_create_sub(buffer,
+ offset, size - offset);
+
+done:
+ gst_buffer_unref(buffer);
+ gst_object_unref(dec);
+
+ return res;
+}
+
+static GstStateChangeReturn sbc_dec_change_state(GstElement *element,
+ GstStateChange transition)
+{
+ GstSbcDec *dec = GST_SBC_DEC(element);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ GST_DEBUG("Setup subband codec");
+ if (dec->buffer) {
+ gst_buffer_unref(dec->buffer);
+ dec->buffer = NULL;
+ }
+ sbc_init(&dec->sbc, 0);
+ dec->outcaps = NULL;
+ break;
+
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ GST_DEBUG("Finish subband codec");
+ if (dec->buffer) {
+ gst_buffer_unref(dec->buffer);
+ dec->buffer = NULL;
+ }
+ sbc_finish(&dec->sbc);
+ if (dec->outcaps) {
+ gst_caps_unref(dec->outcaps);
+ dec->outcaps = NULL;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return parent_class->change_state(element, transition);
+}
+
+static void gst_sbc_dec_base_init(gpointer g_class)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
+
+ gst_element_class_add_pad_template(element_class,
+ gst_static_pad_template_get(&sbc_dec_sink_factory));
+
+ gst_element_class_add_pad_template(element_class,
+ gst_static_pad_template_get(&sbc_dec_src_factory));
+
+ gst_element_class_set_details(element_class, &sbc_dec_details);
+}
+
+static void gst_sbc_dec_class_init(GstSbcDecClass *klass)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ element_class->change_state = GST_DEBUG_FUNCPTR(sbc_dec_change_state);
+
+ GST_DEBUG_CATEGORY_INIT(sbc_dec_debug, "sbcdec", 0,
+ "SBC decoding element");
+}
+
+static void gst_sbc_dec_init(GstSbcDec *self, GstSbcDecClass *klass)
+{
+ self->sinkpad = gst_pad_new_from_static_template(
+ &sbc_dec_sink_factory, "sink");
+ gst_pad_set_chain_function(self->sinkpad, GST_DEBUG_FUNCPTR(
+ sbc_dec_chain));
+ gst_element_add_pad(GST_ELEMENT(self), self->sinkpad);
+
+ self->srcpad = gst_pad_new_from_static_template(
+ &sbc_dec_src_factory, "src");
+ gst_element_add_pad(GST_ELEMENT(self), self->srcpad);
+
+ self->outcaps = NULL;
+}
+
+gboolean gst_sbc_dec_plugin_init(GstPlugin *plugin)
+{
+ return gst_element_register(plugin, "sbcdec", GST_RANK_PRIMARY,
+ GST_TYPE_SBC_DEC);
+}
+
+
diff --git a/audio/gstsbcdec.h b/audio/gstsbcdec.h
new file mode 100644
index 0000000..c77feae
--- /dev/null
+++ b/audio/gstsbcdec.h
@@ -0,0 +1,66 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <gst/gst.h>
+
+#include "sbc.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_SBC_DEC \
+ (gst_sbc_dec_get_type())
+#define GST_SBC_DEC(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SBC_DEC,GstSbcDec))
+#define GST_SBC_DEC_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SBC_DEC,GstSbcDecClass))
+#define GST_IS_SBC_DEC(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SBC_DEC))
+#define GST_IS_SBC_DEC_CLASS(obj) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SBC_DEC))
+
+typedef struct _GstSbcDec GstSbcDec;
+typedef struct _GstSbcDecClass GstSbcDecClass;
+
+struct _GstSbcDec {
+ GstElement element;
+
+ GstPad *sinkpad;
+ GstPad *srcpad;
+
+ GstBuffer *buffer;
+
+ /* caps for outgoing buffers */
+ GstCaps *outcaps;
+
+ sbc_t sbc;
+};
+
+struct _GstSbcDecClass {
+ GstElementClass parent_class;
+};
+
+GType gst_sbc_dec_get_type(void);
+
+gboolean gst_sbc_dec_plugin_init(GstPlugin *plugin);
+
+G_END_DECLS
diff --git a/audio/gstsbcenc.c b/audio/gstsbcenc.c
new file mode 100644
index 0000000..d2de4ee
--- /dev/null
+++ b/audio/gstsbcenc.c
@@ -0,0 +1,603 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 <string.h>
+
+#include "gstpragma.h"
+#include "gstsbcutil.h"
+#include "gstsbcenc.h"
+
+#define SBC_ENC_DEFAULT_MODE SBC_MODE_AUTO
+#define SBC_ENC_DEFAULT_BLOCKS 0
+#define SBC_ENC_DEFAULT_SUB_BANDS 0
+#define SBC_ENC_DEFAULT_ALLOCATION SBC_AM_AUTO
+#define SBC_ENC_DEFAULT_RATE 0
+#define SBC_ENC_DEFAULT_CHANNELS 0
+
+#define SBC_ENC_BITPOOL_AUTO 1
+#define SBC_ENC_BITPOOL_MIN 2
+#define SBC_ENC_BITPOOL_MIN_STR "2"
+#define SBC_ENC_BITPOOL_MAX 64
+#define SBC_ENC_BITPOOL_MAX_STR "64"
+
+GST_DEBUG_CATEGORY_STATIC(sbc_enc_debug);
+#define GST_CAT_DEFAULT sbc_enc_debug
+
+#define GST_TYPE_SBC_MODE (gst_sbc_mode_get_type())
+
+static GType gst_sbc_mode_get_type(void)
+{
+ static GType sbc_mode_type = 0;
+ static GEnumValue sbc_modes[] = {
+ { SBC_MODE_MONO, "Mono", "mono" },
+ { SBC_MODE_DUAL_CHANNEL, "Dual Channel", "dual" },
+ { SBC_MODE_STEREO, "Stereo", "stereo"},
+ { SBC_MODE_JOINT_STEREO, "Joint Stereo", "joint" },
+ { SBC_MODE_AUTO, "Auto", "auto" },
+ { -1, NULL, NULL}
+ };
+
+ if (!sbc_mode_type)
+ sbc_mode_type = g_enum_register_static("GstSbcMode", sbc_modes);
+
+ return sbc_mode_type;
+}
+
+#define GST_TYPE_SBC_ALLOCATION (gst_sbc_allocation_get_type())
+
+static GType gst_sbc_allocation_get_type(void)
+{
+ static GType sbc_allocation_type = 0;
+ static GEnumValue sbc_allocations[] = {
+ { SBC_AM_LOUDNESS, "Loudness", "loudness" },
+ { SBC_AM_SNR, "SNR", "snr" },
+ { SBC_AM_AUTO, "Auto", "auto" },
+ { -1, NULL, NULL}
+ };
+
+ if (!sbc_allocation_type)
+ sbc_allocation_type = g_enum_register_static(
+ "GstSbcAllocation", sbc_allocations);
+
+ return sbc_allocation_type;
+}
+
+#define GST_TYPE_SBC_BLOCKS (gst_sbc_blocks_get_type())
+
+static GType gst_sbc_blocks_get_type(void)
+{
+ static GType sbc_blocks_type = 0;
+ static GEnumValue sbc_blocks[] = {
+ { 0, "Auto", "auto" },
+ { 4, "4", "4" },
+ { 8, "8", "8" },
+ { 12, "12", "12" },
+ { 16, "16", "16" },
+ { -1, NULL, NULL}
+ };
+
+ if (!sbc_blocks_type)
+ sbc_blocks_type = g_enum_register_static(
+ "GstSbcBlocks", sbc_blocks);
+
+ return sbc_blocks_type;
+}
+
+#define GST_TYPE_SBC_SUBBANDS (gst_sbc_subbands_get_type())
+
+static GType gst_sbc_subbands_get_type(void)
+{
+ static GType sbc_subbands_type = 0;
+ static GEnumValue sbc_subbands[] = {
+ { 0, "Auto", "auto" },
+ { 4, "4 subbands", "4" },
+ { 8, "8 subbands", "8" },
+ { -1, NULL, NULL}
+ };
+
+ if (!sbc_subbands_type)
+ sbc_subbands_type = g_enum_register_static(
+ "GstSbcSubbands", sbc_subbands);
+
+ return sbc_subbands_type;
+}
+
+enum {
+ PROP_0,
+ PROP_MODE,
+ PROP_ALLOCATION,
+ PROP_BLOCKS,
+ PROP_SUBBANDS,
+ PROP_BITPOOL
+};
+
+GST_BOILERPLATE(GstSbcEnc, gst_sbc_enc, GstElement, GST_TYPE_ELEMENT);
+
+static const GstElementDetails sbc_enc_details =
+ GST_ELEMENT_DETAILS("Bluetooth SBC encoder",
+ "Codec/Encoder/Audio",
+ "Encode a SBC audio stream",
+ "Marcel Holtmann <marcel@holtmann.org>");
+
+static GstStaticPadTemplate sbc_enc_sink_factory =
+ GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
+ GST_STATIC_CAPS("audio/x-raw-int, "
+ "rate = (int) { 16000, 32000, 44100, 48000 }, "
+ "channels = (int) [ 1, 2 ], "
+ "endianness = (int) BYTE_ORDER, "
+ "signed = (boolean) true, "
+ "width = (int) 16, "
+ "depth = (int) 16"));
+
+static GstStaticPadTemplate sbc_enc_src_factory =
+ GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, GST_PAD_ALWAYS,
+ GST_STATIC_CAPS("audio/x-sbc, "
+ "rate = (int) { 16000, 32000, 44100, 48000 }, "
+ "channels = (int) [ 1, 2 ], "
+ "mode = (string) { \"mono\", \"dual\", \"stereo\", \"joint\" }, "
+ "blocks = (int) { 4, 8, 12, 16 }, "
+ "subbands = (int) { 4, 8 }, "
+ "allocation = (string) { \"snr\", \"loudness\" }, "
+ "bitpool = (int) [ " SBC_ENC_BITPOOL_MIN_STR
+ ", " SBC_ENC_BITPOOL_MAX_STR " ]"));
+
+gboolean gst_sbc_enc_fill_sbc_params(GstSbcEnc *enc, GstCaps *caps);
+
+static GstCaps *sbc_enc_generate_srcpad_caps(GstSbcEnc *enc)
+{
+ GstCaps *src_caps;
+ GstStructure *structure;
+ GEnumValue *enum_value;
+ GEnumClass *enum_class;
+ GValue *value;
+
+ src_caps = gst_caps_copy(gst_pad_get_pad_template_caps(enc->srcpad));
+ structure = gst_caps_get_structure(src_caps, 0);
+
+ value = g_new0(GValue, 1);
+
+ if (enc->rate != 0)
+ gst_sbc_util_set_structure_int_param(structure, "rate",
+ enc->rate, value);
+
+ if (enc->channels != 0)
+ gst_sbc_util_set_structure_int_param(structure, "channels",
+ enc->channels, value);
+
+ if (enc->subbands != 0)
+ gst_sbc_util_set_structure_int_param(structure, "subbands",
+ enc->subbands, value);
+
+ if (enc->blocks != 0)
+ gst_sbc_util_set_structure_int_param(structure, "blocks",
+ enc->blocks, value);
+
+ if (enc->bitpool != SBC_ENC_BITPOOL_AUTO)
+ gst_sbc_util_set_structure_int_param(structure, "bitpool",
+ enc->bitpool, value);
+
+ if (enc->mode != SBC_ENC_DEFAULT_MODE) {
+ enum_class = g_type_class_ref(GST_TYPE_SBC_MODE);
+ enum_value = g_enum_get_value(enum_class, enc->mode);
+ gst_sbc_util_set_structure_string_param(structure, "mode",
+ enum_value->value_nick, value);
+ g_type_class_unref(enum_class);
+ }
+
+ if (enc->allocation != SBC_AM_AUTO) {
+ enum_class = g_type_class_ref(GST_TYPE_SBC_ALLOCATION);
+ enum_value = g_enum_get_value(enum_class, enc->allocation);
+ gst_sbc_util_set_structure_string_param(structure, "allocation",
+ enum_value->value_nick, value);
+ g_type_class_unref(enum_class);
+ }
+
+ g_free(value);
+
+ return src_caps;
+}
+
+static GstCaps *sbc_enc_src_getcaps(GstPad *pad)
+{
+ GstSbcEnc *enc;
+
+ enc = GST_SBC_ENC(GST_PAD_PARENT(pad));
+
+ return sbc_enc_generate_srcpad_caps(enc);
+}
+
+static gboolean sbc_enc_src_setcaps(GstPad *pad, GstCaps *caps)
+{
+ GstSbcEnc *enc = GST_SBC_ENC(GST_PAD_PARENT(pad));
+
+ GST_LOG_OBJECT(enc, "setting srcpad caps");
+
+ return gst_sbc_enc_fill_sbc_params(enc, caps);
+}
+
+static GstCaps *sbc_enc_src_caps_fixate(GstSbcEnc *enc, GstCaps *caps)
+{
+ gchar *error_message = NULL;
+ GstCaps *result;
+
+ result = gst_sbc_util_caps_fixate(caps, &error_message);
+
+ if (!result) {
+ GST_WARNING_OBJECT(enc, "Invalid input caps caused parsing "
+ "error: %s", error_message);
+ g_free(error_message);
+ return NULL;
+ }
+
+ return result;
+}
+
+static GstCaps *sbc_enc_get_fixed_srcpad_caps(GstSbcEnc *enc)
+{
+ GstCaps *caps;
+ gboolean res = TRUE;
+ GstCaps *result_caps = NULL;
+
+ caps = gst_pad_get_allowed_caps(enc->srcpad);
+ if (caps == NULL)
+ caps = sbc_enc_src_getcaps(enc->srcpad);
+
+ if (caps == GST_CAPS_NONE || gst_caps_is_empty(caps)) {
+ res = FALSE;
+ goto done;
+ }
+
+ result_caps = sbc_enc_src_caps_fixate(enc, caps);
+
+done:
+ gst_caps_unref(caps);
+
+ if (!res)
+ return NULL;
+
+ return result_caps;
+}
+
+static gboolean sbc_enc_sink_setcaps(GstPad *pad, GstCaps *caps)
+{
+ GstSbcEnc *enc;
+ GstStructure *structure;
+ GstCaps *src_caps;
+ gint rate, channels;
+ gboolean res;
+
+ enc = GST_SBC_ENC(GST_PAD_PARENT(pad));
+ structure = gst_caps_get_structure(caps, 0);
+
+ if (!gst_structure_get_int(structure, "rate", &rate))
+ return FALSE;
+ if (!gst_structure_get_int(structure, "channels", &channels))
+ return FALSE;
+
+ enc->rate = rate;
+ enc->channels = channels;
+
+ src_caps = sbc_enc_get_fixed_srcpad_caps(enc);
+ if (!src_caps)
+ return FALSE;
+ res = gst_pad_set_caps(enc->srcpad, src_caps);
+ gst_caps_unref(src_caps);
+
+ return res;
+}
+
+gboolean gst_sbc_enc_fill_sbc_params(GstSbcEnc *enc, GstCaps *caps)
+{
+ if (!gst_caps_is_fixed(caps)) {
+ GST_DEBUG_OBJECT(enc, "didn't receive fixed caps, "
+ "returning false");
+ return FALSE;
+ }
+
+ if (!gst_sbc_util_fill_sbc_params(&enc->sbc, caps))
+ return FALSE;
+
+ if (enc->rate != 0 && gst_sbc_parse_rate_from_sbc(enc->sbc.frequency)
+ != enc->rate)
+ goto fail;
+
+ if (enc->channels != 0 && gst_sbc_get_channel_number(enc->sbc.mode)
+ != enc->channels)
+ goto fail;
+
+ if (enc->blocks != 0 && gst_sbc_parse_blocks_from_sbc(enc->sbc.blocks)
+ != enc->blocks)
+ goto fail;
+
+ if (enc->subbands != 0 && gst_sbc_parse_subbands_from_sbc(
+ enc->sbc.subbands) != enc->subbands)
+ goto fail;
+
+ if (enc->mode != SBC_ENC_DEFAULT_MODE && enc->sbc.mode != enc->mode)
+ goto fail;
+
+ if (enc->allocation != SBC_AM_AUTO &&
+ enc->sbc.allocation != enc->allocation)
+ goto fail;
+
+ if (enc->bitpool != SBC_ENC_BITPOOL_AUTO &&
+ enc->sbc.bitpool != enc->bitpool)
+ goto fail;
+
+ enc->codesize = sbc_get_codesize(&enc->sbc);
+ enc->frame_length = sbc_get_frame_length(&enc->sbc);
+ enc->frame_duration = sbc_get_frame_duration(&enc->sbc);
+
+ GST_DEBUG_OBJECT(enc, "codesize: %d, frame_length: %d, frame_duration:"
+ " %d", enc->codesize, enc->frame_length,
+ enc->frame_duration);
+
+ return TRUE;
+
+fail:
+ memset(&enc->sbc, 0, sizeof(sbc_t));
+ return FALSE;
+}
+
+static GstFlowReturn sbc_enc_chain(GstPad *pad, GstBuffer *buffer)
+{
+ GstSbcEnc *enc = GST_SBC_ENC(gst_pad_get_parent(pad));
+ GstAdapter *adapter = enc->adapter;
+ GstFlowReturn res = GST_FLOW_OK;
+
+ gst_adapter_push(adapter, buffer);
+
+ while (gst_adapter_available(adapter) >= enc->codesize &&
+ res == GST_FLOW_OK) {
+ GstBuffer *output;
+ GstCaps *caps;
+ const guint8 *data;
+ gint consumed;
+
+ caps = GST_PAD_CAPS(enc->srcpad);
+ res = gst_pad_alloc_buffer_and_set_caps(enc->srcpad,
+ GST_BUFFER_OFFSET_NONE,
+ enc->frame_length, caps,
+ &output);
+ if (res != GST_FLOW_OK)
+ goto done;
+
+ data = gst_adapter_peek(adapter, enc->codesize);
+
+ consumed = sbc_encode(&enc->sbc, (gpointer) data,
+ enc->codesize,
+ GST_BUFFER_DATA(output),
+ GST_BUFFER_SIZE(output), NULL);
+ if (consumed <= 0) {
+ GST_DEBUG_OBJECT(enc, "comsumed < 0, codesize: %d",
+ enc->codesize);
+ break;
+ }
+ gst_adapter_flush(adapter, consumed);
+
+ GST_BUFFER_TIMESTAMP(output) = GST_BUFFER_TIMESTAMP(buffer);
+ /* we have only 1 frame */
+ GST_BUFFER_DURATION(output) = enc->frame_duration;
+
+ res = gst_pad_push(enc->srcpad, output);
+
+ if (res != GST_FLOW_OK)
+ goto done;
+ }
+
+done:
+ gst_object_unref(enc);
+
+ return res;
+}
+
+static GstStateChangeReturn sbc_enc_change_state(GstElement *element,
+ GstStateChange transition)
+{
+ GstSbcEnc *enc = GST_SBC_ENC(element);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ GST_DEBUG("Setup subband codec");
+ sbc_init(&enc->sbc, 0);
+ break;
+
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ GST_DEBUG("Finish subband codec");
+ sbc_finish(&enc->sbc);
+ break;
+
+ default:
+ break;
+ }
+
+ return parent_class->change_state(element, transition);
+}
+
+static void gst_sbc_enc_dispose(GObject *object)
+{
+ GstSbcEnc *enc = GST_SBC_ENC(object);
+
+ if (enc->adapter != NULL)
+ g_object_unref(G_OBJECT(enc->adapter));
+
+ enc->adapter = NULL;
+}
+
+static void gst_sbc_enc_base_init(gpointer g_class)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
+
+ gst_element_class_add_pad_template(element_class,
+ gst_static_pad_template_get(&sbc_enc_sink_factory));
+
+ gst_element_class_add_pad_template(element_class,
+ gst_static_pad_template_get(&sbc_enc_src_factory));
+
+ gst_element_class_set_details(element_class, &sbc_enc_details);
+}
+
+static void gst_sbc_enc_set_property(GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ GstSbcEnc *enc = GST_SBC_ENC(object);
+
+ /* changes to those properties will only happen on the next caps
+ * negotiation */
+
+ switch (prop_id) {
+ case PROP_MODE:
+ enc->mode = g_value_get_enum(value);
+ break;
+ case PROP_ALLOCATION:
+ enc->allocation = g_value_get_enum(value);
+ break;
+ case PROP_BLOCKS:
+ enc->blocks = g_value_get_enum(value);
+ break;
+ case PROP_SUBBANDS:
+ enc->subbands = g_value_get_enum(value);
+ break;
+ case PROP_BITPOOL:
+ enc->bitpool = g_value_get_int(value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
+ }
+}
+
+static void gst_sbc_enc_get_property(GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GstSbcEnc *enc = GST_SBC_ENC(object);
+
+ switch (prop_id) {
+ case PROP_MODE:
+ g_value_set_enum(value, enc->mode);
+ break;
+ case PROP_ALLOCATION:
+ g_value_set_enum(value, enc->allocation);
+ break;
+ case PROP_BLOCKS:
+ g_value_set_enum(value, enc->blocks);
+ break;
+ case PROP_SUBBANDS:
+ g_value_set_enum(value, enc->subbands);
+ break;
+ case PROP_BITPOOL:
+ g_value_set_int(value, enc->bitpool);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
+ }
+}
+
+static void gst_sbc_enc_class_init(GstSbcEncClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+ GstElementClass *element_class = GST_ELEMENT_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ object_class->set_property = GST_DEBUG_FUNCPTR(gst_sbc_enc_set_property);
+ object_class->get_property = GST_DEBUG_FUNCPTR(gst_sbc_enc_get_property);
+ object_class->dispose = GST_DEBUG_FUNCPTR(gst_sbc_enc_dispose);
+
+ element_class->change_state = GST_DEBUG_FUNCPTR(sbc_enc_change_state);
+
+ g_object_class_install_property(object_class, PROP_MODE,
+ g_param_spec_enum("mode", "Mode",
+ "Encoding mode", GST_TYPE_SBC_MODE,
+ SBC_ENC_DEFAULT_MODE, G_PARAM_READWRITE));
+
+ g_object_class_install_property(object_class, PROP_ALLOCATION,
+ g_param_spec_enum("allocation", "Allocation",
+ "Allocation method", GST_TYPE_SBC_ALLOCATION,
+ SBC_ENC_DEFAULT_ALLOCATION, G_PARAM_READWRITE));
+
+ g_object_class_install_property(object_class, PROP_BLOCKS,
+ g_param_spec_enum("blocks", "Blocks",
+ "Blocks", GST_TYPE_SBC_BLOCKS,
+ SBC_ENC_DEFAULT_BLOCKS, G_PARAM_READWRITE));
+
+ g_object_class_install_property(object_class, PROP_SUBBANDS,
+ g_param_spec_enum("subbands", "Sub bands",
+ "Number of sub bands", GST_TYPE_SBC_SUBBANDS,
+ SBC_ENC_DEFAULT_SUB_BANDS, G_PARAM_READWRITE));
+
+ g_object_class_install_property(object_class, PROP_BITPOOL,
+ g_param_spec_int("bitpool", "Bitpool",
+ "Bitpool (use 1 for automatic selection)",
+ SBC_ENC_BITPOOL_AUTO, SBC_ENC_BITPOOL_MAX,
+ SBC_ENC_BITPOOL_AUTO, G_PARAM_READWRITE));
+
+ GST_DEBUG_CATEGORY_INIT(sbc_enc_debug, "sbcenc", 0,
+ "SBC encoding element");
+}
+
+static void gst_sbc_enc_init(GstSbcEnc *self, GstSbcEncClass *klass)
+{
+ self->sinkpad = gst_pad_new_from_static_template(
+ &sbc_enc_sink_factory, "sink");
+ gst_pad_set_setcaps_function(self->sinkpad,
+ GST_DEBUG_FUNCPTR(sbc_enc_sink_setcaps));
+ gst_element_add_pad(GST_ELEMENT(self), self->sinkpad);
+
+ self->srcpad = gst_pad_new_from_static_template(
+ &sbc_enc_src_factory, "src");
+ gst_pad_set_getcaps_function(self->srcpad,
+ GST_DEBUG_FUNCPTR(sbc_enc_src_getcaps));
+ gst_pad_set_setcaps_function(self->srcpad,
+ GST_DEBUG_FUNCPTR(sbc_enc_src_setcaps));
+ gst_element_add_pad(GST_ELEMENT(self), self->srcpad);
+
+ gst_pad_set_chain_function(self->sinkpad,
+ GST_DEBUG_FUNCPTR(sbc_enc_chain));
+
+ self->subbands = SBC_ENC_DEFAULT_SUB_BANDS;
+ self->blocks = SBC_ENC_DEFAULT_BLOCKS;
+ self->mode = SBC_ENC_DEFAULT_MODE;
+ self->allocation = SBC_ENC_DEFAULT_ALLOCATION;
+ self->rate = SBC_ENC_DEFAULT_RATE;
+ self->channels = SBC_ENC_DEFAULT_CHANNELS;
+ self->bitpool = SBC_ENC_BITPOOL_AUTO;
+
+ self->frame_length = 0;
+ self->frame_duration = 0;
+
+ self->adapter = gst_adapter_new();
+}
+
+gboolean gst_sbc_enc_plugin_init(GstPlugin *plugin)
+{
+ return gst_element_register(plugin, "sbcenc",
+ GST_RANK_NONE, GST_TYPE_SBC_ENC);
+}
+
+
diff --git a/audio/gstsbcenc.h b/audio/gstsbcenc.h
new file mode 100644
index 0000000..0329351
--- /dev/null
+++ b/audio/gstsbcenc.h
@@ -0,0 +1,75 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <gst/gst.h>
+#include <gst/base/gstadapter.h>
+
+#include "sbc.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_SBC_ENC \
+ (gst_sbc_enc_get_type())
+#define GST_SBC_ENC(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SBC_ENC,GstSbcEnc))
+#define GST_SBC_ENC_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SBC_ENC,GstSbcEncClass))
+#define GST_IS_SBC_ENC(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SBC_ENC))
+#define GST_IS_SBC_ENC_CLASS(obj) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SBC_ENC))
+
+typedef struct _GstSbcEnc GstSbcEnc;
+typedef struct _GstSbcEncClass GstSbcEncClass;
+
+struct _GstSbcEnc {
+ GstElement element;
+
+ GstPad *sinkpad;
+ GstPad *srcpad;
+ GstAdapter *adapter;
+
+ gint rate;
+ gint channels;
+ gint mode;
+ gint blocks;
+ gint allocation;
+ gint subbands;
+ gint bitpool;
+
+ guint codesize;
+ gint frame_length;
+ gint frame_duration;
+
+ sbc_t sbc;
+};
+
+struct _GstSbcEncClass {
+ GstElementClass parent_class;
+};
+
+GType gst_sbc_enc_get_type(void);
+
+gboolean gst_sbc_enc_plugin_init(GstPlugin *plugin);
+
+G_END_DECLS
diff --git a/audio/gstsbcparse.c b/audio/gstsbcparse.c
new file mode 100644
index 0000000..a44b52c
--- /dev/null
+++ b/audio/gstsbcparse.c
@@ -0,0 +1,220 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 <string.h>
+
+#include "gstpragma.h"
+#include "gstsbcutil.h"
+#include "gstsbcparse.h"
+
+GST_DEBUG_CATEGORY_STATIC(sbc_parse_debug);
+#define GST_CAT_DEFAULT sbc_parse_debug
+
+GST_BOILERPLATE(GstSbcParse, gst_sbc_parse, GstElement, GST_TYPE_ELEMENT);
+
+static const GstElementDetails sbc_parse_details =
+ GST_ELEMENT_DETAILS("Bluetooth SBC parser",
+ "Codec/Parser/Audio",
+ "Parse a SBC audio stream",
+ "Marcel Holtmann <marcel@holtmann.org>");
+
+static GstStaticPadTemplate sbc_parse_sink_factory =
+ GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
+ GST_STATIC_CAPS("audio/x-sbc,"
+ "parsed = (boolean) false"));
+
+static GstStaticPadTemplate sbc_parse_src_factory =
+ GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, GST_PAD_ALWAYS,
+ GST_STATIC_CAPS("audio/x-sbc, "
+ "rate = (int) { 16000, 32000, 44100, 48000 }, "
+ "channels = (int) [ 1, 2 ], "
+ "mode = (string) { \"mono\", \"dual\", \"stereo\", \"joint\" }, "
+ "blocks = (int) { 4, 8, 12, 16 }, "
+ "subbands = (int) { 4, 8 }, "
+ "allocation = (string) { \"snr\", \"loudness\" },"
+ "bitpool = (int) [ 2, 64 ],"
+ "parsed = (boolean) true"));
+
+static GstFlowReturn sbc_parse_chain(GstPad *pad, GstBuffer *buffer)
+{
+ GstSbcParse *parse = GST_SBC_PARSE(gst_pad_get_parent(pad));
+ GstFlowReturn res = GST_FLOW_OK;
+ guint size, offset = 0;
+ guint8 *data;
+
+ /* FIXME use a gstadpter */
+ if (parse->buffer) {
+ GstBuffer *temp;
+ temp = buffer;
+ buffer = gst_buffer_span(parse->buffer, 0, buffer,
+ GST_BUFFER_SIZE(parse->buffer)
+ + GST_BUFFER_SIZE(buffer));
+ gst_buffer_unref(parse->buffer);
+ gst_buffer_unref(temp);
+ parse->buffer = NULL;
+ }
+
+ data = GST_BUFFER_DATA(buffer);
+ size = GST_BUFFER_SIZE(buffer);
+
+ while (offset < size) {
+ GstBuffer *output;
+ int consumed;
+
+ consumed = sbc_parse(&parse->new_sbc, data + offset,
+ size - offset);
+ if (consumed <= 0)
+ break;
+
+ if (parse->first_parsing || (memcmp(&parse->sbc,
+ &parse->new_sbc, sizeof(sbc_t)) != 0)) {
+
+ memcpy(&parse->sbc, &parse->new_sbc, sizeof(sbc_t));
+ if (parse->outcaps != NULL)
+ gst_caps_unref(parse->outcaps);
+
+ parse->outcaps = gst_sbc_parse_caps_from_sbc(
+ &parse->sbc);
+
+ parse->first_parsing = FALSE;
+ }
+
+ res = gst_pad_alloc_buffer_and_set_caps(parse->srcpad,
+ GST_BUFFER_OFFSET_NONE,
+ consumed, parse->outcaps, &output);
+
+ if (res != GST_FLOW_OK)
+ goto done;
+
+ memcpy(GST_BUFFER_DATA(output), data + offset, consumed);
+
+ res = gst_pad_push(parse->srcpad, output);
+ if (res != GST_FLOW_OK)
+ goto done;
+
+ offset += consumed;
+ }
+
+ if (offset < size)
+ parse->buffer = gst_buffer_create_sub(buffer,
+ offset, size - offset);
+
+done:
+ gst_buffer_unref(buffer);
+ gst_object_unref(parse);
+
+ return res;
+}
+
+static GstStateChangeReturn sbc_parse_change_state(GstElement *element,
+ GstStateChange transition)
+{
+ GstSbcParse *parse = GST_SBC_PARSE(element);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ GST_DEBUG("Setup subband codec");
+
+ parse->channels = -1;
+ parse->rate = -1;
+ parse->first_parsing = TRUE;
+
+ sbc_init(&parse->sbc, 0);
+ break;
+
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ GST_DEBUG("Finish subband codec");
+
+ if (parse->buffer) {
+ gst_buffer_unref(parse->buffer);
+ parse->buffer = NULL;
+ }
+ if (parse->outcaps != NULL) {
+ gst_caps_unref(parse->outcaps);
+ parse->outcaps = NULL;
+ }
+
+ sbc_finish(&parse->sbc);
+ break;
+
+ default:
+ break;
+ }
+
+ return parent_class->change_state(element, transition);
+}
+
+static void gst_sbc_parse_base_init(gpointer g_class)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
+
+ gst_element_class_add_pad_template(element_class,
+ gst_static_pad_template_get(&sbc_parse_sink_factory));
+
+ gst_element_class_add_pad_template(element_class,
+ gst_static_pad_template_get(&sbc_parse_src_factory));
+
+ gst_element_class_set_details(element_class, &sbc_parse_details);
+}
+
+static void gst_sbc_parse_class_init(GstSbcParseClass *klass)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ element_class->change_state = GST_DEBUG_FUNCPTR(sbc_parse_change_state);
+
+ GST_DEBUG_CATEGORY_INIT(sbc_parse_debug, "sbcparse", 0,
+ "SBC parsing element");
+}
+
+static void gst_sbc_parse_init(GstSbcParse *self, GstSbcParseClass *klass)
+{
+ self->sinkpad = gst_pad_new_from_static_template(
+ &sbc_parse_sink_factory, "sink");
+ gst_pad_set_chain_function(self->sinkpad,
+ GST_DEBUG_FUNCPTR(sbc_parse_chain));
+ gst_element_add_pad(GST_ELEMENT(self), self->sinkpad);
+
+ self->srcpad = gst_pad_new_from_static_template(
+ &sbc_parse_src_factory, "src");
+ gst_element_add_pad(GST_ELEMENT(self), self->srcpad);
+
+ self->outcaps = NULL;
+ self->buffer = NULL;
+ self->channels = -1;
+ self->rate = -1;
+ self->first_parsing = TRUE;
+}
+
+gboolean gst_sbc_parse_plugin_init(GstPlugin *plugin)
+{
+ return gst_element_register(plugin, "sbcparse", GST_RANK_NONE,
+ GST_TYPE_SBC_PARSE);
+}
+
diff --git a/audio/gstsbcparse.h b/audio/gstsbcparse.h
new file mode 100644
index 0000000..ecb8be4
--- /dev/null
+++ b/audio/gstsbcparse.h
@@ -0,0 +1,69 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <gst/gst.h>
+
+#include "sbc.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_SBC_PARSE \
+ (gst_sbc_parse_get_type())
+#define GST_SBC_PARSE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SBC_PARSE,GstSbcParse))
+#define GST_SBC_PARSE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SBC_PARSE,GstSbcParseClass))
+#define GST_IS_SBC_PARSE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SBC_PARSE))
+#define GST_IS_SBC_PARSE_CLASS(obj) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SBC_PARSE))
+
+typedef struct _GstSbcParse GstSbcParse;
+typedef struct _GstSbcParseClass GstSbcParseClass;
+
+struct _GstSbcParse {
+ GstElement element;
+
+ GstPad *sinkpad;
+ GstPad *srcpad;
+
+ GstBuffer *buffer;
+
+ sbc_t sbc;
+ sbc_t new_sbc;
+ GstCaps *outcaps;
+ gboolean first_parsing;
+
+ gint channels;
+ gint rate;
+};
+
+struct _GstSbcParseClass {
+ GstElementClass parent_class;
+};
+
+GType gst_sbc_parse_get_type(void);
+
+gboolean gst_sbc_parse_plugin_init(GstPlugin *plugin);
+
+G_END_DECLS
diff --git a/audio/gstsbcutil.c b/audio/gstsbcutil.c
new file mode 100644
index 0000000..63c90c2
--- /dev/null
+++ b/audio/gstsbcutil.c
@@ -0,0 +1,521 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 <math.h>
+#include "gstsbcutil.h"
+
+/*
+ * Selects one rate from a list of possible rates
+ * TODO - use a better approach to this (it is selecting the last element)
+ */
+gint gst_sbc_select_rate_from_list(const GValue *value)
+{
+ guint size = gst_value_list_get_size(value);
+ return g_value_get_int(gst_value_list_get_value(value, size-1));
+}
+
+/*
+ * Selects one number of channels option from a range of possible numbers
+ * TODO - use a better approach to this (it is selecting the maximum value)
+ */
+gint gst_sbc_select_channels_from_range(const GValue *value)
+{
+ return gst_value_get_int_range_max(value);
+}
+
+/*
+ * Selects one number of blocks from a list of possible blocks
+ * TODO - use a better approach to this (it is selecting the last element)
+ */
+gint gst_sbc_select_blocks_from_list(const GValue *value)
+{
+ guint size = gst_value_list_get_size(value);
+ return g_value_get_int(gst_value_list_get_value(value, size-1));
+}
+
+/*
+ * Selects one number of subbands from a list
+ * TODO - use a better approach to this (it is selecting the last element)
+ */
+gint gst_sbc_select_subbands_from_list(const GValue *value)
+{
+ guint size = gst_value_list_get_size(value);
+ return g_value_get_int(gst_value_list_get_value(value, size-1));
+}
+
+/*
+ * Selects one bitpool option from a range
+ * TODO - use a better approach to this (it is selecting the maximum value)
+ */
+gint gst_sbc_select_bitpool_from_range(const GValue *value)
+{
+ return gst_value_get_int_range_max(value);
+}
+
+/*
+ * Selects one allocation mode from the ones on the list
+ * TODO - use a better approach
+ */
+const gchar *gst_sbc_get_allocation_from_list(const GValue *value)
+{
+ guint size = gst_value_list_get_size(value);
+ return g_value_get_string(gst_value_list_get_value(value, size-1));
+}
+
+/*
+ * Selects one mode from the ones on the list
+ */
+const gchar *gst_sbc_get_mode_from_list(const GValue *list, gint channels)
+{
+ unsigned int i;
+ const GValue *value;
+ const gchar *aux;
+ gboolean joint, stereo, dual, mono;
+ guint size = gst_value_list_get_size(list);
+
+ joint = stereo = dual = mono = FALSE;
+
+ for (i = 0; i < size; i++) {
+ value = gst_value_list_get_value(list, i);
+ aux = g_value_get_string(value);
+ if (strcmp("joint", aux) == 0)
+ joint = TRUE;
+ else if (strcmp("stereo", aux) == 0)
+ stereo = TRUE;
+ else if (strcmp("dual", aux) == 0)
+ dual = TRUE;
+ else if (strcmp("mono", aux) == 0)
+ mono = TRUE;
+ }
+
+ if (channels == 1 && mono)
+ return "mono";
+ else if (channels == 2) {
+ if (joint)
+ return "joint";
+ else if (stereo)
+ return "stereo";
+ else if (dual)
+ return "dual";
+ }
+
+ return NULL;
+}
+
+gint gst_sbc_parse_rate_from_sbc(gint frequency)
+{
+ switch (frequency) {
+ case SBC_FREQ_16000:
+ return 16000;
+ case SBC_FREQ_32000:
+ return 32000;
+ case SBC_FREQ_44100:
+ return 44100;
+ case SBC_FREQ_48000:
+ return 48000;
+ default:
+ return 0;
+ }
+}
+
+gint gst_sbc_parse_rate_to_sbc(gint rate)
+{
+ switch (rate) {
+ case 16000:
+ return SBC_FREQ_16000;
+ case 32000:
+ return SBC_FREQ_32000;
+ case 44100:
+ return SBC_FREQ_44100;
+ case 48000:
+ return SBC_FREQ_48000;
+ default:
+ return -1;
+ }
+}
+
+gint gst_sbc_get_channel_number(gint mode)
+{
+ switch (mode) {
+ case SBC_MODE_JOINT_STEREO:
+ case SBC_MODE_STEREO:
+ case SBC_MODE_DUAL_CHANNEL:
+ return 2;
+ case SBC_MODE_MONO:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+gint gst_sbc_parse_subbands_from_sbc(gint subbands)
+{
+ switch (subbands) {
+ case SBC_SB_4:
+ return 4;
+ case SBC_SB_8:
+ return 8;
+ default:
+ return 0;
+ }
+}
+
+gint gst_sbc_parse_subbands_to_sbc(gint subbands)
+{
+ switch (subbands) {
+ case 4:
+ return SBC_SB_4;
+ case 8:
+ return SBC_SB_8;
+ default:
+ return -1;
+ }
+}
+
+gint gst_sbc_parse_blocks_from_sbc(gint blocks)
+{
+ switch (blocks) {
+ case SBC_BLK_4:
+ return 4;
+ case SBC_BLK_8:
+ return 8;
+ case SBC_BLK_12:
+ return 12;
+ case SBC_BLK_16:
+ return 16;
+ default:
+ return 0;
+ }
+}
+
+gint gst_sbc_parse_blocks_to_sbc(gint blocks)
+{
+ switch (blocks) {
+ case 4:
+ return SBC_BLK_4;
+ case 8:
+ return SBC_BLK_8;
+ case 12:
+ return SBC_BLK_12;
+ case 16:
+ return SBC_BLK_16;
+ default:
+ return -1;
+ }
+}
+
+const gchar *gst_sbc_parse_mode_from_sbc(gint mode)
+{
+ switch (mode) {
+ case SBC_MODE_MONO:
+ return "mono";
+ case SBC_MODE_DUAL_CHANNEL:
+ return "dual";
+ case SBC_MODE_STEREO:
+ return "stereo";
+ case SBC_MODE_JOINT_STEREO:
+ case SBC_MODE_AUTO:
+ return "joint";
+ default:
+ return NULL;
+ }
+}
+
+gint gst_sbc_parse_mode_to_sbc(const gchar *mode)
+{
+ if (g_ascii_strcasecmp(mode, "joint") == 0)
+ return SBC_MODE_JOINT_STEREO;
+ else if (g_ascii_strcasecmp(mode, "stereo") == 0)
+ return SBC_MODE_STEREO;
+ else if (g_ascii_strcasecmp(mode, "dual") == 0)
+ return SBC_MODE_DUAL_CHANNEL;
+ else if (g_ascii_strcasecmp(mode, "mono") == 0)
+ return SBC_MODE_MONO;
+ else if (g_ascii_strcasecmp(mode, "auto") == 0)
+ return SBC_MODE_JOINT_STEREO;
+ else
+ return -1;
+}
+
+const gchar *gst_sbc_parse_allocation_from_sbc(gint alloc)
+{
+ switch (alloc) {
+ case SBC_AM_LOUDNESS:
+ return "loudness";
+ case SBC_AM_SNR:
+ return "snr";
+ case SBC_AM_AUTO:
+ return "loudness";
+ default:
+ return NULL;
+ }
+}
+
+gint gst_sbc_parse_allocation_to_sbc(const gchar *allocation)
+{
+ if (g_ascii_strcasecmp(allocation, "loudness") == 0)
+ return SBC_AM_LOUDNESS;
+ else if (g_ascii_strcasecmp(allocation, "snr") == 0)
+ return SBC_AM_SNR;
+ else
+ return SBC_AM_LOUDNESS;
+}
+
+GstCaps *gst_sbc_parse_caps_from_sbc(sbc_t *sbc)
+{
+ GstCaps *caps;
+ const gchar *mode_str;
+ const gchar *allocation_str;
+
+ mode_str = gst_sbc_parse_mode_from_sbc(sbc->mode);
+ allocation_str = gst_sbc_parse_allocation_from_sbc(sbc->allocation);
+ caps = gst_caps_new_simple("audio/x-sbc",
+ "rate", G_TYPE_INT,
+ gst_sbc_parse_rate_from_sbc(sbc->frequency),
+ "channels", G_TYPE_INT,
+ gst_sbc_get_channel_number(sbc->mode),
+ "mode", G_TYPE_STRING, mode_str,
+ "subbands", G_TYPE_INT,
+ gst_sbc_parse_subbands_from_sbc(sbc->subbands),
+ "blocks", G_TYPE_INT,
+ gst_sbc_parse_blocks_from_sbc(sbc->blocks),
+ "allocation", G_TYPE_STRING, allocation_str,
+ "bitpool", G_TYPE_INT, sbc->bitpool,
+ NULL);
+
+ return caps;
+}
+
+/*
+ * Given a GstCaps, this will return a fixed GstCaps on sucessfull conversion.
+ * If an error occurs, it will return NULL and error_message will contain the
+ * error message.
+ *
+ * error_message must be passed NULL, if an error occurs, the caller has the
+ * ownership of the error_message, it must be freed after use.
+ */
+GstCaps *gst_sbc_util_caps_fixate(GstCaps *caps, gchar **error_message)
+{
+ GstCaps *result;
+ GstStructure *structure;
+ const GValue *value;
+ gboolean error = FALSE;
+ gint temp, rate, channels, blocks, subbands, bitpool;
+ const gchar *allocation = NULL;
+ const gchar *mode = NULL;
+
+ g_assert(*error_message == NULL);
+
+ structure = gst_caps_get_structure(caps, 0);
+
+ if (!gst_structure_has_field(structure, "rate")) {
+ error = TRUE;
+ *error_message = g_strdup("no rate");
+ goto error;
+ } else {
+ value = gst_structure_get_value(structure, "rate");
+ if (GST_VALUE_HOLDS_LIST(value))
+ temp = gst_sbc_select_rate_from_list(value);
+ else
+ temp = g_value_get_int(value);
+ rate = temp;
+ }
+
+ if (!gst_structure_has_field(structure, "channels")) {
+ error = TRUE;
+ *error_message = g_strdup("no channels");
+ goto error;
+ } else {
+ value = gst_structure_get_value(structure, "channels");
+ if (GST_VALUE_HOLDS_INT_RANGE(value))
+ temp = gst_sbc_select_channels_from_range(value);
+ else
+ temp = g_value_get_int(value);
+ channels = temp;
+ }
+
+ if (!gst_structure_has_field(structure, "blocks")) {
+ error = TRUE;
+ *error_message = g_strdup("no blocks.");
+ goto error;
+ } else {
+ value = gst_structure_get_value(structure, "blocks");
+ if (GST_VALUE_HOLDS_LIST(value))
+ temp = gst_sbc_select_blocks_from_list(value);
+ else
+ temp = g_value_get_int(value);
+ blocks = temp;
+ }
+
+ if (!gst_structure_has_field(structure, "subbands")) {
+ error = TRUE;
+ *error_message = g_strdup("no subbands");
+ goto error;
+ } else {
+ value = gst_structure_get_value(structure, "subbands");
+ if (GST_VALUE_HOLDS_LIST(value))
+ temp = gst_sbc_select_subbands_from_list(value);
+ else
+ temp = g_value_get_int(value);
+ subbands = temp;
+ }
+
+ if (!gst_structure_has_field(structure, "bitpool")) {
+ error = TRUE;
+ *error_message = g_strdup("no bitpool");
+ goto error;
+ } else {
+ value = gst_structure_get_value(structure, "bitpool");
+ if (GST_VALUE_HOLDS_INT_RANGE(value))
+ temp = gst_sbc_select_bitpool_from_range(value);
+ else
+ temp = g_value_get_int(value);
+ bitpool = temp;
+ }
+
+ if (!gst_structure_has_field(structure, "allocation")) {
+ error = TRUE;
+ *error_message = g_strdup("no allocation");
+ goto error;
+ } else {
+ value = gst_structure_get_value(structure, "allocation");
+ if (GST_VALUE_HOLDS_LIST(value))
+ allocation = gst_sbc_get_allocation_from_list(value);
+ else
+ allocation = g_value_get_string(value);
+ }
+
+ if (!gst_structure_has_field(structure, "mode")) {
+ error = TRUE;
+ *error_message = g_strdup("no mode");
+ goto error;
+ } else {
+ value = gst_structure_get_value(structure, "mode");
+ if (GST_VALUE_HOLDS_LIST(value)) {
+ mode = gst_sbc_get_mode_from_list(value, channels);
+ } else
+ mode = g_value_get_string(value);
+ }
+
+ /* perform validation
+ * if channels is 1, we must have channel mode = mono
+ * if channels is 2, we can't have channel mode = mono */
+ if ( (channels == 1 && (strcmp(mode, "mono") != 0) ) ||
+ ( channels == 2 && ( strcmp(mode, "mono") == 0))) {
+ *error_message = g_strdup_printf("Invalid combination of "
+ "channels (%d) and channel mode (%s)",
+ channels, mode);
+ error = TRUE;
+ }
+
+error:
+ if (error)
+ return NULL;
+
+ result = gst_caps_new_simple("audio/x-sbc",
+ "rate", G_TYPE_INT, rate,
+ "channels", G_TYPE_INT, channels,
+ "mode", G_TYPE_STRING, mode,
+ "blocks", G_TYPE_INT, blocks,
+ "subbands", G_TYPE_INT, subbands,
+ "allocation", G_TYPE_STRING, allocation,
+ "bitpool", G_TYPE_INT, bitpool,
+ NULL);
+
+ return result;
+}
+
+/**
+ * Sets the int field_value to the param "field" on the structure.
+ * value is used to do the operation, it must be a uninitialized (zero-filled)
+ * GValue, it will be left unitialized at the end of the function.
+ */
+void gst_sbc_util_set_structure_int_param(GstStructure *structure,
+ const gchar *field, gint field_value,
+ GValue *value)
+{
+ value = g_value_init(value, G_TYPE_INT);
+ g_value_set_int(value, field_value);
+ gst_structure_set_value(structure, field, value);
+ g_value_unset(value);
+}
+
+/**
+ * Sets the string field_value to the param "field" on the structure.
+ * value is used to do the operation, it must be a uninitialized (zero-filled)
+ * GValue, it will be left unitialized at the end of the function.
+ */
+void gst_sbc_util_set_structure_string_param(GstStructure *structure,
+ const gchar *field, const gchar *field_value,
+ GValue *value)
+{
+ value = g_value_init(value, G_TYPE_STRING);
+ g_value_set_string(value, field_value);
+ gst_structure_set_value(structure, field, value);
+ g_value_unset(value);
+}
+
+gboolean gst_sbc_util_fill_sbc_params(sbc_t *sbc, GstCaps *caps)
+{
+ GstStructure *structure;
+ gint rate, channels, subbands, blocks, bitpool;
+ const gchar *mode;
+ const gchar *allocation;
+
+ g_assert(gst_caps_is_fixed(caps));
+
+ structure = gst_caps_get_structure(caps, 0);
+
+ if (!gst_structure_get_int(structure, "rate", &rate))
+ return FALSE;
+ if (!gst_structure_get_int(structure, "channels", &channels))
+ return FALSE;
+ if (!gst_structure_get_int(structure, "subbands", &subbands))
+ return FALSE;
+ if (!gst_structure_get_int(structure, "blocks", &blocks))
+ return FALSE;
+ if (!gst_structure_get_int(structure, "bitpool", &bitpool))
+ return FALSE;
+
+ if (!(mode = gst_structure_get_string(structure, "mode")))
+ return FALSE;
+ if (!(allocation = gst_structure_get_string(structure, "allocation")))
+ return FALSE;
+
+ if (channels == 1 && strcmp(mode, "mono") != 0)
+ return FALSE;
+
+ sbc->frequency = gst_sbc_parse_rate_to_sbc(rate);
+ sbc->blocks = gst_sbc_parse_blocks_to_sbc(blocks);
+ sbc->subbands = gst_sbc_parse_subbands_to_sbc(subbands);
+ sbc->bitpool = bitpool;
+ sbc->mode = gst_sbc_parse_mode_to_sbc(mode);
+ sbc->allocation = gst_sbc_parse_allocation_to_sbc(allocation);
+
+ return TRUE;
+}
+
diff --git a/audio/gstsbcutil.h b/audio/gstsbcutil.h
new file mode 100644
index 0000000..a7f84d5
--- /dev/null
+++ b/audio/gstsbcutil.h
@@ -0,0 +1,75 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <gst/gst.h>
+
+#include "sbc.h"
+#include <string.h>
+
+#define SBC_AM_AUTO 0x02
+#define SBC_MODE_AUTO 0x04
+
+gint gst_sbc_select_rate_from_list(const GValue *value);
+
+gint gst_sbc_select_channels_from_range(const GValue *value);
+
+gint gst_sbc_select_blocks_from_list(const GValue *value);
+
+gint gst_sbc_select_subbands_from_list(const GValue *value);
+
+gint gst_sbc_select_bitpool_from_range(const GValue *value);
+
+const gchar *gst_sbc_get_allocation_from_list(const GValue *value);
+
+const gchar *gst_sbc_get_mode_from_list(const GValue *value, gint channels);
+
+gint gst_sbc_get_channel_number(gint mode);
+gint gst_sbc_parse_rate_from_sbc(gint frequency);
+gint gst_sbc_parse_rate_to_sbc(gint rate);
+
+gint gst_sbc_parse_subbands_from_sbc(gint subbands);
+gint gst_sbc_parse_subbands_to_sbc(gint subbands);
+
+gint gst_sbc_parse_blocks_from_sbc(gint blocks);
+gint gst_sbc_parse_blocks_to_sbc(gint blocks);
+
+const gchar *gst_sbc_parse_mode_from_sbc(gint mode);
+gint gst_sbc_parse_mode_to_sbc(const gchar *mode);
+
+const gchar *gst_sbc_parse_allocation_from_sbc(gint alloc);
+gint gst_sbc_parse_allocation_to_sbc(const gchar *allocation);
+
+GstCaps* gst_sbc_parse_caps_from_sbc(sbc_t *sbc);
+
+GstCaps* gst_sbc_util_caps_fixate(GstCaps *caps, gchar** error_message);
+
+void gst_sbc_util_set_structure_int_param(GstStructure *structure,
+ const gchar* field, gint field_value,
+ GValue *value);
+
+void gst_sbc_util_set_structure_string_param(GstStructure *structure,
+ const gchar* field, const gchar* field_value,
+ GValue *value);
+
+gboolean gst_sbc_util_fill_sbc_params(sbc_t *sbc, GstCaps *caps);
+
diff --git a/audio/headset.c b/audio/headset.c
new file mode 100644
index 0000000..dff10d1
--- /dev/null
+++ b/audio/headset.c
@@ -0,0 +1,2951 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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 <stdarg.h>
+#include <signal.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <assert.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+#include "device.h"
+#include "manager.h"
+#include "error.h"
+#include "telephony.h"
+#include "headset.h"
+#include "glib-helper.h"
+#include "btio.h"
+#include "dbus-common.h"
+#include "../src/adapter.h"
+#include "../src/device.h"
+
+#define DC_TIMEOUT 3
+
+#define RING_INTERVAL 3
+
+#define BUF_SIZE 1024
+
+#define HEADSET_GAIN_SPEAKER 'S'
+#define HEADSET_GAIN_MICROPHONE 'M'
+
+static struct {
+ gboolean telephony_ready; /* Telephony plugin initialized */
+ uint32_t features; /* HFP AG features */
+ const struct indicator *indicators; /* Available HFP indicators */
+ int er_mode; /* Event reporting mode */
+ int er_ind; /* Event reporting for indicators */
+ int rh; /* Response and Hold state */
+ char *number; /* Incoming phone number */
+ int number_type; /* Incoming number type */
+ guint ring_timer; /* For incoming call indication */
+ const char *chld; /* Response to AT+CHLD=? */
+} ag = {
+ .telephony_ready = FALSE,
+ .features = 0,
+ .er_mode = 3,
+ .er_ind = 0,
+ .rh = BTRH_NOT_SUPPORTED,
+ .number = NULL,
+ .number_type = 0,
+ .ring_timer = 0,
+};
+
+static gboolean sco_hci = TRUE;
+static gboolean fast_connectable = FALSE;
+
+static GSList *active_devices = NULL;
+
+static char *str_state[] = {
+ "HEADSET_STATE_DISCONNECTED",
+ "HEADSET_STATE_CONNECTING",
+ "HEADSET_STATE_CONNECTED",
+ "HEADSET_STATE_PLAY_IN_PROGRESS",
+ "HEADSET_STATE_PLAYING",
+};
+
+struct headset_state_callback {
+ headset_state_cb cb;
+ void *user_data;
+ unsigned int id;
+};
+
+struct headset_nrec_callback {
+ unsigned int id;
+ headset_nrec_cb cb;
+ void *user_data;
+};
+
+struct connect_cb {
+ unsigned int id;
+ headset_stream_cb_t cb;
+ void *cb_data;
+};
+
+struct pending_connect {
+ DBusMessage *msg;
+ DBusPendingCall *call;
+ GIOChannel *io;
+ int err;
+ headset_state_t target_state;
+ GSList *callbacks;
+ uint16_t svclass;
+};
+
+struct headset_slc {
+ char buf[BUF_SIZE];
+ int data_start;
+ int data_length;
+
+ gboolean cli_active;
+ gboolean cme_enabled;
+ gboolean cwa_enabled;
+ gboolean pending_ring;
+ gboolean inband_ring;
+ gboolean nrec;
+ gboolean nrec_req;
+
+ int sp_gain;
+ int mic_gain;
+
+ unsigned int hf_features;
+};
+
+struct headset {
+ uint32_t hsp_handle;
+ uint32_t hfp_handle;
+
+ int rfcomm_ch;
+
+ GIOChannel *rfcomm;
+ GIOChannel *tmp_rfcomm;
+ GIOChannel *sco;
+ guint sco_id;
+
+ gboolean auto_dc;
+
+ guint dc_timer;
+
+ gboolean hfp_active;
+ gboolean search_hfp;
+
+ headset_state_t state;
+ struct pending_connect *pending;
+
+ headset_lock_t lock;
+ struct headset_slc *slc;
+ GSList *nrec_cbs;
+};
+
+struct event {
+ const char *cmd;
+ int (*callback) (struct audio_device *device, const char *buf);
+};
+
+static GSList *headset_callbacks = NULL;
+
+static void error_connect_failed(DBusConnection *conn, DBusMessage *msg,
+ int err)
+{
+ DBusMessage *reply = btd_error_failed(msg,
+ err < 0 ? strerror(-err) : "Connect failed");
+ g_dbus_send_message(conn, reply);
+}
+
+static int rfcomm_connect(struct audio_device *device, headset_stream_cb_t cb,
+ void *user_data, unsigned int *cb_id);
+static int get_records(struct audio_device *device, headset_stream_cb_t cb,
+ void *user_data, unsigned int *cb_id);
+
+static void print_ag_features(uint32_t features)
+{
+ GString *gstr;
+ char *str;
+
+ if (features == 0) {
+ DBG("HFP AG features: (none)");
+ return;
+ }
+
+ gstr = g_string_new("HFP AG features: ");
+
+ if (features & AG_FEATURE_THREE_WAY_CALLING)
+ g_string_append(gstr, "\"Three-way calling\" ");
+ if (features & AG_FEATURE_EC_ANDOR_NR)
+ g_string_append(gstr, "\"EC and/or NR function\" ");
+ if (features & AG_FEATURE_VOICE_RECOGNITION)
+ g_string_append(gstr, "\"Voice recognition function\" ");
+ if (features & AG_FEATURE_INBAND_RINGTONE)
+ g_string_append(gstr, "\"In-band ring tone capability\" ");
+ if (features & AG_FEATURE_ATTACH_NUMBER_TO_VOICETAG)
+ g_string_append(gstr, "\"Attach a number to a voice tag\" ");
+ if (features & AG_FEATURE_REJECT_A_CALL)
+ g_string_append(gstr, "\"Ability to reject a call\" ");
+ if (features & AG_FEATURE_ENHANCED_CALL_STATUS)
+ g_string_append(gstr, "\"Enhanced call status\" ");
+ if (features & AG_FEATURE_ENHANCED_CALL_CONTROL)
+ g_string_append(gstr, "\"Enhanced call control\" ");
+ if (features & AG_FEATURE_EXTENDED_ERROR_RESULT_CODES)
+ g_string_append(gstr, "\"Extended Error Result Codes\" ");
+
+ str = g_string_free(gstr, FALSE);
+
+ DBG("%s", str);
+
+ g_free(str);
+}
+
+static void print_hf_features(uint32_t features)
+{
+ GString *gstr;
+ char *str;
+
+ if (features == 0) {
+ DBG("HFP HF features: (none)");
+ return;
+ }
+
+ gstr = g_string_new("HFP HF features: ");
+
+ if (features & HF_FEATURE_EC_ANDOR_NR)
+ g_string_append(gstr, "\"EC and/or NR function\" ");
+ if (features & HF_FEATURE_CALL_WAITING_AND_3WAY)
+ g_string_append(gstr, "\"Call waiting and 3-way calling\" ");
+ if (features & HF_FEATURE_CLI_PRESENTATION)
+ g_string_append(gstr, "\"CLI presentation capability\" ");
+ if (features & HF_FEATURE_VOICE_RECOGNITION)
+ g_string_append(gstr, "\"Voice recognition activation\" ");
+ if (features & HF_FEATURE_REMOTE_VOLUME_CONTROL)
+ g_string_append(gstr, "\"Remote volume control\" ");
+ if (features & HF_FEATURE_ENHANCED_CALL_STATUS)
+ g_string_append(gstr, "\"Enhanced call status\" ");
+ if (features & HF_FEATURE_ENHANCED_CALL_CONTROL)
+ g_string_append(gstr, "\"Enhanced call control\" ");
+
+ str = g_string_free(gstr, FALSE);
+
+ DBG("%s", str);
+
+ g_free(str);
+}
+
+static const char *state2str(headset_state_t state)
+{
+ switch (state) {
+ case HEADSET_STATE_DISCONNECTED:
+ return "disconnected";
+ case HEADSET_STATE_CONNECTING:
+ return "connecting";
+ case HEADSET_STATE_CONNECTED:
+ case HEADSET_STATE_PLAY_IN_PROGRESS:
+ return "connected";
+ case HEADSET_STATE_PLAYING:
+ return "playing";
+ }
+
+ return NULL;
+}
+
+static int headset_send_valist(struct headset *hs, char *format, va_list ap)
+{
+ char rsp[BUF_SIZE];
+ ssize_t total_written, count;
+ int fd;
+
+ count = vsnprintf(rsp, sizeof(rsp), format, ap);
+
+ if (count < 0)
+ return -EINVAL;
+
+ if (!hs->rfcomm) {
+ error("headset_send: the headset is not connected");
+ return -EIO;
+ }
+
+ total_written = 0;
+ fd = g_io_channel_unix_get_fd(hs->rfcomm);
+
+ while (total_written < count) {
+ ssize_t written;
+
+ written = write(fd, rsp + total_written,
+ count - total_written);
+ if (written < 0)
+ return -errno;
+
+ total_written += written;
+ }
+
+ return 0;
+}
+
+static int headset_send(struct headset *hs, char *format, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, format);
+ ret = headset_send_valist(hs, format, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+static int supported_features(struct audio_device *device, const char *buf)
+{
+ struct headset *hs = device->headset;
+ struct headset_slc *slc = hs->slc;
+ int err;
+
+ if (strlen(buf) < 9)
+ return -EINVAL;
+
+ slc->hf_features = strtoul(&buf[8], NULL, 10);
+
+ print_hf_features(slc->hf_features);
+
+ err = headset_send(hs, "\r\n+BRSF: %u\r\n", ag.features);
+ if (err < 0)
+ return err;
+
+ return headset_send(hs, "\r\nOK\r\n");
+}
+
+static char *indicator_ranges(const struct indicator *indicators)
+{
+ int i;
+ GString *gstr;
+
+ gstr = g_string_new("\r\n+CIND: ");
+
+ for (i = 0; indicators[i].desc != NULL; i++) {
+ if (i == 0)
+ g_string_append_printf(gstr, "(\"%s\",(%s))",
+ indicators[i].desc,
+ indicators[i].range);
+ else
+ g_string_append_printf(gstr, ",(\"%s\",(%s))",
+ indicators[i].desc,
+ indicators[i].range);
+ }
+
+ g_string_append(gstr, "\r\n");
+
+ return g_string_free(gstr, FALSE);
+}
+
+static char *indicator_values(const struct indicator *indicators)
+{
+ int i;
+ GString *gstr;
+
+ gstr = g_string_new("\r\n+CIND: ");
+
+ for (i = 0; indicators[i].desc != NULL; i++) {
+ if (i == 0)
+ g_string_append_printf(gstr, "%d", indicators[i].val);
+ else
+ g_string_append_printf(gstr, ",%d", indicators[i].val);
+ }
+
+ g_string_append(gstr, "\r\n");
+
+ return g_string_free(gstr, FALSE);
+}
+
+static int report_indicators(struct audio_device *device, const char *buf)
+{
+ struct headset *hs = device->headset;
+ int err;
+ char *str;
+
+ if (strlen(buf) < 8)
+ return -EINVAL;
+
+ if (ag.indicators == NULL) {
+ error("HFP AG indicators not initialized");
+ return headset_send(hs, "\r\nERROR\r\n");
+ }
+
+ if (buf[7] == '=')
+ str = indicator_ranges(ag.indicators);
+ else
+ str = indicator_values(ag.indicators);
+
+ err = headset_send(hs, str);
+
+ g_free(str);
+
+ if (err < 0)
+ return err;
+
+ return headset_send(hs, "\r\nOK\r\n");
+}
+
+static void pending_connect_complete(struct connect_cb *cb, struct audio_device *dev)
+{
+ struct headset *hs = dev->headset;
+
+ if (hs->pending->err < 0)
+ cb->cb(NULL, cb->cb_data);
+ else
+ cb->cb(dev, cb->cb_data);
+}
+
+static void pending_connect_finalize(struct audio_device *dev)
+{
+ struct headset *hs = dev->headset;
+ struct pending_connect *p = hs->pending;
+
+ if (p == NULL)
+ return;
+
+ if (p->svclass)
+ bt_cancel_discovery(&dev->src, &dev->dst);
+
+ g_slist_foreach(p->callbacks, (GFunc) pending_connect_complete, dev);
+
+ g_slist_foreach(p->callbacks, (GFunc) g_free, NULL);
+ g_slist_free(p->callbacks);
+
+ if (p->io) {
+ g_io_channel_shutdown(p->io, TRUE, NULL);
+ g_io_channel_unref(p->io);
+ }
+
+ if (p->msg)
+ dbus_message_unref(p->msg);
+
+ if (p->call) {
+ dbus_pending_call_cancel(p->call);
+ dbus_pending_call_unref(p->call);
+ }
+
+ g_free(p);
+
+ hs->pending = NULL;
+}
+
+static void pending_connect_init(struct headset *hs, headset_state_t target_state)
+{
+ if (hs->pending) {
+ if (hs->pending->target_state < target_state)
+ hs->pending->target_state = target_state;
+ return;
+ }
+
+ hs->pending = g_new0(struct pending_connect, 1);
+ hs->pending->target_state = target_state;
+}
+
+static unsigned int connect_cb_new(struct headset *hs,
+ headset_state_t target_state,
+ headset_stream_cb_t func,
+ void *user_data)
+{
+ struct connect_cb *cb;
+ unsigned int free_cb_id = 1;
+
+ pending_connect_init(hs, target_state);
+
+ if (!func)
+ return 0;
+
+ cb = g_new(struct connect_cb, 1);
+
+ cb->cb = func;
+ cb->cb_data = user_data;
+ cb->id = free_cb_id++;
+
+ hs->pending->callbacks = g_slist_append(hs->pending->callbacks,
+ cb);
+
+ return cb->id;
+}
+
+static void send_foreach_headset(GSList *devices,
+ int (*cmp) (struct headset *hs),
+ char *format, ...)
+{
+ GSList *l;
+ va_list ap;
+
+ for (l = devices; l != NULL; l = l->next) {
+ struct audio_device *device = l->data;
+ struct headset *hs = device->headset;
+ int ret;
+
+ assert(hs != NULL);
+
+ if (cmp && cmp(hs) != 0)
+ continue;
+
+ va_start(ap, format);
+ ret = headset_send_valist(hs, format, ap);
+ if (ret < 0)
+ error("Failed to send to headset: %s (%d)",
+ strerror(-ret), -ret);
+ va_end(ap);
+ }
+}
+
+static int cli_cmp(struct headset *hs)
+{
+ struct headset_slc *slc = hs->slc;
+
+ if (!hs->hfp_active)
+ return -1;
+
+ if (slc->cli_active)
+ return 0;
+ else
+ return -1;
+}
+
+static gboolean ring_timer_cb(gpointer data)
+{
+ send_foreach_headset(active_devices, NULL, "\r\nRING\r\n");
+
+ if (ag.number)
+ send_foreach_headset(active_devices, cli_cmp,
+ "\r\n+CLIP: \"%s\",%d\r\n",
+ ag.number, ag.number_type);
+
+ return TRUE;
+}
+
+static void sco_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+ int sk;
+ struct audio_device *dev = user_data;
+ struct headset *hs = dev->headset;
+ struct headset_slc *slc = hs->slc;
+ struct pending_connect *p = hs->pending;
+
+ if (err) {
+ error("%s", err->message);
+
+ if (p != NULL) {
+ p->err = -errno;
+ if (p->msg)
+ error_connect_failed(dev->conn, p->msg, p->err);
+ pending_connect_finalize(dev);
+ }
+
+ if (hs->rfcomm)
+ headset_set_state(dev, HEADSET_STATE_CONNECTED);
+ else
+ headset_set_state(dev, HEADSET_STATE_DISCONNECTED);
+
+ return;
+ }
+
+ DBG("SCO socket opened for headset %s", dev->path);
+
+ sk = g_io_channel_unix_get_fd(chan);
+
+ DBG("SCO fd=%d", sk);
+
+ if (p) {
+ p->io = NULL;
+ if (p->msg) {
+ DBusMessage *reply;
+ reply = dbus_message_new_method_return(p->msg);
+ g_dbus_send_message(dev->conn, reply);
+ }
+
+ pending_connect_finalize(dev);
+ }
+
+ fcntl(sk, F_SETFL, 0);
+
+ headset_set_state(dev, HEADSET_STATE_PLAYING);
+
+ if (slc->pending_ring) {
+ ring_timer_cb(NULL);
+ ag.ring_timer = g_timeout_add_seconds(RING_INTERVAL,
+ ring_timer_cb,
+ NULL);
+ slc->pending_ring = FALSE;
+ }
+}
+
+static int sco_connect(struct audio_device *dev, headset_stream_cb_t cb,
+ void *user_data, unsigned int *cb_id)
+{
+ struct headset *hs = dev->headset;
+ GError *err = NULL;
+ GIOChannel *io;
+
+ if (hs->state != HEADSET_STATE_CONNECTED)
+ return -EINVAL;
+
+ io = bt_io_connect(BT_IO_SCO, sco_connect_cb, dev, NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &dev->src,
+ BT_IO_OPT_DEST_BDADDR, &dev->dst,
+ BT_IO_OPT_INVALID);
+ if (!io) {
+ error("%s", err->message);
+ g_error_free(err);
+ return -EIO;
+ }
+
+ hs->sco = io;
+
+ headset_set_state(dev, HEADSET_STATE_PLAY_IN_PROGRESS);
+
+ pending_connect_init(hs, HEADSET_STATE_PLAYING);
+
+ if (cb) {
+ unsigned int id = connect_cb_new(hs, HEADSET_STATE_PLAYING,
+ cb, user_data);
+ if (cb_id)
+ *cb_id = id;
+ }
+
+ return 0;
+}
+
+static int hfp_cmp(struct headset *hs)
+{
+ if (hs->hfp_active)
+ return 0;
+ else
+ return -1;
+}
+
+static void hfp_slc_complete(struct audio_device *dev)
+{
+ struct headset *hs = dev->headset;
+ struct pending_connect *p = hs->pending;
+
+ DBG("HFP Service Level Connection established");
+
+ headset_set_state(dev, HEADSET_STATE_CONNECTED);
+
+ if (p == NULL)
+ return;
+
+ if (p->target_state == HEADSET_STATE_CONNECTED) {
+ if (p->msg) {
+ DBusMessage *reply = dbus_message_new_method_return(p->msg);
+ g_dbus_send_message(dev->conn, reply);
+ }
+ pending_connect_finalize(dev);
+ return;
+ }
+
+ p->err = sco_connect(dev, NULL, NULL, NULL);
+ if (p->err < 0) {
+ if (p->msg)
+ error_connect_failed(dev->conn, p->msg, p->err);
+ pending_connect_finalize(dev);
+ }
+}
+
+static int telephony_generic_rsp(struct audio_device *device, cme_error_t err)
+{
+ struct headset *hs = device->headset;
+ struct headset_slc *slc = hs->slc;
+
+ if (err != CME_ERROR_NONE) {
+ if (slc->cme_enabled)
+ return headset_send(hs, "\r\n+CME ERROR: %d\r\n", err);
+ else
+ return headset_send(hs, "\r\nERROR\r\n");
+ }
+
+ return headset_send(hs, "\r\nOK\r\n");
+}
+
+int telephony_event_reporting_rsp(void *telephony_device, cme_error_t err)
+{
+ struct audio_device *device = telephony_device;
+ struct headset *hs = device->headset;
+ struct headset_slc *slc = hs->slc;
+ int ret;
+
+ if (err != CME_ERROR_NONE)
+ return telephony_generic_rsp(telephony_device, err);
+
+ ret = headset_send(hs, "\r\nOK\r\n");
+ if (ret < 0)
+ return ret;
+
+ if (hs->state != HEADSET_STATE_CONNECTING)
+ return 0;
+
+ if (slc->hf_features & HF_FEATURE_CALL_WAITING_AND_3WAY &&
+ ag.features & AG_FEATURE_THREE_WAY_CALLING)
+ return 0;
+
+ hfp_slc_complete(device);
+
+ return 0;
+}
+
+static int event_reporting(struct audio_device *dev, const char *buf)
+{
+ char **tokens; /* <mode>, <keyp>, <disp>, <ind>, <bfr> */
+
+ if (strlen(buf) < 13)
+ return -EINVAL;
+
+ tokens = g_strsplit(&buf[8], ",", 5);
+ if (g_strv_length(tokens) < 4) {
+ g_strfreev(tokens);
+ return -EINVAL;
+ }
+
+ ag.er_mode = atoi(tokens[0]);
+ ag.er_ind = atoi(tokens[3]);
+
+ g_strfreev(tokens);
+ tokens = NULL;
+
+ DBG("Event reporting (CMER): mode=%d, ind=%d",
+ ag.er_mode, ag.er_ind);
+
+ switch (ag.er_ind) {
+ case 0:
+ case 1:
+ telephony_event_reporting_req(dev, ag.er_ind);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int call_hold(struct audio_device *dev, const char *buf)
+{
+ struct headset *hs = dev->headset;
+ int err;
+
+ if (strlen(buf) < 9)
+ return -EINVAL;
+
+ if (buf[8] != '?') {
+ telephony_call_hold_req(dev, &buf[8]);
+ return 0;
+ }
+
+ err = headset_send(hs, "\r\n+CHLD: (%s)\r\n", ag.chld);
+ if (err < 0)
+ return err;
+
+ err = headset_send(hs, "\r\nOK\r\n");
+ if (err < 0)
+ return err;
+
+ if (hs->state != HEADSET_STATE_CONNECTING)
+ return 0;
+
+ hfp_slc_complete(dev);
+
+ return 0;
+}
+
+int telephony_key_press_rsp(void *telephony_device, cme_error_t err)
+{
+ return telephony_generic_rsp(telephony_device, err);
+}
+
+static int key_press(struct audio_device *device, const char *buf)
+{
+ if (strlen(buf) < 9)
+ return -EINVAL;
+
+ g_dbus_emit_signal(device->conn, device->path,
+ AUDIO_HEADSET_INTERFACE, "AnswerRequested",
+ DBUS_TYPE_INVALID);
+
+ if (ag.ring_timer) {
+ g_source_remove(ag.ring_timer);
+ ag.ring_timer = 0;
+ }
+
+ telephony_key_press_req(device, &buf[8]);
+
+ return 0;
+}
+
+int telephony_answer_call_rsp(void *telephony_device, cme_error_t err)
+{
+ return telephony_generic_rsp(telephony_device, err);
+}
+
+static int answer_call(struct audio_device *device, const char *buf)
+{
+ if (ag.ring_timer) {
+ g_source_remove(ag.ring_timer);
+ ag.ring_timer = 0;
+ }
+
+ if (ag.number) {
+ g_free(ag.number);
+ ag.number = NULL;
+ }
+
+ telephony_answer_call_req(device);
+
+ return 0;
+}
+
+int telephony_terminate_call_rsp(void *telephony_device,
+ cme_error_t err)
+{
+ struct audio_device *device = telephony_device;
+ struct headset *hs = device->headset;
+
+ if (err != CME_ERROR_NONE)
+ return telephony_generic_rsp(telephony_device, err);
+
+ g_dbus_emit_signal(device->conn, device->path,
+ AUDIO_HEADSET_INTERFACE, "CallTerminated",
+ DBUS_TYPE_INVALID);
+
+ return headset_send(hs, "\r\nOK\r\n");
+}
+
+static int terminate_call(struct audio_device *device, const char *buf)
+{
+ if (ag.number) {
+ g_free(ag.number);
+ ag.number = NULL;
+ }
+
+ if (ag.ring_timer) {
+ g_source_remove(ag.ring_timer);
+ ag.ring_timer = 0;
+ }
+
+ telephony_terminate_call_req(device);
+
+ return 0;
+}
+
+static int cli_notification(struct audio_device *device, const char *buf)
+{
+ struct headset *hs = device->headset;
+ struct headset_slc *slc = hs->slc;
+
+ if (strlen(buf) < 9)
+ return -EINVAL;
+
+ slc->cli_active = buf[8] == '1' ? TRUE : FALSE;
+
+ return headset_send(hs, "\r\nOK\r\n");
+}
+
+int telephony_response_and_hold_rsp(void *telephony_device, cme_error_t err)
+{
+ return telephony_generic_rsp(telephony_device, err);
+}
+
+static int response_and_hold(struct audio_device *device, const char *buf)
+{
+ struct headset *hs = device->headset;
+
+ if (strlen(buf) < 8)
+ return -EINVAL;
+
+ if (ag.rh == BTRH_NOT_SUPPORTED)
+ return telephony_generic_rsp(device, CME_ERROR_NOT_SUPPORTED);
+
+ if (buf[7] == '=') {
+ telephony_response_and_hold_req(device, atoi(&buf[8]) < 0);
+ return 0;
+ }
+
+ if (ag.rh >= 0)
+ headset_send(hs, "\r\n+BTRH: %d\r\n", ag.rh);
+
+ return headset_send(hs, "\r\nOK\r\n", ag.rh);
+}
+
+int telephony_last_dialed_number_rsp(void *telephony_device, cme_error_t err)
+{
+ return telephony_generic_rsp(telephony_device, err);
+}
+
+static int last_dialed_number(struct audio_device *device, const char *buf)
+{
+ telephony_last_dialed_number_req(device);
+
+ return 0;
+}
+
+int telephony_dial_number_rsp(void *telephony_device, cme_error_t err)
+{
+ return telephony_generic_rsp(telephony_device, err);
+}
+
+static int dial_number(struct audio_device *device, const char *buf)
+{
+ char number[BUF_SIZE];
+ size_t buf_len;
+
+ buf_len = strlen(buf);
+
+ if (buf[buf_len - 1] != ';') {
+ DBG("Rejecting non-voice call dial request");
+ return -EINVAL;
+ }
+
+ memset(number, 0, sizeof(number));
+ strncpy(number, &buf[3], buf_len - 4);
+
+ telephony_dial_number_req(device, number);
+
+ return 0;
+}
+
+static int headset_set_gain(struct audio_device *device, uint16_t gain, char type)
+{
+ struct headset *hs = device->headset;
+ struct headset_slc *slc = hs->slc;
+ const char *name, *property;
+
+ if (gain > 15) {
+ error("Invalid gain value: %u", gain);
+ return -EINVAL;
+ }
+
+ switch (type) {
+ case HEADSET_GAIN_SPEAKER:
+ if (slc->sp_gain == gain) {
+ DBG("Ignoring no-change in speaker gain");
+ return -EALREADY;
+ }
+ name = "SpeakerGainChanged";
+ property = "SpeakerGain";
+ slc->sp_gain = gain;
+ break;
+ case HEADSET_GAIN_MICROPHONE:
+ if (slc->mic_gain == gain) {
+ DBG("Ignoring no-change in microphone gain");
+ return -EALREADY;
+ }
+ name = "MicrophoneGainChanged";
+ property = "MicrophoneGain";
+ slc->mic_gain = gain;
+ break;
+ default:
+ error("Unknown gain setting");
+ return -EINVAL;
+ }
+
+ g_dbus_emit_signal(device->conn, device->path,
+ AUDIO_HEADSET_INTERFACE, name,
+ DBUS_TYPE_UINT16, &gain,
+ DBUS_TYPE_INVALID);
+
+ emit_property_changed(device->conn, device->path,
+ AUDIO_HEADSET_INTERFACE, property,
+ DBUS_TYPE_UINT16, &gain);
+
+ return 0;
+}
+
+static int signal_gain_setting(struct audio_device *device, const char *buf)
+{
+ struct headset *hs = device->headset;
+ dbus_uint16_t gain;
+ int err;
+
+ if (strlen(buf) < 8) {
+ error("Too short string for Gain setting");
+ return -EINVAL;
+ }
+
+ gain = (dbus_uint16_t) strtol(&buf[7], NULL, 10);
+
+ err = headset_set_gain(device, gain, buf[5]);
+ if (err < 0 && err != -EALREADY)
+ return err;
+
+ return headset_send(hs, "\r\nOK\r\n");
+}
+
+int telephony_transmit_dtmf_rsp(void *telephony_device, cme_error_t err)
+{
+ return telephony_generic_rsp(telephony_device, err);
+}
+
+static int dtmf_tone(struct audio_device *device, const char *buf)
+{
+ char tone;
+
+ if (strlen(buf) < 8) {
+ error("Too short string for DTMF tone");
+ return -EINVAL;
+ }
+
+ tone = buf[7];
+ if (tone >= '#' && tone <= 'D')
+ telephony_transmit_dtmf_req(device, tone);
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+int telephony_subscriber_number_rsp(void *telephony_device, cme_error_t err)
+{
+ return telephony_generic_rsp(telephony_device, err);
+}
+
+static int subscriber_number(struct audio_device *device, const char *buf)
+{
+ telephony_subscriber_number_req(device);
+
+ return 0;
+}
+
+int telephony_list_current_calls_rsp(void *telephony_device, cme_error_t err)
+{
+ return telephony_generic_rsp(telephony_device, err);
+}
+
+static int list_current_calls(struct audio_device *device, const char *buf)
+{
+ telephony_list_current_calls_req(device);
+
+ return 0;
+}
+
+static int extended_errors(struct audio_device *device, const char *buf)
+{
+ struct headset *hs = device->headset;
+ struct headset_slc *slc = hs->slc;
+
+ if (strlen(buf) < 9)
+ return -EINVAL;
+
+ if (buf[8] == '1') {
+ slc->cme_enabled = TRUE;
+ DBG("CME errors enabled for headset %p", hs);
+ } else {
+ slc->cme_enabled = FALSE;
+ DBG("CME errors disabled for headset %p", hs);
+ }
+
+ return headset_send(hs, "\r\nOK\r\n");
+}
+
+static int call_waiting_notify(struct audio_device *device, const char *buf)
+{
+ struct headset *hs = device->headset;
+ struct headset_slc *slc = hs->slc;
+
+ if (strlen(buf) < 9)
+ return -EINVAL;
+
+ if (buf[8] == '1') {
+ slc->cwa_enabled = TRUE;
+ DBG("Call waiting notification enabled for headset %p", hs);
+ } else {
+ slc->cwa_enabled = FALSE;
+ DBG("Call waiting notification disabled for headset %p", hs);
+ }
+
+ return headset_send(hs, "\r\nOK\r\n");
+}
+
+int telephony_operator_selection_rsp(void *telephony_device, cme_error_t err)
+{
+ return telephony_generic_rsp(telephony_device, err);
+}
+
+int telephony_call_hold_rsp(void *telephony_device, cme_error_t err)
+{
+ return telephony_generic_rsp(telephony_device, err);
+}
+
+int telephony_nr_and_ec_rsp(void *telephony_device, cme_error_t err)
+{
+ struct audio_device *device = telephony_device;
+ struct headset *hs = device->headset;
+ struct headset_slc *slc = hs->slc;
+
+ if (err == CME_ERROR_NONE) {
+ GSList *l;
+
+ for (l = hs->nrec_cbs; l; l = l->next) {
+ struct headset_nrec_callback *nrec_cb = l->data;
+
+ nrec_cb->cb(device, slc->nrec_req, nrec_cb->user_data);
+ }
+
+ slc->nrec = hs->slc->nrec_req;
+ }
+
+ return telephony_generic_rsp(telephony_device, err);
+}
+
+int telephony_voice_dial_rsp(void *telephony_device, cme_error_t err)
+{
+ return telephony_generic_rsp(telephony_device, err);
+}
+
+int telephony_operator_selection_ind(int mode, const char *oper)
+{
+ if (!active_devices)
+ return -ENODEV;
+
+ send_foreach_headset(active_devices, hfp_cmp,
+ "\r\n+COPS: %d,0,\"%s\"\r\n",
+ mode, oper);
+ return 0;
+}
+
+static int operator_selection(struct audio_device *device, const char *buf)
+{
+ struct headset *hs = device->headset;
+
+ if (strlen(buf) < 8)
+ return -EINVAL;
+
+ switch (buf[7]) {
+ case '?':
+ telephony_operator_selection_req(device);
+ break;
+ case '=':
+ return headset_send(hs, "\r\nOK\r\n");
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int nr_and_ec(struct audio_device *device, const char *buf)
+{
+ struct headset *hs = device->headset;
+ struct headset_slc *slc = hs->slc;
+
+ if (strlen(buf) < 9)
+ return -EINVAL;
+
+ if (buf[8] == '0')
+ slc->nrec_req = FALSE;
+ else
+ slc->nrec_req = TRUE;
+
+ telephony_nr_and_ec_req(device, slc->nrec_req);
+
+ return 0;
+}
+
+static int voice_dial(struct audio_device *device, const char *buf)
+{
+ gboolean enable;
+
+ if (strlen(buf) < 9)
+ return -EINVAL;
+
+ if (buf[8] == '0')
+ enable = FALSE;
+ else
+ enable = TRUE;
+
+ telephony_voice_dial_req(device, enable);
+
+ return 0;
+}
+
+static struct event event_callbacks[] = {
+ { "ATA", answer_call },
+ { "ATD", dial_number },
+ { "AT+VG", signal_gain_setting },
+ { "AT+BRSF", supported_features },
+ { "AT+CIND", report_indicators },
+ { "AT+CMER", event_reporting },
+ { "AT+CHLD", call_hold },
+ { "AT+CHUP", terminate_call },
+ { "AT+CKPD", key_press },
+ { "AT+CLIP", cli_notification },
+ { "AT+BTRH", response_and_hold },
+ { "AT+BLDN", last_dialed_number },
+ { "AT+VTS", dtmf_tone },
+ { "AT+CNUM", subscriber_number },
+ { "AT+CLCC", list_current_calls },
+ { "AT+CMEE", extended_errors },
+ { "AT+CCWA", call_waiting_notify },
+ { "AT+COPS", operator_selection },
+ { "AT+NREC", nr_and_ec },
+ { "AT+BVRA", voice_dial },
+ { 0 }
+};
+
+static int handle_event(struct audio_device *device, const char *buf)
+{
+ struct event *ev;
+
+ DBG("Received %s", buf);
+
+ for (ev = event_callbacks; ev->cmd; ev++) {
+ if (!strncmp(buf, ev->cmd, strlen(ev->cmd)))
+ return ev->callback(device, buf);
+ }
+
+ return -EINVAL;
+}
+
+static void close_sco(struct audio_device *device)
+{
+ struct headset *hs = device->headset;
+
+ if (hs->sco) {
+ int sock = g_io_channel_unix_get_fd(hs->sco);
+ shutdown(sock, SHUT_RDWR);
+ g_io_channel_shutdown(hs->sco, TRUE, NULL);
+ g_io_channel_unref(hs->sco);
+ hs->sco = NULL;
+ }
+
+ if (hs->sco_id) {
+ g_source_remove(hs->sco_id);
+ hs->sco_id = 0;
+ }
+}
+
+static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond,
+ struct audio_device *device)
+{
+ struct headset *hs;
+ struct headset_slc *slc;
+ unsigned char buf[BUF_SIZE];
+ ssize_t bytes_read;
+ size_t free_space;
+ int fd;
+
+ if (cond & G_IO_NVAL)
+ return FALSE;
+
+ hs = device->headset;
+ slc = hs->slc;
+
+ if (cond & (G_IO_ERR | G_IO_HUP)) {
+ DBG("ERR or HUP on RFCOMM socket");
+ goto failed;
+ }
+
+ fd = g_io_channel_unix_get_fd(chan);
+
+ bytes_read = read(fd, buf, sizeof(buf) - 1);
+ if (bytes_read < 0)
+ return TRUE;
+
+ free_space = sizeof(slc->buf) - slc->data_start -
+ slc->data_length - 1;
+
+ if (free_space < (size_t) bytes_read) {
+ /* Very likely that the HS is sending us garbage so
+ * just ignore the data and disconnect */
+ error("Too much data to fit incomming buffer");
+ goto failed;
+ }
+
+ memcpy(&slc->buf[slc->data_start], buf, bytes_read);
+ slc->data_length += bytes_read;
+
+ /* Make sure the data is null terminated so we can use string
+ * functions */
+ slc->buf[slc->data_start + slc->data_length] = '\0';
+
+ while (slc->data_length > 0) {
+ char *cr;
+ int err;
+ off_t cmd_len;
+
+ cr = strchr(&slc->buf[slc->data_start], '\r');
+ if (!cr)
+ break;
+
+ cmd_len = 1 + (off_t) cr - (off_t) &slc->buf[slc->data_start];
+ *cr = '\0';
+
+ if (cmd_len > 1)
+ err = handle_event(device, &slc->buf[slc->data_start]);
+ else
+ /* Silently skip empty commands */
+ err = 0;
+
+ if (err == -EINVAL) {
+ error("Badly formated or unrecognized command: %s",
+ &slc->buf[slc->data_start]);
+ err = headset_send(hs, "\r\nERROR\r\n");
+ } else if (err < 0)
+ error("Error handling command %s: %s (%d)",
+ &slc->buf[slc->data_start],
+ strerror(-err), -err);
+
+ slc->data_start += cmd_len;
+ slc->data_length -= cmd_len;
+
+ if (!slc->data_length)
+ slc->data_start = 0;
+ }
+
+ return TRUE;
+
+failed:
+ headset_set_state(device, HEADSET_STATE_DISCONNECTED);
+
+ return FALSE;
+}
+
+static gboolean sco_cb(GIOChannel *chan, GIOCondition cond,
+ struct audio_device *device)
+{
+ if (cond & G_IO_NVAL)
+ return FALSE;
+
+ error("Audio connection got disconnected");
+
+ pending_connect_finalize(device);
+ headset_set_state(device, HEADSET_STATE_CONNECTED);
+
+ return FALSE;
+}
+
+void headset_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+ struct audio_device *dev = user_data;
+ struct headset *hs = dev->headset;
+ struct pending_connect *p = hs->pending;
+ char hs_address[18];
+
+ if (err) {
+ error("%s", err->message);
+ goto failed;
+ }
+
+ /* For HFP telephony isn't ready just disconnect */
+ if (hs->hfp_active && !ag.telephony_ready) {
+ error("Unable to accept HFP connection since the telephony "
+ "subsystem isn't initialized");
+ goto failed;
+ }
+
+ hs->rfcomm = hs->tmp_rfcomm;
+ hs->tmp_rfcomm = NULL;
+
+ ba2str(&dev->dst, hs_address);
+
+ if (p)
+ p->io = NULL;
+ else
+ hs->auto_dc = FALSE;
+
+ g_io_add_watch(chan, G_IO_IN | G_IO_ERR | G_IO_HUP| G_IO_NVAL,
+ (GIOFunc) rfcomm_io_cb, dev);
+
+ DBG("%s: Connected to %s", dev->path, hs_address);
+
+ hs->slc = g_new0(struct headset_slc, 1);
+ hs->slc->sp_gain = 15;
+ hs->slc->mic_gain = 15;
+ hs->slc->nrec = TRUE;
+
+ /* In HFP mode wait for Service Level Connection */
+ if (hs->hfp_active)
+ return;
+
+ headset_set_state(dev, HEADSET_STATE_CONNECTED);
+
+ if (p && p->target_state == HEADSET_STATE_PLAYING) {
+ p->err = sco_connect(dev, NULL, NULL, NULL);
+ if (p->err < 0)
+ goto failed;
+ return;
+ }
+
+ if (p && p->msg) {
+ DBusMessage *reply = dbus_message_new_method_return(p->msg);
+ g_dbus_send_message(dev->conn, reply);
+ }
+
+ pending_connect_finalize(dev);
+
+ return;
+
+failed:
+ if (p && p->msg)
+ error_connect_failed(dev->conn, p->msg, p->err);
+ pending_connect_finalize(dev);
+ if (hs->rfcomm)
+ headset_set_state(dev, HEADSET_STATE_CONNECTED);
+ else
+ headset_set_state(dev, HEADSET_STATE_DISCONNECTED);
+}
+
+static int headset_set_channel(struct headset *headset,
+ const sdp_record_t *record, uint16_t svc)
+{
+ int ch;
+ sdp_list_t *protos;
+
+ if (sdp_get_access_protos(record, &protos) < 0) {
+ error("Unable to get access protos from headset record");
+ return -1;
+ }
+
+ ch = sdp_get_proto_port(protos, RFCOMM_UUID);
+
+ sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL);
+ sdp_list_free(protos, NULL);
+
+ if (ch <= 0) {
+ error("Unable to get RFCOMM channel from Headset record");
+ return -1;
+ }
+
+ headset->rfcomm_ch = ch;
+
+ if (svc == HANDSFREE_SVCLASS_ID) {
+ headset->hfp_handle = record->handle;
+ DBG("Discovered Handsfree service on channel %d", ch);
+ } else {
+ headset->hsp_handle = record->handle;
+ DBG("Discovered Headset service on channel %d", ch);
+ }
+
+ return 0;
+}
+
+static void get_record_cb(sdp_list_t *recs, int err, gpointer user_data)
+{
+ struct audio_device *dev = user_data;
+ struct headset *hs = dev->headset;
+ struct pending_connect *p = hs->pending;
+ sdp_record_t *record = NULL;
+ sdp_list_t *r;
+ uuid_t uuid;
+
+ assert(hs->pending != NULL);
+
+ if (err < 0) {
+ error("Unable to get service record: %s (%d)",
+ strerror(-err), -err);
+ p->err = -err;
+ if (p->msg)
+ error_connect_failed(dev->conn, p->msg, p->err);
+ goto failed;
+ }
+
+ if (!recs || !recs->data) {
+ error("No records found");
+ goto failed_not_supported;
+ }
+
+ sdp_uuid16_create(&uuid, p->svclass);
+
+ for (r = recs; r != NULL; r = r->next) {
+ sdp_list_t *classes;
+ uuid_t class;
+
+ record = r->data;
+
+ if (sdp_get_service_classes(record, &classes) < 0) {
+ error("Unable to get service classes from record");
+ continue;
+ }
+
+ memcpy(&class, classes->data, sizeof(uuid));
+
+ sdp_list_free(classes, free);
+
+
+ if (sdp_uuid_cmp(&class, &uuid) == 0)
+ break;
+ }
+
+ if (r == NULL) {
+ error("No record found with UUID 0x%04x", p->svclass);
+ goto failed_not_supported;
+ }
+
+ if (headset_set_channel(hs, record, p->svclass) < 0) {
+ error("Unable to extract RFCOMM channel from service record");
+ goto failed_not_supported;
+ }
+
+ /* Set svclass to 0 so we can easily check that SDP is no-longer
+ * going on (to know if bt_cancel_discovery needs to be called) */
+ p->svclass = 0;
+
+ err = rfcomm_connect(dev, NULL, NULL, NULL);
+ if (err < 0) {
+ error("Unable to connect: %s (%d)", strerror(-err), -err);
+ p->err = -err;
+ error_connect_failed(dev->conn, p->msg, p->err);
+ goto failed;
+ }
+
+ return;
+
+failed_not_supported:
+ if (p->svclass == HANDSFREE_SVCLASS_ID &&
+ get_records(dev, NULL, NULL, NULL) == 0)
+ return;
+ if (p->msg) {
+ DBusMessage *reply = btd_error_not_supported(p->msg);
+ g_dbus_send_message(dev->conn, reply);
+ }
+failed:
+ p->svclass = 0;
+ pending_connect_finalize(dev);
+ headset_set_state(dev, HEADSET_STATE_DISCONNECTED);
+}
+
+static int get_records(struct audio_device *device, headset_stream_cb_t cb,
+ void *user_data, unsigned int *cb_id)
+{
+ struct headset *hs = device->headset;
+ uint16_t svclass;
+ uuid_t uuid;
+ int err;
+
+ if (hs->pending && hs->pending->svclass == HANDSFREE_SVCLASS_ID)
+ svclass = HEADSET_SVCLASS_ID;
+ else
+ svclass = hs->search_hfp ? HANDSFREE_SVCLASS_ID :
+ HEADSET_SVCLASS_ID;
+
+ sdp_uuid16_create(&uuid, svclass);
+
+ err = bt_search_service(&device->src, &device->dst, &uuid,
+ get_record_cb, device, NULL);
+ if (err < 0)
+ return err;
+
+ if (hs->pending) {
+ hs->pending->svclass = svclass;
+ return 0;
+ }
+
+ headset_set_state(device, HEADSET_STATE_CONNECTING);
+
+ pending_connect_init(hs, HEADSET_STATE_CONNECTED);
+
+ hs->pending->svclass = svclass;
+
+ if (cb) {
+ unsigned int id;
+ id = connect_cb_new(hs, HEADSET_STATE_CONNECTED,
+ cb, user_data);
+ if (cb_id)
+ *cb_id = id;
+ }
+
+ return 0;
+}
+
+static int rfcomm_connect(struct audio_device *dev, headset_stream_cb_t cb,
+ void *user_data, unsigned int *cb_id)
+{
+ struct headset *hs = dev->headset;
+ char address[18];
+ GError *err = NULL;
+
+ if (!manager_allow_headset_connection(dev))
+ return -ECONNREFUSED;
+
+ if (hs->rfcomm_ch < 0)
+ return get_records(dev, cb, user_data, cb_id);
+
+ ba2str(&dev->dst, address);
+
+ DBG("%s: Connecting to %s channel %d", dev->path, address,
+ hs->rfcomm_ch);
+
+ hs->tmp_rfcomm = bt_io_connect(BT_IO_RFCOMM, headset_connect_cb, dev,
+ NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &dev->src,
+ BT_IO_OPT_DEST_BDADDR, &dev->dst,
+ BT_IO_OPT_CHANNEL, hs->rfcomm_ch,
+ BT_IO_OPT_INVALID);
+
+ hs->rfcomm_ch = -1;
+
+ if (!hs->tmp_rfcomm) {
+ error("%s", err->message);
+ g_error_free(err);
+ return -EIO;
+ }
+
+ hs->hfp_active = hs->hfp_handle != 0 ? TRUE : FALSE;
+
+ headset_set_state(dev, HEADSET_STATE_CONNECTING);
+
+ pending_connect_init(hs, HEADSET_STATE_CONNECTED);
+
+ if (cb) {
+ unsigned int id = connect_cb_new(hs, HEADSET_STATE_CONNECTED,
+ cb, user_data);
+ if (cb_id)
+ *cb_id = id;
+ }
+
+ return 0;
+}
+
+static DBusMessage *hs_stop(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct audio_device *device = data;
+ struct headset *hs = device->headset;
+ DBusMessage *reply = NULL;
+
+ if (hs->state < HEADSET_STATE_PLAY_IN_PROGRESS)
+ return btd_error_not_connected(msg);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ headset_set_state(device, HEADSET_STATE_CONNECTED);
+
+ return reply;
+}
+
+static DBusMessage *hs_is_playing(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct audio_device *device = data;
+ struct headset *hs = device->headset;
+ DBusMessage *reply;
+ dbus_bool_t playing;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ playing = (hs->state == HEADSET_STATE_PLAYING);
+
+ dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &playing,
+ DBUS_TYPE_INVALID);
+
+ return reply;
+}
+
+static DBusMessage *hs_disconnect(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct audio_device *device = data;
+ struct headset *hs = device->headset;
+ char hs_address[18];
+
+ if (hs->state == HEADSET_STATE_DISCONNECTED)
+ return btd_error_not_connected(msg);
+
+ headset_shutdown(device);
+ ba2str(&device->dst, hs_address);
+ info("Disconnected from %s, %s", hs_address, device->path);
+
+ return dbus_message_new_method_return(msg);
+
+}
+
+static DBusMessage *hs_is_connected(DBusConnection *conn,
+ DBusMessage *msg,
+ void *data)
+{
+ struct audio_device *device = data;
+ DBusMessage *reply;
+ dbus_bool_t connected;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ connected = (device->headset->state >= HEADSET_STATE_CONNECTED);
+
+ dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &connected,
+ DBUS_TYPE_INVALID);
+
+ return reply;
+}
+
+static DBusMessage *hs_connect(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct audio_device *device = data;
+ struct headset *hs = device->headset;
+ int err;
+
+ if (hs->state == HEADSET_STATE_CONNECTING)
+ return btd_error_in_progress(msg);
+ else if (hs->state > HEADSET_STATE_CONNECTING)
+ return btd_error_already_connected(msg);
+
+ if (hs->hfp_handle && !ag.telephony_ready)
+ return btd_error_not_ready(msg);
+
+ device->auto_connect = FALSE;
+
+ err = rfcomm_connect(device, NULL, NULL, NULL);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+ hs->auto_dc = FALSE;
+
+ hs->pending->msg = dbus_message_ref(msg);
+
+ return NULL;
+}
+
+static DBusMessage *hs_ring(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct audio_device *device = data;
+ struct headset *hs = device->headset;
+ DBusMessage *reply = NULL;
+ int err;
+
+ if (hs->state < HEADSET_STATE_CONNECTED)
+ return btd_error_not_connected(msg);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ if (ag.ring_timer) {
+ DBG("IndicateCall received when already indicating");
+ goto done;
+ }
+
+ err = headset_send(hs, "\r\nRING\r\n");
+ if (err < 0) {
+ dbus_message_unref(reply);
+ return btd_error_failed(msg, strerror(-err));
+ }
+
+ ring_timer_cb(NULL);
+ ag.ring_timer = g_timeout_add_seconds(RING_INTERVAL, ring_timer_cb,
+ NULL);
+
+done:
+ return reply;
+}
+
+static DBusMessage *hs_cancel_call(DBusConnection *conn,
+ DBusMessage *msg,
+ void *data)
+{
+ struct audio_device *device = data;
+ struct headset *hs = device->headset;
+ DBusMessage *reply = NULL;
+
+ if (hs->state < HEADSET_STATE_CONNECTED)
+ return btd_error_not_connected(msg);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ if (ag.ring_timer) {
+ g_source_remove(ag.ring_timer);
+ ag.ring_timer = 0;
+ } else
+ DBG("Got CancelCall method call but no call is active");
+
+ return reply;
+}
+
+static DBusMessage *hs_play(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct audio_device *device = data;
+ struct headset *hs = device->headset;
+ int err;
+
+ if (sco_hci) {
+ error("Refusing Headset.Play() because SCO HCI routing "
+ "is enabled");
+ return btd_error_not_available(msg);
+ }
+
+ switch (hs->state) {
+ case HEADSET_STATE_DISCONNECTED:
+ case HEADSET_STATE_CONNECTING:
+ return btd_error_not_connected(msg);
+ case HEADSET_STATE_PLAY_IN_PROGRESS:
+ if (hs->pending && hs->pending->msg == NULL) {
+ hs->pending->msg = dbus_message_ref(msg);
+ return NULL;
+ }
+ return btd_error_busy(msg);
+ case HEADSET_STATE_PLAYING:
+ return btd_error_already_connected(msg);
+ case HEADSET_STATE_CONNECTED:
+ default:
+ break;
+ }
+
+ err = sco_connect(device, NULL, NULL, NULL);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+ hs->pending->msg = dbus_message_ref(msg);
+
+ return NULL;
+}
+
+static DBusMessage *hs_get_speaker_gain(DBusConnection *conn,
+ DBusMessage *msg,
+ void *data)
+{
+ struct audio_device *device = data;
+ struct headset *hs = device->headset;
+ struct headset_slc *slc = hs->slc;
+ DBusMessage *reply;
+ dbus_uint16_t gain;
+
+ if (hs->state < HEADSET_STATE_CONNECTED)
+ return btd_error_not_available(msg);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ gain = (dbus_uint16_t) slc->sp_gain;
+
+ dbus_message_append_args(reply, DBUS_TYPE_UINT16, &gain,
+ DBUS_TYPE_INVALID);
+
+ return reply;
+}
+
+static DBusMessage *hs_get_mic_gain(DBusConnection *conn,
+ DBusMessage *msg,
+ void *data)
+{
+ struct audio_device *device = data;
+ struct headset *hs = device->headset;
+ struct headset_slc *slc = hs->slc;
+ DBusMessage *reply;
+ dbus_uint16_t gain;
+
+ if (hs->state < HEADSET_STATE_CONNECTED || slc == NULL)
+ return btd_error_not_available(msg);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ gain = (dbus_uint16_t) slc->mic_gain;
+
+ dbus_message_append_args(reply, DBUS_TYPE_UINT16, &gain,
+ DBUS_TYPE_INVALID);
+
+ return reply;
+}
+
+static DBusMessage *hs_set_gain(DBusConnection *conn,
+ DBusMessage *msg,
+ void *data, uint16_t gain,
+ char type)
+{
+ struct audio_device *device = data;
+ struct headset *hs = device->headset;
+ DBusMessage *reply;
+ int err;
+
+ if (hs->state < HEADSET_STATE_CONNECTED)
+ return btd_error_not_connected(msg);
+
+ err = headset_set_gain(device, gain, type);
+ if (err < 0)
+ return btd_error_invalid_args(msg);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ if (hs->state == HEADSET_STATE_PLAYING) {
+ err = headset_send(hs, "\r\n+VG%c=%u\r\n", type, gain);
+ if (err < 0) {
+ dbus_message_unref(reply);
+ return btd_error_failed(msg, strerror(-err));
+ }
+ }
+
+ return reply;
+}
+
+static DBusMessage *hs_set_speaker_gain(DBusConnection *conn,
+ DBusMessage *msg,
+ void *data)
+{
+ uint16_t gain;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT16, &gain,
+ DBUS_TYPE_INVALID))
+ return NULL;
+
+ return hs_set_gain(conn, msg, data, gain, HEADSET_GAIN_SPEAKER);
+}
+
+static DBusMessage *hs_set_mic_gain(DBusConnection *conn,
+ DBusMessage *msg,
+ void *data)
+{
+ uint16_t gain;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT16, &gain,
+ DBUS_TYPE_INVALID))
+ return NULL;
+
+ return hs_set_gain(conn, msg, data, gain, HEADSET_GAIN_MICROPHONE);
+}
+
+static DBusMessage *hs_get_properties(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct audio_device *device = data;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ gboolean value;
+ const char *state;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+
+ /* Playing */
+ value = (device->headset->state == HEADSET_STATE_PLAYING);
+ dict_append_entry(&dict, "Playing", DBUS_TYPE_BOOLEAN, &value);
+
+ /* State */
+ state = state2str(device->headset->state);
+ if (state)
+ dict_append_entry(&dict, "State", DBUS_TYPE_STRING, &state);
+
+ /* Connected */
+ value = (device->headset->state >= HEADSET_STATE_CONNECTED);
+ dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &value);
+
+ if (!value)
+ goto done;
+
+ /* SpeakerGain */
+ dict_append_entry(&dict, "SpeakerGain",
+ DBUS_TYPE_UINT16,
+ &device->headset->slc->sp_gain);
+
+ /* MicrophoneGain */
+ dict_append_entry(&dict, "MicrophoneGain",
+ DBUS_TYPE_UINT16,
+ &device->headset->slc->mic_gain);
+
+done:
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static DBusMessage *hs_set_property(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ const char *property;
+ DBusMessageIter iter;
+ DBusMessageIter sub;
+ uint16_t gain;
+
+ if (!dbus_message_iter_init(msg, &iter))
+ return btd_error_invalid_args(msg);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&iter, &property);
+ dbus_message_iter_next(&iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+ return btd_error_invalid_args(msg);
+ dbus_message_iter_recurse(&iter, &sub);
+
+ if (g_str_equal("SpeakerGain", property)) {
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT16)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&sub, &gain);
+ return hs_set_gain(conn, msg, data, gain,
+ HEADSET_GAIN_SPEAKER);
+ } else if (g_str_equal("MicrophoneGain", property)) {
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT16)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&sub, &gain);
+ return hs_set_gain(conn, msg, data, gain,
+ HEADSET_GAIN_MICROPHONE);
+ }
+
+ return btd_error_invalid_args(msg);
+}
+static GDBusMethodTable headset_methods[] = {
+ { "Connect", "", "", hs_connect,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "Disconnect", "", "", hs_disconnect },
+ { "IsConnected", "", "b", hs_is_connected },
+ { "IndicateCall", "", "", hs_ring },
+ { "CancelCall", "", "", hs_cancel_call },
+ { "Play", "", "", hs_play,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "Stop", "", "", hs_stop },
+ { "IsPlaying", "", "b", hs_is_playing,
+ G_DBUS_METHOD_FLAG_DEPRECATED },
+ { "GetSpeakerGain", "", "q", hs_get_speaker_gain,
+ G_DBUS_METHOD_FLAG_DEPRECATED },
+ { "GetMicrophoneGain", "", "q", hs_get_mic_gain,
+ G_DBUS_METHOD_FLAG_DEPRECATED },
+ { "SetSpeakerGain", "q", "", hs_set_speaker_gain,
+ G_DBUS_METHOD_FLAG_DEPRECATED },
+ { "SetMicrophoneGain", "q", "", hs_set_mic_gain,
+ G_DBUS_METHOD_FLAG_DEPRECATED },
+ { "GetProperties", "", "a{sv}",hs_get_properties },
+ { "SetProperty", "sv", "", hs_set_property },
+ { NULL, NULL, NULL, NULL }
+};
+
+static GDBusSignalTable headset_signals[] = {
+ { "Connected", "", G_DBUS_SIGNAL_FLAG_DEPRECATED },
+ { "Disconnected", "", G_DBUS_SIGNAL_FLAG_DEPRECATED },
+ { "AnswerRequested", "" },
+ { "Stopped", "", G_DBUS_SIGNAL_FLAG_DEPRECATED },
+ { "Playing", "", G_DBUS_SIGNAL_FLAG_DEPRECATED },
+ { "SpeakerGainChanged", "q", G_DBUS_SIGNAL_FLAG_DEPRECATED },
+ { "MicrophoneGainChanged", "q", G_DBUS_SIGNAL_FLAG_DEPRECATED },
+ { "CallTerminated", "" },
+ { "PropertyChanged", "sv" },
+ { NULL, NULL }
+};
+
+void headset_update(struct audio_device *dev, uint16_t svc,
+ const char *uuidstr)
+{
+ struct headset *headset = dev->headset;
+ const sdp_record_t *record;
+
+ record = btd_device_get_record(dev->btd_dev, uuidstr);
+ if (!record)
+ return;
+
+ switch (svc) {
+ case HANDSFREE_SVCLASS_ID:
+ if (headset->hfp_handle &&
+ (headset->hfp_handle != record->handle)) {
+ error("More than one HFP record found on device");
+ return;
+ }
+
+ headset->hfp_handle = record->handle;
+ break;
+
+ case HEADSET_SVCLASS_ID:
+ if (headset->hsp_handle &&
+ (headset->hsp_handle != record->handle)) {
+ error("More than one HSP record found on device");
+ return;
+ }
+
+ headset->hsp_handle = record->handle;
+
+ /* Ignore this record if we already have access to HFP */
+ if (headset->hfp_handle)
+ return;
+
+ break;
+
+ default:
+ DBG("Invalid record passed to headset_update");
+ return;
+ }
+}
+
+static int headset_close_rfcomm(struct audio_device *dev)
+{
+ struct headset *hs = dev->headset;
+ GIOChannel *rfcomm = hs->tmp_rfcomm ? hs->tmp_rfcomm : hs->rfcomm;
+
+ if (rfcomm) {
+ g_io_channel_shutdown(rfcomm, TRUE, NULL);
+ g_io_channel_unref(rfcomm);
+ hs->tmp_rfcomm = NULL;
+ hs->rfcomm = NULL;
+ }
+
+ g_free(hs->slc);
+ hs->slc = NULL;
+
+ return 0;
+}
+
+static void headset_free(struct audio_device *dev)
+{
+ struct headset *hs = dev->headset;
+
+ if (hs->dc_timer) {
+ g_source_remove(hs->dc_timer);
+ hs->dc_timer = 0;
+ }
+
+ close_sco(dev);
+
+ headset_close_rfcomm(dev);
+
+ g_slist_foreach(hs->nrec_cbs, (GFunc) g_free, NULL);
+ g_slist_free(hs->nrec_cbs);
+
+ g_free(hs);
+ dev->headset = NULL;
+}
+
+static void path_unregister(void *data)
+{
+ struct audio_device *dev = data;
+ struct headset *hs = dev->headset;
+
+ if (hs->state > HEADSET_STATE_DISCONNECTED) {
+ DBG("Headset unregistered while device was connected!");
+ headset_shutdown(dev);
+ }
+
+ DBG("Unregistered interface %s on path %s",
+ AUDIO_HEADSET_INTERFACE, dev->path);
+
+ headset_free(dev);
+}
+
+void headset_unregister(struct audio_device *dev)
+{
+ g_dbus_unregister_interface(dev->conn, dev->path,
+ AUDIO_HEADSET_INTERFACE);
+}
+
+struct headset *headset_init(struct audio_device *dev, uint16_t svc,
+ const char *uuidstr)
+{
+ struct headset *hs;
+ const sdp_record_t *record;
+
+ hs = g_new0(struct headset, 1);
+ hs->rfcomm_ch = -1;
+ hs->search_hfp = server_is_enabled(&dev->src, HANDSFREE_SVCLASS_ID);
+
+ record = btd_device_get_record(dev->btd_dev, uuidstr);
+ if (!record)
+ goto register_iface;
+
+ switch (svc) {
+ case HANDSFREE_SVCLASS_ID:
+ hs->hfp_handle = record->handle;
+ break;
+
+ case HEADSET_SVCLASS_ID:
+ hs->hsp_handle = record->handle;
+ break;
+
+ default:
+ DBG("Invalid record passed to headset_init");
+ g_free(hs);
+ return NULL;
+ }
+
+register_iface:
+ if (!g_dbus_register_interface(dev->conn, dev->path,
+ AUDIO_HEADSET_INTERFACE,
+ headset_methods, headset_signals, NULL,
+ dev, path_unregister)) {
+ g_free(hs);
+ return NULL;
+ }
+
+ DBG("Registered interface %s on path %s",
+ AUDIO_HEADSET_INTERFACE, dev->path);
+
+ return hs;
+}
+
+uint32_t headset_config_init(GKeyFile *config)
+{
+ GError *err = NULL;
+ char *str;
+
+ /* Use the default values if there is no config file */
+ if (config == NULL)
+ return ag.features;
+
+ str = g_key_file_get_string(config, "General", "SCORouting",
+ &err);
+ if (err) {
+ DBG("audio.conf: %s", err->message);
+ g_clear_error(&err);
+ } else {
+ if (strcmp(str, "PCM") == 0)
+ sco_hci = FALSE;
+ else if (strcmp(str, "HCI") == 0)
+ sco_hci = TRUE;
+ else
+ error("Invalid Headset Routing value: %s", str);
+ g_free(str);
+ }
+
+ /* Init fast connectable option */
+ str = g_key_file_get_string(config, "Headset", "FastConnectable",
+ &err);
+ if (err) {
+ DBG("audio.conf: %s", err->message);
+ g_clear_error(&err);
+ } else {
+ fast_connectable = strcmp(str, "true") == 0;
+ if (fast_connectable)
+ manager_set_fast_connectable(FALSE);
+ g_free(str);
+ }
+
+ return ag.features;
+}
+
+static gboolean hs_dc_timeout(struct audio_device *dev)
+{
+ headset_set_state(dev, HEADSET_STATE_DISCONNECTED);
+ return FALSE;
+}
+
+gboolean headset_cancel_stream(struct audio_device *dev, unsigned int id)
+{
+ struct headset *hs = dev->headset;
+ struct pending_connect *p = hs->pending;
+ GSList *l;
+ struct connect_cb *cb = NULL;
+
+ if (!p)
+ return FALSE;
+
+ for (l = p->callbacks; l != NULL; l = l->next) {
+ struct connect_cb *tmp = l->data;
+
+ if (tmp->id == id) {
+ cb = tmp;
+ break;
+ }
+ }
+
+ if (!cb)
+ return FALSE;
+
+ p->callbacks = g_slist_remove(p->callbacks, cb);
+ g_free(cb);
+
+ if (p->callbacks || p->msg)
+ return TRUE;
+
+ if (hs->auto_dc) {
+ if (hs->rfcomm)
+ hs->dc_timer = g_timeout_add_seconds(DC_TIMEOUT,
+ (GSourceFunc) hs_dc_timeout,
+ dev);
+ else
+ headset_set_state(dev, HEADSET_STATE_DISCONNECTED);
+ }
+
+ return TRUE;
+}
+
+static gboolean dummy_connect_complete(struct audio_device *dev)
+{
+ pending_connect_finalize(dev);
+ return FALSE;
+}
+
+unsigned int headset_request_stream(struct audio_device *dev,
+ headset_stream_cb_t cb,
+ void *user_data)
+{
+ struct headset *hs = dev->headset;
+ unsigned int id;
+
+ if (hs->state == HEADSET_STATE_PLAYING) {
+ id = connect_cb_new(hs, HEADSET_STATE_PLAYING, cb, user_data);
+ g_idle_add((GSourceFunc) dummy_connect_complete, dev);
+ return id;
+ }
+
+ if (hs->dc_timer) {
+ g_source_remove(hs->dc_timer);
+ hs->dc_timer = 0;
+ }
+
+ if (hs->state == HEADSET_STATE_CONNECTING ||
+ hs->state == HEADSET_STATE_PLAY_IN_PROGRESS)
+ return connect_cb_new(hs, HEADSET_STATE_PLAYING, cb, user_data);
+
+ if (hs->rfcomm == NULL) {
+ if (rfcomm_connect(dev, cb, user_data, &id) < 0)
+ return 0;
+ hs->auto_dc = TRUE;
+ } else if (sco_connect(dev, cb, user_data, &id) < 0)
+ return 0;
+
+ hs->pending->target_state = HEADSET_STATE_PLAYING;
+
+ return id;
+}
+
+unsigned int headset_config_stream(struct audio_device *dev,
+ gboolean auto_dc,
+ headset_stream_cb_t cb,
+ void *user_data)
+{
+ struct headset *hs = dev->headset;
+ unsigned int id = 0;
+
+ if (hs->dc_timer) {
+ g_source_remove(hs->dc_timer);
+ hs->dc_timer = 0;
+ }
+
+ if (hs->state == HEADSET_STATE_CONNECTING)
+ return connect_cb_new(hs, HEADSET_STATE_CONNECTED, cb,
+ user_data);
+
+ if (hs->rfcomm)
+ goto done;
+
+ if (rfcomm_connect(dev, cb, user_data, &id) < 0)
+ return 0;
+
+ hs->auto_dc = auto_dc;
+ hs->pending->target_state = HEADSET_STATE_CONNECTED;
+
+ return id;
+
+done:
+ id = connect_cb_new(hs, HEADSET_STATE_CONNECTED, cb, user_data);
+ g_idle_add((GSourceFunc) dummy_connect_complete, dev);
+ return id;
+}
+
+unsigned int headset_suspend_stream(struct audio_device *dev,
+ headset_stream_cb_t cb,
+ void *user_data)
+{
+ struct headset *hs = dev->headset;
+ unsigned int id;
+ int sock;
+
+ if (hs->state == HEADSET_STATE_DISCONNECTED ||
+ hs->state == HEADSET_STATE_CONNECTING)
+ return 0;
+
+ if (hs->dc_timer) {
+ g_source_remove(hs->dc_timer);
+ hs->dc_timer = 0;
+ }
+
+ sock = g_io_channel_unix_get_fd(hs->sco);
+
+ /* shutdown but leave the socket open and wait for hup */
+ shutdown(sock, SHUT_RDWR);
+
+ id = connect_cb_new(hs, HEADSET_STATE_CONNECTED, cb, user_data);
+
+ return id;
+}
+
+gboolean get_hfp_active(struct audio_device *dev)
+{
+ struct headset *hs = dev->headset;
+
+ return hs->hfp_active;
+}
+
+void set_hfp_active(struct audio_device *dev, gboolean active)
+{
+ struct headset *hs = dev->headset;
+
+ hs->hfp_active = active;
+}
+
+GIOChannel *headset_get_rfcomm(struct audio_device *dev)
+{
+ struct headset *hs = dev->headset;
+
+ return hs->tmp_rfcomm;
+}
+
+int headset_connect_rfcomm(struct audio_device *dev, GIOChannel *io)
+{
+ struct headset *hs = dev->headset;
+
+ if (hs->tmp_rfcomm)
+ return -EALREADY;
+
+ hs->tmp_rfcomm = g_io_channel_ref(io);
+
+ return 0;
+}
+
+int headset_connect_sco(struct audio_device *dev, GIOChannel *io)
+{
+ struct headset *hs = dev->headset;
+ struct headset_slc *slc = hs->slc;
+
+ if (hs->sco)
+ return -EISCONN;
+
+ hs->sco = g_io_channel_ref(io);
+
+ if (slc->pending_ring) {
+ ring_timer_cb(NULL);
+ ag.ring_timer = g_timeout_add_seconds(RING_INTERVAL,
+ ring_timer_cb,
+ NULL);
+ slc->pending_ring = FALSE;
+ }
+
+ return 0;
+}
+
+void headset_set_state(struct audio_device *dev, headset_state_t state)
+{
+ struct headset *hs = dev->headset;
+ struct headset_slc *slc = hs->slc;
+ gboolean value;
+ const char *state_str;
+ headset_state_t old_state = hs->state;
+ GSList *l;
+
+ if (old_state == state)
+ return;
+
+ state_str = state2str(state);
+
+ switch (state) {
+ case HEADSET_STATE_DISCONNECTED:
+ value = FALSE;
+ close_sco(dev);
+ headset_close_rfcomm(dev);
+ emit_property_changed(dev->conn, dev->path,
+ AUDIO_HEADSET_INTERFACE, "State",
+ DBUS_TYPE_STRING, &state_str);
+ g_dbus_emit_signal(dev->conn, dev->path,
+ AUDIO_HEADSET_INTERFACE,
+ "Disconnected",
+ DBUS_TYPE_INVALID);
+ if (hs->state > HEADSET_STATE_CONNECTING) {
+ emit_property_changed(dev->conn, dev->path,
+ AUDIO_HEADSET_INTERFACE, "Connected",
+ DBUS_TYPE_BOOLEAN, &value);
+ telephony_device_disconnected(dev);
+ }
+ active_devices = g_slist_remove(active_devices, dev);
+ break;
+ case HEADSET_STATE_CONNECTING:
+ emit_property_changed(dev->conn, dev->path,
+ AUDIO_HEADSET_INTERFACE, "State",
+ DBUS_TYPE_STRING, &state_str);
+ break;
+ case HEADSET_STATE_CONNECTED:
+ close_sco(dev);
+ if (hs->state != HEADSET_STATE_PLAY_IN_PROGRESS)
+ emit_property_changed(dev->conn, dev->path,
+ AUDIO_HEADSET_INTERFACE, "State",
+ DBUS_TYPE_STRING, &state_str);
+ if (hs->state < state) {
+ if (ag.features & AG_FEATURE_INBAND_RINGTONE)
+ slc->inband_ring = TRUE;
+ else
+ slc->inband_ring = FALSE;
+ g_dbus_emit_signal(dev->conn, dev->path,
+ AUDIO_HEADSET_INTERFACE,
+ "Connected",
+ DBUS_TYPE_INVALID);
+ value = TRUE;
+ emit_property_changed(dev->conn, dev->path,
+ AUDIO_HEADSET_INTERFACE,
+ "Connected",
+ DBUS_TYPE_BOOLEAN, &value);
+ active_devices = g_slist_append(active_devices, dev);
+ telephony_device_connected(dev);
+ } else if (hs->state == HEADSET_STATE_PLAYING) {
+ value = FALSE;
+ g_dbus_emit_signal(dev->conn, dev->path,
+ AUDIO_HEADSET_INTERFACE,
+ "Stopped",
+ DBUS_TYPE_INVALID);
+ emit_property_changed(dev->conn, dev->path,
+ AUDIO_HEADSET_INTERFACE,
+ "Playing",
+ DBUS_TYPE_BOOLEAN, &value);
+ }
+ break;
+ case HEADSET_STATE_PLAY_IN_PROGRESS:
+ break;
+ case HEADSET_STATE_PLAYING:
+ value = TRUE;
+ emit_property_changed(dev->conn, dev->path,
+ AUDIO_HEADSET_INTERFACE, "State",
+ DBUS_TYPE_STRING, &state_str);
+ hs->sco_id = g_io_add_watch(hs->sco,
+ G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ (GIOFunc) sco_cb, dev);
+
+ g_dbus_emit_signal(dev->conn, dev->path,
+ AUDIO_HEADSET_INTERFACE, "Playing",
+ DBUS_TYPE_INVALID);
+ emit_property_changed(dev->conn, dev->path,
+ AUDIO_HEADSET_INTERFACE, "Playing",
+ DBUS_TYPE_BOOLEAN, &value);
+
+ if (slc->sp_gain >= 0)
+ headset_send(hs, "\r\n+VGS=%u\r\n", slc->sp_gain);
+ if (slc->mic_gain >= 0)
+ headset_send(hs, "\r\n+VGM=%u\r\n", slc->mic_gain);
+ break;
+ }
+
+ hs->state = state;
+
+ DBG("State changed %s: %s -> %s", dev->path, str_state[old_state],
+ str_state[state]);
+
+ for (l = headset_callbacks; l != NULL; l = l->next) {
+ struct headset_state_callback *cb = l->data;
+ cb->cb(dev, old_state, state, cb->user_data);
+ }
+}
+
+headset_state_t headset_get_state(struct audio_device *dev)
+{
+ struct headset *hs = dev->headset;
+
+ return hs->state;
+}
+
+int headset_get_channel(struct audio_device *dev)
+{
+ struct headset *hs = dev->headset;
+
+ return hs->rfcomm_ch;
+}
+
+gboolean headset_is_active(struct audio_device *dev)
+{
+ struct headset *hs = dev->headset;
+
+ if (hs->state != HEADSET_STATE_DISCONNECTED)
+ return TRUE;
+
+ return FALSE;
+}
+
+headset_lock_t headset_get_lock(struct audio_device *dev)
+{
+ struct headset *hs = dev->headset;
+
+ return hs->lock;
+}
+
+gboolean headset_lock(struct audio_device *dev, headset_lock_t lock)
+{
+ struct headset *hs = dev->headset;
+
+ if (hs->lock & lock)
+ return FALSE;
+
+ hs->lock |= lock;
+
+ return TRUE;
+}
+
+gboolean headset_unlock(struct audio_device *dev, headset_lock_t lock)
+{
+ struct headset *hs = dev->headset;
+
+ if (!(hs->lock & lock))
+ return FALSE;
+
+ hs->lock &= ~lock;
+
+ if (hs->lock)
+ return TRUE;
+
+ if (hs->state == HEADSET_STATE_PLAYING)
+ headset_set_state(dev, HEADSET_STATE_CONNECTED);
+
+ if (hs->auto_dc) {
+ if (hs->state == HEADSET_STATE_CONNECTED)
+ hs->dc_timer = g_timeout_add_seconds(DC_TIMEOUT,
+ (GSourceFunc) hs_dc_timeout,
+ dev);
+ else
+ headset_set_state(dev, HEADSET_STATE_DISCONNECTED);
+ }
+
+ return TRUE;
+}
+
+gboolean headset_suspend(struct audio_device *dev, void *data)
+{
+ return TRUE;
+}
+
+gboolean headset_play(struct audio_device *dev, void *data)
+{
+ return TRUE;
+}
+
+int headset_get_sco_fd(struct audio_device *dev)
+{
+ struct headset *hs = dev->headset;
+
+ if (!hs->sco)
+ return -1;
+
+ return g_io_channel_unix_get_fd(hs->sco);
+}
+
+gboolean headset_get_nrec(struct audio_device *dev)
+{
+ struct headset *hs = dev->headset;
+
+ if (!hs->slc)
+ return TRUE;
+
+ return hs->slc->nrec;
+}
+
+unsigned int headset_add_nrec_cb(struct audio_device *dev,
+ headset_nrec_cb cb, void *user_data)
+{
+ struct headset *hs = dev->headset;
+ struct headset_nrec_callback *nrec_cb;
+ static unsigned int id = 0;
+
+ nrec_cb = g_new(struct headset_nrec_callback, 1);
+ nrec_cb->cb = cb;
+ nrec_cb->user_data = user_data;
+ nrec_cb->id = ++id;
+
+ hs->nrec_cbs = g_slist_prepend(hs->nrec_cbs, nrec_cb);
+
+ return nrec_cb->id;
+}
+
+gboolean headset_remove_nrec_cb(struct audio_device *dev, unsigned int id)
+{
+ struct headset *hs = dev->headset;
+ GSList *l;
+
+ for (l = hs->nrec_cbs; l != NULL; l = l->next) {
+ struct headset_nrec_callback *cb = l->data;
+ if (cb && cb->id == id) {
+ hs->nrec_cbs = g_slist_remove(hs->nrec_cbs, cb);
+ g_free(cb);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+gboolean headset_get_inband(struct audio_device *dev)
+{
+ struct headset *hs = dev->headset;
+
+ if (!hs->slc)
+ return TRUE;
+
+ return hs->slc->inband_ring;
+}
+
+gboolean headset_get_sco_hci(struct audio_device *dev)
+{
+ return sco_hci;
+}
+
+void headset_shutdown(struct audio_device *dev)
+{
+ struct pending_connect *p = dev->headset->pending;
+
+ if (p && p->msg)
+ error_connect_failed(dev->conn, p->msg, ECANCELED);
+
+ pending_connect_finalize(dev);
+ headset_set_state(dev, HEADSET_STATE_DISCONNECTED);
+}
+
+int telephony_event_ind(int index)
+{
+ if (!active_devices)
+ return -ENODEV;
+
+ if (!ag.er_ind) {
+ DBG("telephony_report_event called but events are disabled");
+ return -EINVAL;
+ }
+
+ send_foreach_headset(active_devices, hfp_cmp,
+ "\r\n+CIEV: %d,%d\r\n", index + 1,
+ ag.indicators[index].val);
+
+ return 0;
+}
+
+int telephony_response_and_hold_ind(int rh)
+{
+ if (!active_devices)
+ return -ENODEV;
+
+ ag.rh = rh;
+
+ /* If we aren't in any response and hold state don't send anything */
+ if (ag.rh < 0)
+ return 0;
+
+ send_foreach_headset(active_devices, hfp_cmp, "\r\n+BTRH: %d\r\n",
+ ag.rh);
+
+ return 0;
+}
+
+int telephony_incoming_call_ind(const char *number, int type)
+{
+ struct audio_device *dev;
+ struct headset *hs;
+ struct headset_slc *slc;
+
+ if (fast_connectable)
+ manager_set_fast_connectable(TRUE);
+
+ if (!active_devices)
+ return -ENODEV;
+
+ /* Get the latest connected device */
+ dev = active_devices->data;
+ hs = dev->headset;
+ slc = hs->slc;
+
+ if (ag.ring_timer) {
+ DBG("telephony_incoming_call_ind: already calling");
+ return -EBUSY;
+ }
+
+ /* With HSP 1.2 the RING messages should *not* be sent if inband
+ * ringtone is being used */
+ if (!hs->hfp_active && slc->inband_ring)
+ return 0;
+
+ g_free(ag.number);
+ ag.number = g_strdup(number);
+ ag.number_type = type;
+
+ if (slc->inband_ring && hs->hfp_active &&
+ hs->state != HEADSET_STATE_PLAYING) {
+ slc->pending_ring = TRUE;
+ return 0;
+ }
+
+ ring_timer_cb(NULL);
+ ag.ring_timer = g_timeout_add_seconds(RING_INTERVAL, ring_timer_cb,
+ NULL);
+
+ return 0;
+}
+
+int telephony_calling_stopped_ind(void)
+{
+ struct audio_device *dev;
+
+ if (fast_connectable)
+ manager_set_fast_connectable(FALSE);
+
+ if (ag.ring_timer) {
+ g_source_remove(ag.ring_timer);
+ ag.ring_timer = 0;
+ }
+
+ if (!active_devices)
+ return 0;
+
+ /* In case SCO isn't fully up yet */
+ dev = active_devices->data;
+
+ if (!dev->headset->slc->pending_ring && !ag.ring_timer)
+ return -EINVAL;
+
+ dev->headset->slc->pending_ring = FALSE;
+
+ return 0;
+}
+
+int telephony_ready_ind(uint32_t features,
+ const struct indicator *indicators, int rh,
+ const char *chld)
+{
+ ag.telephony_ready = TRUE;
+ ag.features = features;
+ ag.indicators = indicators;
+ ag.rh = rh;
+ ag.chld = chld;
+
+ DBG("Telephony plugin initialized");
+
+ print_ag_features(ag.features);
+
+ return 0;
+}
+
+int telephony_deinit(void)
+{
+ g_free(ag.number);
+
+ memset(&ag, 0, sizeof(ag));
+
+ ag.er_mode = 3;
+ ag.rh = BTRH_NOT_SUPPORTED;
+
+ DBG("Telephony deinitialized");
+
+ return 0;
+}
+
+int telephony_list_current_call_ind(int idx, int dir, int status, int mode,
+ int mprty, const char *number,
+ int type)
+{
+ if (!active_devices)
+ return -ENODEV;
+
+ if (number && strlen(number) > 0)
+ send_foreach_headset(active_devices, hfp_cmp,
+ "\r\n+CLCC: %d,%d,%d,%d,%d,\"%s\",%d\r\n",
+ idx, dir, status, mode, mprty, number, type);
+ else
+ send_foreach_headset(active_devices, hfp_cmp,
+ "\r\n+CLCC: %d,%d,%d,%d,%d\r\n",
+ idx, dir, status, mode, mprty);
+
+ return 0;
+}
+
+int telephony_subscriber_number_ind(const char *number, int type, int service)
+{
+ if (!active_devices)
+ return -ENODEV;
+
+ send_foreach_headset(active_devices, hfp_cmp,
+ "\r\n+CNUM: ,%s,%d,,%d\r\n",
+ number, type, service);
+
+ return 0;
+}
+
+static int cwa_cmp(struct headset *hs)
+{
+ if (!hs->hfp_active)
+ return -1;
+
+ if (hs->slc->cwa_enabled)
+ return 0;
+ else
+ return -1;
+}
+
+int telephony_call_waiting_ind(const char *number, int type)
+{
+ if (!active_devices)
+ return -ENODEV;
+
+ send_foreach_headset(active_devices, cwa_cmp,
+ "\r\n+CCWA: \"%s\",%d\r\n",
+ number, type);
+
+ return 0;
+}
+
+unsigned int headset_add_state_cb(headset_state_cb cb, void *user_data)
+{
+ struct headset_state_callback *state_cb;
+ static unsigned int id = 0;
+
+ state_cb = g_new(struct headset_state_callback, 1);
+ state_cb->cb = cb;
+ state_cb->user_data = user_data;
+ state_cb->id = ++id;
+
+ headset_callbacks = g_slist_append(headset_callbacks, state_cb);
+
+ return state_cb->id;
+}
+
+gboolean headset_remove_state_cb(unsigned int id)
+{
+ GSList *l;
+
+ for (l = headset_callbacks; l != NULL; l = l->next) {
+ struct headset_state_callback *cb = l->data;
+ if (cb && cb->id == id) {
+ headset_callbacks = g_slist_remove(headset_callbacks, cb);
+ g_free(cb);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
diff --git a/audio/headset.h b/audio/headset.h
new file mode 100644
index 0000000..7ce88c8
--- /dev/null
+++ b/audio/headset.h
@@ -0,0 +1,109 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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
+ *
+ */
+
+#define AUDIO_HEADSET_INTERFACE "org.bluez.Headset"
+
+#define DEFAULT_HS_AG_CHANNEL 12
+#define DEFAULT_HF_AG_CHANNEL 13
+
+typedef enum {
+ HEADSET_STATE_DISCONNECTED,
+ HEADSET_STATE_CONNECTING,
+ HEADSET_STATE_CONNECTED,
+ HEADSET_STATE_PLAY_IN_PROGRESS,
+ HEADSET_STATE_PLAYING
+} headset_state_t;
+
+typedef enum {
+ HEADSET_LOCK_READ = 1,
+ HEADSET_LOCK_WRITE = 1 << 1,
+} headset_lock_t;
+
+typedef void (*headset_state_cb) (struct audio_device *dev,
+ headset_state_t old_state,
+ headset_state_t new_state,
+ void *user_data);
+typedef void (*headset_nrec_cb) (struct audio_device *dev,
+ gboolean nrec,
+ void *user_data);
+
+unsigned int headset_add_state_cb(headset_state_cb cb, void *user_data);
+gboolean headset_remove_state_cb(unsigned int id);
+
+typedef void (*headset_stream_cb_t) (struct audio_device *dev, void *user_data);
+
+void headset_connect_cb(GIOChannel *chan, GError *err, gpointer user_data);
+
+GIOChannel *headset_get_rfcomm(struct audio_device *dev);
+
+struct headset *headset_init(struct audio_device *dev, uint16_t svc,
+ const char *uuidstr);
+
+void headset_unregister(struct audio_device *dev);
+
+uint32_t headset_config_init(GKeyFile *config);
+
+void headset_update(struct audio_device *dev, uint16_t svc,
+ const char *uuidstr);
+
+unsigned int headset_config_stream(struct audio_device *dev,
+ gboolean auto_dc,
+ headset_stream_cb_t cb,
+ void *user_data);
+unsigned int headset_request_stream(struct audio_device *dev,
+ headset_stream_cb_t cb,
+ void *user_data);
+unsigned int headset_suspend_stream(struct audio_device *dev,
+ headset_stream_cb_t cb,
+ void *user_data);
+gboolean headset_cancel_stream(struct audio_device *dev, unsigned int id);
+
+gboolean get_hfp_active(struct audio_device *dev);
+void set_hfp_active(struct audio_device *dev, gboolean active);
+
+void headset_set_authorized(struct audio_device *dev);
+int headset_connect_rfcomm(struct audio_device *dev, GIOChannel *chan);
+int headset_connect_sco(struct audio_device *dev, GIOChannel *io);
+
+headset_state_t headset_get_state(struct audio_device *dev);
+void headset_set_state(struct audio_device *dev, headset_state_t state);
+
+int headset_get_channel(struct audio_device *dev);
+
+int headset_get_sco_fd(struct audio_device *dev);
+gboolean headset_get_nrec(struct audio_device *dev);
+unsigned int headset_add_nrec_cb(struct audio_device *dev,
+ headset_nrec_cb cb, void *user_data);
+gboolean headset_remove_nrec_cb(struct audio_device *dev, unsigned int id);
+gboolean headset_get_inband(struct audio_device *dev);
+gboolean headset_get_sco_hci(struct audio_device *dev);
+
+gboolean headset_is_active(struct audio_device *dev);
+
+headset_lock_t headset_get_lock(struct audio_device *dev);
+gboolean headset_lock(struct audio_device *dev, headset_lock_t lock);
+gboolean headset_unlock(struct audio_device *dev, headset_lock_t lock);
+gboolean headset_suspend(struct audio_device *dev, void *data);
+gboolean headset_play(struct audio_device *dev, void *data);
+void headset_shutdown(struct audio_device *dev);
diff --git a/audio/ipc.c b/audio/ipc.c
new file mode 100644
index 0000000..1bdad78
--- /dev/null
+++ b/audio/ipc.c
@@ -0,0 +1,133 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "ipc.h"
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+/* This table contains the string representation for messages types */
+static const char *strtypes[] = {
+ "BT_REQUEST",
+ "BT_RESPONSE",
+ "BT_INDICATION",
+ "BT_ERROR",
+};
+
+/* This table contains the string representation for messages names */
+static const char *strnames[] = {
+ "BT_GET_CAPABILITIES",
+ "BT_OPEN",
+ "BT_SET_CONFIGURATION",
+ "BT_NEW_STREAM",
+ "BT_START_STREAM",
+ "BT_STOP_STREAM",
+ "BT_SUSPEND_STREAM",
+ "BT_RESUME_STREAM",
+ "BT_CONTROL",
+};
+
+int bt_audio_service_open(void)
+{
+ int sk;
+ int err;
+ struct sockaddr_un addr = {
+ AF_UNIX, BT_IPC_SOCKET_NAME
+ };
+
+ sk = socket(PF_LOCAL, SOCK_STREAM, 0);
+ if (sk < 0) {
+ err = errno;
+ fprintf(stderr, "%s: Cannot open socket: %s (%d)\n",
+ __FUNCTION__, strerror(err), err);
+ errno = err;
+ return -1;
+ }
+
+ if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ err = errno;
+ fprintf(stderr, "%s: connect() failed: %s (%d)\n",
+ __FUNCTION__, strerror(err), err);
+ close(sk);
+ errno = err;
+ return -1;
+ }
+
+ return sk;
+}
+
+int bt_audio_service_close(int sk)
+{
+ return close(sk);
+}
+
+int bt_audio_service_get_data_fd(int sk)
+{
+ char cmsg_b[CMSG_SPACE(sizeof(int))], m;
+ int err, ret;
+ struct iovec iov = { &m, sizeof(m) };
+ struct msghdr msgh;
+ struct cmsghdr *cmsg;
+
+ memset(&msgh, 0, sizeof(msgh));
+ msgh.msg_iov = &iov;
+ msgh.msg_iovlen = 1;
+ msgh.msg_control = &cmsg_b;
+ msgh.msg_controllen = CMSG_LEN(sizeof(int));
+
+ ret = recvmsg(sk, &msgh, 0);
+ if (ret < 0) {
+ err = errno;
+ fprintf(stderr, "%s: Unable to receive fd: %s (%d)\n",
+ __FUNCTION__, strerror(err), err);
+ errno = err;
+ return -1;
+ }
+
+ /* Receive auxiliary data in msgh */
+ for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(&msgh, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET
+ && cmsg->cmsg_type == SCM_RIGHTS) {
+ memcpy(&ret, CMSG_DATA(cmsg), sizeof(int));
+ return ret;
+ }
+ }
+
+ errno = EINVAL;
+ return -1;
+}
+
+const char *bt_audio_strtype(uint8_t type)
+{
+ if (type >= ARRAY_SIZE(strtypes))
+ return NULL;
+
+ return strtypes[type];
+}
+
+const char *bt_audio_strname(uint8_t name)
+{
+ if (name >= ARRAY_SIZE(strnames))
+ return NULL;
+
+ return strnames[name];
+}
diff --git a/audio/ipc.h b/audio/ipc.h
new file mode 100644
index 0000000..d69b97e
--- /dev/null
+++ b/audio/ipc.h
@@ -0,0 +1,360 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+/*
+ Message sequence chart of streaming sequence for A2DP transport
+
+ Audio daemon User
+ on snd_pcm_open
+ <--BT_GET_CAPABILITIES_REQ
+
+ BT_GET_CAPABILITIES_RSP-->
+
+ on snd_pcm_hw_params
+ <--BT_SETCONFIGURATION_REQ
+
+ BT_SET_CONFIGURATION_RSP-->
+
+ on snd_pcm_prepare
+ <--BT_START_STREAM_REQ
+
+ <Moves to streaming state>
+ BT_START_STREAM_RSP-->
+
+ BT_NEW_STREAM_IND -->
+
+ < streams data >
+ ..........
+
+ on snd_pcm_drop/snd_pcm_drain
+
+ <--BT_STOP_STREAM_REQ
+
+ <Moves to open state>
+ BT_STOP_STREAM_RSP-->
+
+ on IPC close or appl crash
+ <Moves to idle>
+
+ */
+
+#ifndef BT_AUDIOCLIENT_H
+#define BT_AUDIOCLIENT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <errno.h>
+
+#define BT_SUGGESTED_BUFFER_SIZE 512
+#define BT_IPC_SOCKET_NAME "\0/org/bluez/audio"
+
+/* Generic message header definition, except for RESPONSE messages */
+typedef struct {
+ uint8_t type;
+ uint8_t name;
+ uint16_t length;
+} __attribute__ ((packed)) bt_audio_msg_header_t;
+
+typedef struct {
+ bt_audio_msg_header_t h;
+ uint8_t posix_errno;
+} __attribute__ ((packed)) bt_audio_error_t;
+
+/* Message types */
+#define BT_REQUEST 0
+#define BT_RESPONSE 1
+#define BT_INDICATION 2
+#define BT_ERROR 3
+
+/* Messages names */
+#define BT_GET_CAPABILITIES 0
+#define BT_OPEN 1
+#define BT_SET_CONFIGURATION 2
+#define BT_NEW_STREAM 3
+#define BT_START_STREAM 4
+#define BT_STOP_STREAM 5
+#define BT_CLOSE 6
+#define BT_CONTROL 7
+#define BT_DELAY_REPORT 8
+
+#define BT_CAPABILITIES_TRANSPORT_A2DP 0
+#define BT_CAPABILITIES_TRANSPORT_SCO 1
+#define BT_CAPABILITIES_TRANSPORT_ANY 2
+
+#define BT_CAPABILITIES_ACCESS_MODE_READ 1
+#define BT_CAPABILITIES_ACCESS_MODE_WRITE 2
+#define BT_CAPABILITIES_ACCESS_MODE_READWRITE 3
+
+#define BT_FLAG_AUTOCONNECT 1
+
+struct bt_get_capabilities_req {
+ bt_audio_msg_header_t h;
+ char source[18]; /* Address of the local Device */
+ char destination[18];/* Address of the remote Device */
+ char object[128]; /* DBus object path */
+ uint8_t transport; /* Requested transport */
+ uint8_t flags; /* Requested flags */
+ uint8_t seid; /* Requested capability configuration */
+} __attribute__ ((packed));
+
+/**
+ * SBC Codec parameters as per A2DP profile 1.0 § 4.3
+ */
+
+/* A2DP seid are 6 bytes long so HSP/HFP are assigned to 7-8 bits */
+#define BT_A2DP_SEID_RANGE (1 << 6) - 1
+
+#define BT_A2DP_SBC_SOURCE 0x00
+#define BT_A2DP_SBC_SINK 0x01
+#define BT_A2DP_MPEG12_SOURCE 0x02
+#define BT_A2DP_MPEG12_SINK 0x03
+#define BT_A2DP_MPEG24_SOURCE 0x04
+#define BT_A2DP_MPEG24_SINK 0x05
+#define BT_A2DP_ATRAC_SOURCE 0x06
+#define BT_A2DP_ATRAC_SINK 0x07
+#define BT_A2DP_UNKNOWN_SOURCE 0x08
+#define BT_A2DP_UNKNOWN_SINK 0x09
+
+#define BT_SBC_SAMPLING_FREQ_16000 (1 << 3)
+#define BT_SBC_SAMPLING_FREQ_32000 (1 << 2)
+#define BT_SBC_SAMPLING_FREQ_44100 (1 << 1)
+#define BT_SBC_SAMPLING_FREQ_48000 1
+
+#define BT_A2DP_CHANNEL_MODE_MONO (1 << 3)
+#define BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL (1 << 2)
+#define BT_A2DP_CHANNEL_MODE_STEREO (1 << 1)
+#define BT_A2DP_CHANNEL_MODE_JOINT_STEREO 1
+
+#define BT_A2DP_BLOCK_LENGTH_4 (1 << 3)
+#define BT_A2DP_BLOCK_LENGTH_8 (1 << 2)
+#define BT_A2DP_BLOCK_LENGTH_12 (1 << 1)
+#define BT_A2DP_BLOCK_LENGTH_16 1
+
+#define BT_A2DP_SUBBANDS_4 (1 << 1)
+#define BT_A2DP_SUBBANDS_8 1
+
+#define BT_A2DP_ALLOCATION_SNR (1 << 1)
+#define BT_A2DP_ALLOCATION_LOUDNESS 1
+
+#define BT_MPEG_SAMPLING_FREQ_16000 (1 << 5)
+#define BT_MPEG_SAMPLING_FREQ_22050 (1 << 4)
+#define BT_MPEG_SAMPLING_FREQ_24000 (1 << 3)
+#define BT_MPEG_SAMPLING_FREQ_32000 (1 << 2)
+#define BT_MPEG_SAMPLING_FREQ_44100 (1 << 1)
+#define BT_MPEG_SAMPLING_FREQ_48000 1
+
+#define BT_MPEG_LAYER_1 (1 << 2)
+#define BT_MPEG_LAYER_2 (1 << 1)
+#define BT_MPEG_LAYER_3 1
+
+#define BT_HFP_CODEC_PCM 0x00
+
+#define BT_PCM_FLAG_NREC 0x01
+#define BT_PCM_FLAG_PCM_ROUTING 0x02
+
+#define BT_WRITE_LOCK (1 << 1)
+#define BT_READ_LOCK 1
+
+typedef struct {
+ uint8_t seid;
+ uint8_t transport;
+ uint8_t type;
+ uint8_t length;
+ uint8_t configured;
+ uint8_t lock;
+ uint8_t data[0];
+} __attribute__ ((packed)) codec_capabilities_t;
+
+typedef struct {
+ codec_capabilities_t capability;
+ uint8_t channel_mode;
+ uint8_t frequency;
+ uint8_t allocation_method;
+ uint8_t subbands;
+ uint8_t block_length;
+ uint8_t min_bitpool;
+ uint8_t max_bitpool;
+} __attribute__ ((packed)) sbc_capabilities_t;
+
+typedef struct {
+ codec_capabilities_t capability;
+ uint8_t channel_mode;
+ uint8_t crc;
+ uint8_t layer;
+ uint8_t frequency;
+ uint8_t mpf;
+ uint16_t bitrate;
+} __attribute__ ((packed)) mpeg_capabilities_t;
+
+typedef struct {
+ codec_capabilities_t capability;
+ uint8_t flags;
+ uint16_t sampling_rate;
+} __attribute__ ((packed)) pcm_capabilities_t;
+
+struct bt_get_capabilities_rsp {
+ bt_audio_msg_header_t h;
+ char source[18]; /* Address of the local Device */
+ char destination[18];/* Address of the remote Device */
+ char object[128]; /* DBus object path */
+ uint8_t data[0]; /* First codec_capabilities_t */
+} __attribute__ ((packed));
+
+struct bt_open_req {
+ bt_audio_msg_header_t h;
+ char source[18]; /* Address of the local Device */
+ char destination[18];/* Address of the remote Device */
+ char object[128]; /* DBus object path */
+ uint8_t seid; /* Requested capability configuration to lock */
+ uint8_t lock; /* Requested lock */
+} __attribute__ ((packed));
+
+struct bt_open_rsp {
+ bt_audio_msg_header_t h;
+ char source[18]; /* Address of the local Device */
+ char destination[18];/* Address of the remote Device */
+ char object[128]; /* DBus object path */
+} __attribute__ ((packed));
+
+struct bt_set_configuration_req {
+ bt_audio_msg_header_t h;
+ codec_capabilities_t codec; /* Requested codec */
+} __attribute__ ((packed));
+
+struct bt_set_configuration_rsp {
+ bt_audio_msg_header_t h;
+ uint16_t link_mtu; /* Max length that transport supports */
+} __attribute__ ((packed));
+
+#define BT_STREAM_ACCESS_READ 0
+#define BT_STREAM_ACCESS_WRITE 1
+#define BT_STREAM_ACCESS_READWRITE 2
+struct bt_start_stream_req {
+ bt_audio_msg_header_t h;
+} __attribute__ ((packed));
+
+struct bt_start_stream_rsp {
+ bt_audio_msg_header_t h;
+} __attribute__ ((packed));
+
+/* This message is followed by one byte of data containing the stream data fd
+ as ancilliary data */
+struct bt_new_stream_ind {
+ bt_audio_msg_header_t h;
+} __attribute__ ((packed));
+
+struct bt_stop_stream_req {
+ bt_audio_msg_header_t h;
+} __attribute__ ((packed));
+
+struct bt_stop_stream_rsp {
+ bt_audio_msg_header_t h;
+} __attribute__ ((packed));
+
+struct bt_close_req {
+ bt_audio_msg_header_t h;
+} __attribute__ ((packed));
+
+struct bt_close_rsp {
+ bt_audio_msg_header_t h;
+} __attribute__ ((packed));
+
+struct bt_suspend_stream_ind {
+ bt_audio_msg_header_t h;
+} __attribute__ ((packed));
+
+struct bt_resume_stream_ind {
+ bt_audio_msg_header_t h;
+} __attribute__ ((packed));
+
+#define BT_CONTROL_KEY_POWER 0x40
+#define BT_CONTROL_KEY_VOL_UP 0x41
+#define BT_CONTROL_KEY_VOL_DOWN 0x42
+#define BT_CONTROL_KEY_MUTE 0x43
+#define BT_CONTROL_KEY_PLAY 0x44
+#define BT_CONTROL_KEY_STOP 0x45
+#define BT_CONTROL_KEY_PAUSE 0x46
+#define BT_CONTROL_KEY_RECORD 0x47
+#define BT_CONTROL_KEY_REWIND 0x48
+#define BT_CONTROL_KEY_FAST_FORWARD 0x49
+#define BT_CONTROL_KEY_EJECT 0x4A
+#define BT_CONTROL_KEY_FORWARD 0x4B
+#define BT_CONTROL_KEY_BACKWARD 0x4C
+
+struct bt_control_req {
+ bt_audio_msg_header_t h;
+ uint8_t mode; /* Control Mode */
+ uint8_t key; /* Control Key */
+} __attribute__ ((packed));
+
+struct bt_control_rsp {
+ bt_audio_msg_header_t h;
+ uint8_t mode; /* Control Mode */
+ uint8_t key; /* Control Key */
+} __attribute__ ((packed));
+
+struct bt_control_ind {
+ bt_audio_msg_header_t h;
+ uint8_t mode; /* Control Mode */
+ uint8_t key; /* Control Key */
+} __attribute__ ((packed));
+
+struct bt_delay_report_req {
+ bt_audio_msg_header_t h;
+ uint16_t delay;
+} __attribute__ ((packed));
+
+struct bt_delay_report_ind {
+ bt_audio_msg_header_t h;
+ uint16_t delay;
+} __attribute__ ((packed));
+
+/* Function declaration */
+
+/* Opens a connection to the audio service: return a socket descriptor */
+int bt_audio_service_open(void);
+
+/* Closes a connection to the audio service */
+int bt_audio_service_close(int sk);
+
+/* Receives stream data file descriptor : must be called after a
+BT_STREAMFD_IND message is returned */
+int bt_audio_service_get_data_fd(int sk);
+
+/* Human readable message type string */
+const char *bt_audio_strtype(uint8_t type);
+
+/* Human readable message name string */
+const char *bt_audio_strname(uint8_t name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BT_AUDIOCLIENT_H */
diff --git a/audio/main.c b/audio/main.c
new file mode 100644
index 0000000..745c307
--- /dev/null
+++ b/audio/main.c
@@ -0,0 +1,194 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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 <errno.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "glib-helper.h"
+#include "btio.h"
+#include "plugin.h"
+#include "log.h"
+#include "device.h"
+#include "headset.h"
+#include "manager.h"
+#include "gateway.h"
+
+static GIOChannel *sco_server = NULL;
+
+static GKeyFile *load_config_file(const char *file)
+{
+ GError *err = NULL;
+ GKeyFile *keyfile;
+
+ keyfile = g_key_file_new();
+
+ g_key_file_set_list_separator(keyfile, ',');
+
+ if (!g_key_file_load_from_file(keyfile, file, 0, &err)) {
+ error("Parsing %s failed: %s", file, err->message);
+ g_error_free(err);
+ g_key_file_free(keyfile);
+ return NULL;
+ }
+
+ return keyfile;
+}
+
+static void sco_server_cb(GIOChannel *chan, GError *err, gpointer data)
+{
+ int sk;
+ struct audio_device *device;
+ char addr[18];
+ bdaddr_t src, dst;
+
+ if (err) {
+ error("sco_server_cb: %s", err->message);
+ return;
+ }
+
+ bt_io_get(chan, BT_IO_SCO, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &src,
+ BT_IO_OPT_DEST_BDADDR, &dst,
+ BT_IO_OPT_DEST, addr,
+ BT_IO_OPT_INVALID);
+ if (err) {
+ error("bt_io_get: %s", err->message);
+ goto drop;
+ }
+
+ device = manager_find_device(NULL, &src, &dst, AUDIO_HEADSET_INTERFACE,
+ FALSE);
+ if (!device)
+ device = manager_find_device(NULL, &src, &dst,
+ AUDIO_GATEWAY_INTERFACE,
+ FALSE);
+
+ if (!device)
+ goto drop;
+
+ if (device->headset) {
+ if (headset_get_state(device) < HEADSET_STATE_CONNECTED) {
+ DBG("Refusing SCO from non-connected headset");
+ goto drop;
+ }
+
+ if (!get_hfp_active(device)) {
+ error("Refusing non-HFP SCO connect attempt from %s",
+ addr);
+ goto drop;
+ }
+
+ if (headset_connect_sco(device, chan) < 0)
+ goto drop;
+
+ headset_set_state(device, HEADSET_STATE_PLAYING);
+ } else if (device->gateway) {
+ if (!gateway_is_connected(device)) {
+ DBG("Refusing SCO from non-connected AG");
+ goto drop;
+ }
+
+ if (gateway_connect_sco(device, chan) < 0)
+ goto drop;
+ } else
+ goto drop;
+
+ sk = g_io_channel_unix_get_fd(chan);
+ fcntl(sk, F_SETFL, 0);
+
+ DBG("Accepted SCO connection from %s", addr);
+
+ return;
+
+drop:
+ g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+static DBusConnection *connection;
+
+static int audio_init(void)
+{
+ GKeyFile *config;
+ gboolean enable_sco;
+
+ connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+ if (connection == NULL)
+ return -EIO;
+
+ config = load_config_file(CONFIGDIR "/audio.conf");
+
+ if (audio_manager_init(connection, config, &enable_sco) < 0)
+ goto failed;
+
+ if (!enable_sco)
+ return 0;
+
+ sco_server = bt_io_listen(BT_IO_SCO, sco_server_cb, NULL, NULL,
+ NULL, NULL,
+ BT_IO_OPT_INVALID);
+ if (!sco_server) {
+ error("Unable to start SCO server socket");
+ goto failed;
+ }
+
+ return 0;
+
+failed:
+ audio_manager_exit();
+
+ if (connection) {
+ dbus_connection_unref(connection);
+ connection = NULL;
+ }
+
+ return -EIO;
+}
+
+static void audio_exit(void)
+{
+ if (sco_server) {
+ g_io_channel_shutdown(sco_server, TRUE, NULL);
+ g_io_channel_unref(sco_server);
+ sco_server = NULL;
+ }
+
+ audio_manager_exit();
+
+ dbus_connection_unref(connection);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(audio, VERSION,
+ BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, audio_init, audio_exit)
diff --git a/audio/manager.c b/audio/manager.c
new file mode 100644
index 0000000..7e206be
--- /dev/null
+++ b/audio/manager.c
@@ -0,0 +1,1415 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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 <stdlib.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <signal.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "glib-helper.h"
+#include "btio.h"
+#include "../src/adapter.h"
+#include "../src/manager.h"
+#include "../src/device.h"
+
+#include "log.h"
+#include "textfile.h"
+#include "ipc.h"
+#include "device.h"
+#include "error.h"
+#include "avdtp.h"
+#include "media.h"
+#include "a2dp.h"
+#include "headset.h"
+#include "gateway.h"
+#include "sink.h"
+#include "source.h"
+#include "control.h"
+#include "manager.h"
+#include "sdpd.h"
+#include "telephony.h"
+#include "unix.h"
+
+#ifndef DBUS_TYPE_UNIX_FD
+#define DBUS_TYPE_UNIX_FD -1
+#endif
+
+typedef enum {
+ HEADSET = 1 << 0,
+ GATEWAY = 1 << 1,
+ SINK = 1 << 2,
+ SOURCE = 1 << 3,
+ CONTROL = 1 << 4,
+ TARGET = 1 << 5,
+ INVALID = 1 << 6
+} audio_service_type;
+
+typedef enum {
+ GENERIC_AUDIO = 0,
+ ADVANCED_AUDIO,
+ AV_REMOTE,
+ GET_RECORDS
+} audio_sdp_state_t;
+
+struct audio_adapter {
+ struct btd_adapter *btd_adapter;
+ gboolean powered;
+ uint32_t hsp_ag_record_id;
+ uint32_t hfp_ag_record_id;
+ uint32_t hfp_hs_record_id;
+ GIOChannel *hsp_ag_server;
+ GIOChannel *hfp_ag_server;
+ GIOChannel *hfp_hs_server;
+ gint ref;
+};
+
+static gboolean auto_connect = TRUE;
+static int max_connected_headsets = 1;
+static DBusConnection *connection = NULL;
+static GKeyFile *config = NULL;
+static GSList *adapters = NULL;
+static GSList *devices = NULL;
+
+static struct enabled_interfaces enabled = {
+ .hfp = TRUE,
+ .headset = TRUE,
+ .gateway = FALSE,
+ .sink = TRUE,
+ .source = FALSE,
+ .control = TRUE,
+ .socket = TRUE,
+ .media = FALSE
+};
+
+static struct audio_adapter *find_adapter(GSList *list,
+ struct btd_adapter *btd_adapter)
+{
+ GSList *l;
+
+ for (l = list; l; l = l->next) {
+ struct audio_adapter *adapter = l->data;
+
+ if (adapter->btd_adapter == btd_adapter)
+ return adapter;
+ }
+
+ return NULL;
+}
+
+gboolean server_is_enabled(bdaddr_t *src, uint16_t svc)
+{
+ switch (svc) {
+ case HEADSET_SVCLASS_ID:
+ return enabled.headset;
+ case HEADSET_AGW_SVCLASS_ID:
+ return FALSE;
+ case HANDSFREE_SVCLASS_ID:
+ return enabled.headset && enabled.hfp;
+ case HANDSFREE_AGW_SVCLASS_ID:
+ return enabled.gateway;
+ case AUDIO_SINK_SVCLASS_ID:
+ return enabled.sink;
+ case AUDIO_SOURCE_SVCLASS_ID:
+ return enabled.source;
+ case AV_REMOTE_TARGET_SVCLASS_ID:
+ case AV_REMOTE_SVCLASS_ID:
+ return enabled.control;
+ default:
+ return FALSE;
+ }
+}
+
+static void handle_uuid(const char *uuidstr, struct audio_device *device)
+{
+ uuid_t uuid;
+ uint16_t uuid16;
+
+ if (bt_string2uuid(&uuid, uuidstr) < 0) {
+ error("%s not detected as an UUID-128", uuidstr);
+ return;
+ }
+
+ if (!sdp_uuid128_to_uuid(&uuid) && uuid.type != SDP_UUID16) {
+ error("Could not convert %s to a UUID-16", uuidstr);
+ return;
+ }
+
+ uuid16 = uuid.value.uuid16;
+
+ if (!server_is_enabled(&device->src, uuid16)) {
+ DBG("server not enabled for %s (0x%04x)", uuidstr, uuid16);
+ return;
+ }
+
+ switch (uuid16) {
+ case HEADSET_SVCLASS_ID:
+ DBG("Found Headset record");
+ if (device->headset)
+ headset_update(device, uuid16, uuidstr);
+ else
+ device->headset = headset_init(device, uuid16,
+ uuidstr);
+ break;
+ case HEADSET_AGW_SVCLASS_ID:
+ DBG("Found Headset AG record");
+ break;
+ case HANDSFREE_SVCLASS_ID:
+ DBG("Found Handsfree record");
+ if (device->headset)
+ headset_update(device, uuid16, uuidstr);
+ else
+ device->headset = headset_init(device, uuid16,
+ uuidstr);
+ break;
+ case HANDSFREE_AGW_SVCLASS_ID:
+ DBG("Found Handsfree AG record");
+ if (enabled.gateway && (device->gateway == NULL))
+ device->gateway = gateway_init(device);
+ break;
+ case AUDIO_SINK_SVCLASS_ID:
+ DBG("Found Audio Sink");
+ if (device->sink == NULL)
+ device->sink = sink_init(device);
+ break;
+ case AUDIO_SOURCE_SVCLASS_ID:
+ DBG("Found Audio Source");
+ if (device->source == NULL)
+ device->source = source_init(device);
+ break;
+ case AV_REMOTE_SVCLASS_ID:
+ case AV_REMOTE_TARGET_SVCLASS_ID:
+ DBG("Found AV %s", uuid16 == AV_REMOTE_SVCLASS_ID ?
+ "Remote" : "Target");
+ if (device->control)
+ control_update(device, uuid16);
+ else
+ device->control = control_init(device, uuid16);
+ if (device->sink && sink_is_active(device))
+ avrcp_connect(device);
+ break;
+ default:
+ DBG("Unrecognized UUID: 0x%04X", uuid16);
+ break;
+ }
+}
+
+static sdp_record_t *hsp_ag_record(uint8_t ch)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, svclass_uuid, ga_svclass_uuid;
+ uuid_t l2cap_uuid, rfcomm_uuid;
+ sdp_profile_desc_t profile;
+ sdp_record_t *record;
+ sdp_list_t *aproto, *proto[2];
+ sdp_data_t *channel;
+
+ record = sdp_record_alloc();
+ if (!record)
+ return NULL;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(record, root);
+
+ sdp_uuid16_create(&svclass_uuid, HEADSET_AGW_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &svclass_uuid);
+ sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
+ svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+ sdp_set_service_classes(record, svclass_id);
+
+ sdp_uuid16_create(&profile.uuid, HEADSET_PROFILE_ID);
+ profile.version = 0x0102;
+ pfseq = sdp_list_append(0, &profile);
+ sdp_set_profile_descs(record, pfseq);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap_uuid);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto[1] = sdp_list_append(0, &rfcomm_uuid);
+ channel = sdp_data_alloc(SDP_UINT8, &ch);
+ proto[1] = sdp_list_append(proto[1], channel);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(record, aproto);
+
+ sdp_set_info_attr(record, "Headset Audio Gateway", 0, 0);
+
+ sdp_data_free(channel);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(pfseq, 0);
+ sdp_list_free(aproto, 0);
+ sdp_list_free(root, 0);
+ sdp_list_free(svclass_id, 0);
+
+ return record;
+}
+
+static sdp_record_t *hfp_hs_record(uint8_t ch)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, svclass_uuid, ga_svclass_uuid;
+ uuid_t l2cap_uuid, rfcomm_uuid;
+ sdp_profile_desc_t profile;
+ sdp_record_t *record;
+ sdp_list_t *aproto, *proto[2];
+ sdp_data_t *channel;
+
+ record = sdp_record_alloc();
+ if (!record)
+ return NULL;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(record, root);
+
+ sdp_uuid16_create(&svclass_uuid, HANDSFREE_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &svclass_uuid);
+ sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
+ svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+ sdp_set_service_classes(record, svclass_id);
+
+ sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID);
+ profile.version = 0x0105;
+ pfseq = sdp_list_append(0, &profile);
+ sdp_set_profile_descs(record, pfseq);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap_uuid);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto[1] = sdp_list_append(0, &rfcomm_uuid);
+ channel = sdp_data_alloc(SDP_UINT8, &ch);
+ proto[1] = sdp_list_append(proto[1], channel);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(record, aproto);
+
+ sdp_set_info_attr(record, "Hands-Free", 0, 0);
+
+ sdp_data_free(channel);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(pfseq, 0);
+ sdp_list_free(aproto, 0);
+ sdp_list_free(root, 0);
+ sdp_list_free(svclass_id, 0);
+
+ return record;
+}
+
+static sdp_record_t *hfp_ag_record(uint8_t ch, uint32_t feat)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, svclass_uuid, ga_svclass_uuid;
+ uuid_t l2cap_uuid, rfcomm_uuid;
+ sdp_profile_desc_t profile;
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t *record;
+ sdp_data_t *channel, *features;
+ uint8_t netid = 0x01;
+ uint16_t sdpfeat;
+ sdp_data_t *network;
+
+ record = sdp_record_alloc();
+ if (!record)
+ return NULL;
+
+ network = sdp_data_alloc(SDP_UINT8, &netid);
+ if (!network) {
+ sdp_record_free(record);
+ return NULL;
+ }
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(record, root);
+
+ sdp_uuid16_create(&svclass_uuid, HANDSFREE_AGW_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &svclass_uuid);
+ sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
+ svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+ sdp_set_service_classes(record, svclass_id);
+
+ sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID);
+ profile.version = 0x0105;
+ pfseq = sdp_list_append(0, &profile);
+ sdp_set_profile_descs(record, pfseq);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap_uuid);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto[1] = sdp_list_append(0, &rfcomm_uuid);
+ channel = sdp_data_alloc(SDP_UINT8, &ch);
+ proto[1] = sdp_list_append(proto[1], channel);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ sdpfeat = (uint16_t) feat & 0xF;
+ features = sdp_data_alloc(SDP_UINT16, &sdpfeat);
+ sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(record, aproto);
+
+ sdp_set_info_attr(record, "Hands-Free Audio Gateway", 0, 0);
+
+ sdp_attr_add(record, SDP_ATTR_EXTERNAL_NETWORK, network);
+
+ sdp_data_free(channel);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(pfseq, 0);
+ sdp_list_free(aproto, 0);
+ sdp_list_free(root, 0);
+ sdp_list_free(svclass_id, 0);
+
+ return record;
+}
+
+static void headset_auth_cb(DBusError *derr, void *user_data)
+{
+ struct audio_device *device = user_data;
+ GError *err = NULL;
+ GIOChannel *io;
+
+ if (device->hs_preauth_id) {
+ g_source_remove(device->hs_preauth_id);
+ device->hs_preauth_id = 0;
+ }
+
+ if (derr && dbus_error_is_set(derr)) {
+ error("Access denied: %s", derr->message);
+ headset_set_state(device, HEADSET_STATE_DISCONNECTED);
+ return;
+ }
+
+ io = headset_get_rfcomm(device);
+
+ if (!bt_io_accept(io, headset_connect_cb, device, NULL, &err)) {
+ error("bt_io_accept: %s", err->message);
+ g_error_free(err);
+ headset_set_state(device, HEADSET_STATE_DISCONNECTED);
+ return;
+ }
+}
+
+static gboolean hs_preauth_cb(GIOChannel *chan, GIOCondition cond,
+ gpointer user_data)
+{
+ struct audio_device *device = user_data;
+
+ DBG("Headset disconnected during authorization");
+
+ audio_device_cancel_authorization(device, headset_auth_cb, device);
+
+ headset_set_state(device, HEADSET_STATE_DISCONNECTED);
+
+ device->hs_preauth_id = 0;
+
+ return FALSE;
+}
+
+static void ag_confirm(GIOChannel *chan, gpointer data)
+{
+ const char *server_uuid, *remote_uuid;
+ struct audio_device *device;
+ gboolean hfp_active;
+ bdaddr_t src, dst;
+ int perr;
+ GError *err = NULL;
+ uint8_t ch;
+
+ bt_io_get(chan, BT_IO_RFCOMM, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &src,
+ BT_IO_OPT_DEST_BDADDR, &dst,
+ BT_IO_OPT_CHANNEL, &ch,
+ BT_IO_OPT_INVALID);
+ if (err) {
+ error("%s", err->message);
+ g_error_free(err);
+ goto drop;
+ }
+
+ if (ch == DEFAULT_HS_AG_CHANNEL) {
+ hfp_active = FALSE;
+ server_uuid = HSP_AG_UUID;
+ remote_uuid = HSP_HS_UUID;
+ } else {
+ hfp_active = TRUE;
+ server_uuid = HFP_AG_UUID;
+ remote_uuid = HFP_HS_UUID;
+ }
+
+ device = manager_get_device(&src, &dst, TRUE);
+ if (!device)
+ goto drop;
+
+ if (!manager_allow_headset_connection(device)) {
+ DBG("Refusing headset: too many existing connections");
+ goto drop;
+ }
+
+ if (!device->headset) {
+ btd_device_add_uuid(device->btd_dev, remote_uuid);
+ if (!device->headset)
+ goto drop;
+ }
+
+ if (headset_get_state(device) > HEADSET_STATE_DISCONNECTED) {
+ DBG("Refusing new connection since one already exists");
+ goto drop;
+ }
+
+ set_hfp_active(device, hfp_active);
+
+ if (headset_connect_rfcomm(device, chan) < 0) {
+ error("headset_connect_rfcomm failed");
+ goto drop;
+ }
+
+ headset_set_state(device, HEADSET_STATE_CONNECTING);
+
+ perr = audio_device_request_authorization(device, server_uuid,
+ headset_auth_cb, device);
+ if (perr < 0) {
+ DBG("Authorization denied: %s", strerror(-perr));
+ headset_set_state(device, HEADSET_STATE_DISCONNECTED);
+ return;
+ }
+
+ device->hs_preauth_id = g_io_add_watch(chan,
+ G_IO_NVAL | G_IO_HUP | G_IO_ERR,
+ hs_preauth_cb, device);
+
+ device->auto_connect = auto_connect;
+
+ return;
+
+drop:
+ g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+static void gateway_auth_cb(DBusError *derr, void *user_data)
+{
+ struct audio_device *device = user_data;
+
+ if (derr && dbus_error_is_set(derr))
+ error("Access denied: %s", derr->message);
+ else {
+ char ag_address[18];
+
+ ba2str(&device->dst, ag_address);
+ DBG("Accepted AG connection from %s for %s",
+ ag_address, device->path);
+
+ gateway_start_service(device);
+ }
+}
+
+static void hf_io_cb(GIOChannel *chan, gpointer data)
+{
+ bdaddr_t src, dst;
+ GError *err = NULL;
+ uint8_t ch;
+ const char *server_uuid, *remote_uuid;
+ uint16_t svclass;
+ struct audio_device *device;
+ int perr;
+
+ bt_io_get(chan, BT_IO_RFCOMM, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &src,
+ BT_IO_OPT_DEST_BDADDR, &dst,
+ BT_IO_OPT_CHANNEL, &ch,
+ BT_IO_OPT_INVALID);
+
+ if (err) {
+ error("%s", err->message);
+ g_error_free(err);
+ return;
+ }
+
+ server_uuid = HFP_AG_UUID;
+ remote_uuid = HFP_HS_UUID;
+ svclass = HANDSFREE_AGW_SVCLASS_ID;
+
+ device = manager_get_device(&src, &dst, TRUE);
+ if (!device)
+ goto drop;
+
+ if (!device->gateway) {
+ btd_device_add_uuid(device->btd_dev, remote_uuid);
+ if (!device->gateway)
+ goto drop;
+ }
+
+ if (gateway_is_connected(device)) {
+ DBG("Refusing new connection since one already exists");
+ goto drop;
+ }
+
+ if (gateway_connect_rfcomm(device, chan) < 0) {
+ error("Allocating new GIOChannel failed!");
+ goto drop;
+ }
+
+ perr = audio_device_request_authorization(device, server_uuid,
+ gateway_auth_cb, device);
+ if (perr < 0) {
+ DBG("Authorization denied!");
+ goto drop;
+ }
+
+ return;
+
+drop:
+ g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+static int headset_server_init(struct audio_adapter *adapter)
+{
+ uint8_t chan = DEFAULT_HS_AG_CHANNEL;
+ sdp_record_t *record;
+ gboolean master = TRUE;
+ GError *err = NULL;
+ uint32_t features;
+ GIOChannel *io;
+ bdaddr_t src;
+
+ if (config) {
+ gboolean tmp;
+
+ tmp = g_key_file_get_boolean(config, "General", "Master",
+ &err);
+ if (err) {
+ DBG("audio.conf: %s", err->message);
+ g_clear_error(&err);
+ } else
+ master = tmp;
+ }
+
+ adapter_get_address(adapter->btd_adapter, &src);
+
+ io = bt_io_listen(BT_IO_RFCOMM, NULL, ag_confirm, adapter, NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &src,
+ BT_IO_OPT_CHANNEL, chan,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+ BT_IO_OPT_MASTER, master,
+ BT_IO_OPT_INVALID);
+ if (!io)
+ goto failed;
+
+ adapter->hsp_ag_server = io;
+
+ record = hsp_ag_record(chan);
+ if (!record) {
+ error("Unable to allocate new service record");
+ goto failed;
+ }
+
+ if (add_record_to_server(&src, record) < 0) {
+ error("Unable to register HS AG service record");
+ sdp_record_free(record);
+ goto failed;
+ }
+ adapter->hsp_ag_record_id = record->handle;
+
+ features = headset_config_init(config);
+
+ if (!enabled.hfp)
+ return 0;
+
+ chan = DEFAULT_HF_AG_CHANNEL;
+
+ io = bt_io_listen(BT_IO_RFCOMM, NULL, ag_confirm, adapter, NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &src,
+ BT_IO_OPT_CHANNEL, chan,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+ BT_IO_OPT_MASTER, master,
+ BT_IO_OPT_INVALID);
+ if (!io)
+ goto failed;
+
+ adapter->hfp_ag_server = io;
+
+ record = hfp_ag_record(chan, features);
+ if (!record) {
+ error("Unable to allocate new service record");
+ goto failed;
+ }
+
+ if (add_record_to_server(&src, record) < 0) {
+ error("Unable to register HF AG service record");
+ sdp_record_free(record);
+ goto failed;
+ }
+ adapter->hfp_ag_record_id = record->handle;
+
+ return 0;
+
+failed:
+ error("%s", err->message);
+ g_error_free(err);
+ if (adapter->hsp_ag_server) {
+ g_io_channel_shutdown(adapter->hsp_ag_server, TRUE, NULL);
+ g_io_channel_unref(adapter->hsp_ag_server);
+ adapter->hsp_ag_server = NULL;
+ }
+
+ if (adapter->hfp_ag_server) {
+ g_io_channel_shutdown(adapter->hfp_ag_server, TRUE, NULL);
+ g_io_channel_unref(adapter->hfp_ag_server);
+ adapter->hfp_ag_server = NULL;
+ }
+
+ return -1;
+}
+
+static int gateway_server_init(struct audio_adapter *adapter)
+{
+ uint8_t chan = DEFAULT_HFP_HS_CHANNEL;
+ sdp_record_t *record;
+ gboolean master = TRUE;
+ GError *err = NULL;
+ GIOChannel *io;
+ bdaddr_t src;
+
+ if (config) {
+ gboolean tmp;
+
+ tmp = g_key_file_get_boolean(config, "General", "Master",
+ &err);
+ if (err) {
+ DBG("audio.conf: %s", err->message);
+ g_clear_error(&err);
+ } else
+ master = tmp;
+ }
+
+ adapter_get_address(adapter->btd_adapter, &src);
+
+ io = bt_io_listen(BT_IO_RFCOMM, NULL, hf_io_cb, adapter, NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &src,
+ BT_IO_OPT_CHANNEL, chan,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+ BT_IO_OPT_MASTER, master,
+ BT_IO_OPT_INVALID);
+ if (!io) {
+ error("%s", err->message);
+ g_error_free(err);
+ return -1;
+ }
+
+ adapter->hfp_hs_server = io;
+ record = hfp_hs_record(chan);
+ if (!record) {
+ error("Unable to allocate new service record");
+ return -1;
+ }
+
+ if (add_record_to_server(&src, record) < 0) {
+ error("Unable to register HFP HS service record");
+ sdp_record_free(record);
+ g_io_channel_unref(adapter->hfp_hs_server);
+ adapter->hfp_hs_server = NULL;
+ return -1;
+ }
+
+ adapter->hfp_hs_record_id = record->handle;
+
+ return 0;
+}
+
+static int audio_probe(struct btd_device *device, GSList *uuids)
+{
+ struct btd_adapter *adapter = device_get_adapter(device);
+ bdaddr_t src, dst;
+ struct audio_device *audio_dev;
+
+ adapter_get_address(adapter, &src);
+ device_get_address(device, &dst);
+
+ audio_dev = manager_get_device(&src, &dst, TRUE);
+ if (!audio_dev) {
+ DBG("unable to get a device object");
+ return -1;
+ }
+
+ g_slist_foreach(uuids, (GFunc) handle_uuid, audio_dev);
+
+ return 0;
+}
+
+static void audio_remove(struct btd_device *device)
+{
+ struct audio_device *dev;
+ const char *path;
+
+ path = device_get_path(device);
+
+ dev = manager_find_device(path, NULL, NULL, NULL, FALSE);
+ if (!dev)
+ return;
+
+ devices = g_slist_remove(devices, dev);
+
+ audio_device_unregister(dev);
+
+}
+
+static struct audio_adapter *audio_adapter_ref(struct audio_adapter *adp)
+{
+ adp->ref++;
+
+ DBG("%p: ref=%d", adp, adp->ref);
+
+ return adp;
+}
+
+static void audio_adapter_unref(struct audio_adapter *adp)
+{
+ adp->ref--;
+
+ DBG("%p: ref=%d", adp, adp->ref);
+
+ if (adp->ref > 0)
+ return;
+
+ adapters = g_slist_remove(adapters, adp);
+ btd_adapter_unref(adp->btd_adapter);
+ g_free(adp);
+}
+
+static struct audio_adapter *audio_adapter_create(struct btd_adapter *adapter)
+{
+ struct audio_adapter *adp;
+
+ adp = g_new0(struct audio_adapter, 1);
+ adp->btd_adapter = btd_adapter_ref(adapter);
+
+ return audio_adapter_ref(adp);
+}
+
+static struct audio_adapter *audio_adapter_get(struct btd_adapter *adapter)
+{
+ struct audio_adapter *adp;
+
+ adp = find_adapter(adapters, adapter);
+ if (!adp) {
+ adp = audio_adapter_create(adapter);
+ if (!adp)
+ return NULL;
+ adapters = g_slist_append(adapters, adp);
+ } else
+ audio_adapter_ref(adp);
+
+ return adp;
+}
+
+static void state_changed(struct btd_adapter *adapter, gboolean powered)
+{
+ struct audio_adapter *adp;
+ static gboolean telephony = FALSE;
+ GSList *l;
+
+ DBG("%s powered %s", adapter_get_path(adapter),
+ powered ? "on" : "off");
+
+ /* ignore powered change, adapter is powering down */
+ if (powered && adapter_powering_down(adapter))
+ return;
+
+ adp = find_adapter(adapters, adapter);
+ if (!adp)
+ return;
+
+ adp->powered = powered;
+
+ if (powered) {
+ /* telephony driver already initialized*/
+ if (telephony == TRUE)
+ return;
+ telephony_init();
+ telephony = TRUE;
+ return;
+ }
+
+ /* telephony not initialized just ignore power down */
+ if (telephony == FALSE)
+ return;
+
+ for (l = adapters; l; l = l->next) {
+ adp = l->data;
+
+ if (adp->powered == TRUE)
+ return;
+ }
+
+ telephony_exit();
+ telephony = FALSE;
+}
+
+static int headset_server_probe(struct btd_adapter *adapter)
+{
+ struct audio_adapter *adp;
+ const gchar *path = adapter_get_path(adapter);
+ int err;
+
+ DBG("path %s", path);
+
+ adp = audio_adapter_get(adapter);
+ if (!adp)
+ return -EINVAL;
+
+ err = headset_server_init(adp);
+ if (err < 0) {
+ audio_adapter_unref(adp);
+ return err;
+ }
+
+ btd_adapter_register_powered_callback(adapter, state_changed);
+ state_changed(adapter, TRUE);
+
+ return 0;
+}
+
+static void headset_server_remove(struct btd_adapter *adapter)
+{
+ struct audio_adapter *adp;
+ const gchar *path = adapter_get_path(adapter);
+
+ DBG("path %s", path);
+
+ adp = find_adapter(adapters, adapter);
+ if (!adp)
+ return;
+
+ if (adp->hsp_ag_record_id) {
+ remove_record_from_server(adp->hsp_ag_record_id);
+ adp->hsp_ag_record_id = 0;
+ }
+
+ if (adp->hsp_ag_server) {
+ g_io_channel_shutdown(adp->hsp_ag_server, TRUE, NULL);
+ g_io_channel_unref(adp->hsp_ag_server);
+ adp->hsp_ag_server = NULL;
+ }
+
+ if (adp->hfp_ag_record_id) {
+ remove_record_from_server(adp->hfp_ag_record_id);
+ adp->hfp_ag_record_id = 0;
+ }
+
+ if (adp->hfp_ag_server) {
+ g_io_channel_shutdown(adp->hfp_ag_server, TRUE, NULL);
+ g_io_channel_unref(adp->hfp_ag_server);
+ adp->hfp_ag_server = NULL;
+ }
+
+ btd_adapter_unregister_powered_callback(adapter, state_changed);
+
+ audio_adapter_unref(adp);
+}
+
+static int gateway_server_probe(struct btd_adapter *adapter)
+{
+ struct audio_adapter *adp;
+
+ adp = audio_adapter_get(adapter);
+ if (!adp)
+ return -EINVAL;
+
+ return gateway_server_init(adp);
+}
+
+static void gateway_server_remove(struct btd_adapter *adapter)
+{
+ struct audio_adapter *adp;
+ const gchar *path = adapter_get_path(adapter);
+
+ DBG("path %s", path);
+
+ adp = find_adapter(adapters, adapter);
+ if (!adp)
+ return;
+
+ if (adp->hfp_hs_record_id) {
+ remove_record_from_server(adp->hfp_hs_record_id);
+ adp->hfp_hs_record_id = 0;
+ }
+
+ if (adp->hfp_hs_server) {
+ g_io_channel_unref(adp->hfp_hs_server);
+ adp->hfp_hs_server = NULL;
+ }
+
+ audio_adapter_unref(adp);
+}
+
+static int a2dp_server_probe(struct btd_adapter *adapter)
+{
+ struct audio_adapter *adp;
+ const gchar *path = adapter_get_path(adapter);
+ bdaddr_t src;
+ int err;
+
+ DBG("path %s", path);
+
+ adp = audio_adapter_get(adapter);
+ if (!adp)
+ return -EINVAL;
+
+ adapter_get_address(adapter, &src);
+
+ err = a2dp_register(connection, &src, config);
+ if (err < 0)
+ audio_adapter_unref(adp);
+
+ return err;
+}
+
+static void a2dp_server_remove(struct btd_adapter *adapter)
+{
+ struct audio_adapter *adp;
+ const gchar *path = adapter_get_path(adapter);
+ bdaddr_t src;
+
+ DBG("path %s", path);
+
+ adp = find_adapter(adapters, adapter);
+ if (!adp)
+ return;
+
+ adapter_get_address(adapter, &src);
+ a2dp_unregister(&src);
+ audio_adapter_unref(adp);
+}
+
+static int avrcp_server_probe(struct btd_adapter *adapter)
+{
+ struct audio_adapter *adp;
+ const gchar *path = adapter_get_path(adapter);
+ bdaddr_t src;
+
+ DBG("path %s", path);
+
+ adp = audio_adapter_get(adapter);
+ if (!adp)
+ return -EINVAL;
+
+ adapter_get_address(adapter, &src);
+
+ return avrcp_register(connection, &src, config);
+}
+
+static void avrcp_server_remove(struct btd_adapter *adapter)
+{
+ struct audio_adapter *adp;
+ const gchar *path = adapter_get_path(adapter);
+ bdaddr_t src;
+
+ DBG("path %s", path);
+
+ adp = find_adapter(adapters, adapter);
+ if (!adp)
+ return;
+
+ adapter_get_address(adapter, &src);
+ avrcp_unregister(&src);
+ audio_adapter_unref(adp);
+}
+
+static int media_server_probe(struct btd_adapter *adapter)
+{
+ struct audio_adapter *adp;
+ const gchar *path = adapter_get_path(adapter);
+ bdaddr_t src;
+
+ DBG("path %s", path);
+
+ adp = audio_adapter_get(adapter);
+ if (!adp)
+ return -EINVAL;
+
+ adapter_get_address(adapter, &src);
+
+ return media_register(connection, path, &src);
+}
+
+static void media_server_remove(struct btd_adapter *adapter)
+{
+ struct audio_adapter *adp;
+ const gchar *path = adapter_get_path(adapter);
+
+ DBG("path %s", path);
+
+ adp = find_adapter(adapters, adapter);
+ if (!adp)
+ return;
+
+ media_unregister(path);
+ audio_adapter_unref(adp);
+}
+
+static struct btd_device_driver audio_driver = {
+ .name = "audio",
+ .uuids = BTD_UUIDS(HSP_HS_UUID, HFP_HS_UUID, HSP_AG_UUID, HFP_AG_UUID,
+ ADVANCED_AUDIO_UUID, A2DP_SOURCE_UUID, A2DP_SINK_UUID,
+ AVRCP_TARGET_UUID, AVRCP_REMOTE_UUID),
+ .probe = audio_probe,
+ .remove = audio_remove,
+};
+
+static struct btd_adapter_driver headset_server_driver = {
+ .name = "audio-headset",
+ .probe = headset_server_probe,
+ .remove = headset_server_remove,
+};
+
+static struct btd_adapter_driver gateway_server_driver = {
+ .name = "audio-gateway",
+ .probe = gateway_server_probe,
+ .remove = gateway_server_remove,
+};
+
+static struct btd_adapter_driver a2dp_server_driver = {
+ .name = "audio-a2dp",
+ .probe = a2dp_server_probe,
+ .remove = a2dp_server_remove,
+};
+
+static struct btd_adapter_driver avrcp_server_driver = {
+ .name = "audio-control",
+ .probe = avrcp_server_probe,
+ .remove = avrcp_server_remove,
+};
+
+static struct btd_adapter_driver media_server_driver = {
+ .name = "media",
+ .probe = media_server_probe,
+ .remove = media_server_remove,
+};
+
+int audio_manager_init(DBusConnection *conn, GKeyFile *conf,
+ gboolean *enable_sco)
+{
+ char **list;
+ int i;
+ gboolean b;
+ GError *err = NULL;
+
+ connection = dbus_connection_ref(conn);
+
+ if (!conf)
+ goto proceed;
+
+ config = conf;
+
+ list = g_key_file_get_string_list(config, "General", "Enable",
+ NULL, NULL);
+ for (i = 0; list && list[i] != NULL; i++) {
+ if (g_str_equal(list[i], "Headset"))
+ enabled.headset = TRUE;
+ else if (g_str_equal(list[i], "Gateway"))
+ enabled.gateway = TRUE;
+ else if (g_str_equal(list[i], "Sink"))
+ enabled.sink = TRUE;
+ else if (g_str_equal(list[i], "Source"))
+ enabled.source = TRUE;
+ else if (g_str_equal(list[i], "Control"))
+ enabled.control = TRUE;
+ else if (g_str_equal(list[i], "Socket"))
+ enabled.socket = TRUE;
+ else if (g_str_equal(list[i], "Media"))
+ enabled.media = TRUE;
+ }
+ g_strfreev(list);
+
+ list = g_key_file_get_string_list(config, "General", "Disable",
+ NULL, NULL);
+ for (i = 0; list && list[i] != NULL; i++) {
+ if (g_str_equal(list[i], "Headset"))
+ enabled.headset = FALSE;
+ else if (g_str_equal(list[i], "Gateway"))
+ enabled.gateway = FALSE;
+ else if (g_str_equal(list[i], "Sink"))
+ enabled.sink = FALSE;
+ else if (g_str_equal(list[i], "Source"))
+ enabled.source = FALSE;
+ else if (g_str_equal(list[i], "Control"))
+ enabled.control = FALSE;
+ else if (g_str_equal(list[i], "Socket"))
+ enabled.socket = FALSE;
+ else if (g_str_equal(list[i], "Media"))
+ enabled.media = FALSE;
+ }
+ g_strfreev(list);
+
+ b = g_key_file_get_boolean(config, "General", "AutoConnect", &err);
+ if (err) {
+ DBG("audio.conf: %s", err->message);
+ g_clear_error(&err);
+ } else
+ auto_connect = b;
+
+ b = g_key_file_get_boolean(config, "Headset", "HFP",
+ &err);
+ if (err)
+ g_clear_error(&err);
+ else
+ enabled.hfp = b;
+
+ err = NULL;
+ i = g_key_file_get_integer(config, "Headset", "MaxConnected",
+ &err);
+ if (err) {
+ DBG("audio.conf: %s", err->message);
+ g_clear_error(&err);
+ } else
+ max_connected_headsets = i;
+
+proceed:
+ if (enabled.socket)
+ unix_init();
+
+ if (enabled.media)
+ btd_register_adapter_driver(&media_server_driver);
+
+ if (enabled.headset)
+ btd_register_adapter_driver(&headset_server_driver);
+
+ if (enabled.gateway)
+ btd_register_adapter_driver(&gateway_server_driver);
+
+ if (enabled.source || enabled.sink)
+ btd_register_adapter_driver(&a2dp_server_driver);
+
+ if (enabled.control)
+ btd_register_adapter_driver(&avrcp_server_driver);
+
+ btd_register_device_driver(&audio_driver);
+
+ *enable_sco = (enabled.gateway || enabled.headset);
+
+ return 0;
+}
+
+void audio_manager_exit(void)
+{
+ /* Bail out early if we haven't been initialized */
+ if (connection == NULL)
+ return;
+
+ dbus_connection_unref(connection);
+ connection = NULL;
+
+ if (config) {
+ g_key_file_free(config);
+ config = NULL;
+ }
+
+ if (enabled.socket)
+ unix_exit();
+
+ if (enabled.media)
+ btd_unregister_adapter_driver(&media_server_driver);
+
+ if (enabled.headset)
+ btd_unregister_adapter_driver(&headset_server_driver);
+
+ if (enabled.gateway)
+ btd_unregister_adapter_driver(&gateway_server_driver);
+
+ if (enabled.source || enabled.sink)
+ btd_unregister_adapter_driver(&a2dp_server_driver);
+
+ if (enabled.control)
+ btd_unregister_adapter_driver(&avrcp_server_driver);
+
+ btd_unregister_device_driver(&audio_driver);
+}
+
+struct audio_device *manager_find_device(const char *path,
+ const bdaddr_t *src,
+ const bdaddr_t *dst,
+ const char *interface,
+ gboolean connected)
+{
+ GSList *l;
+
+ for (l = devices; l != NULL; l = l->next) {
+ struct audio_device *dev = l->data;
+
+ if ((path && (strcmp(path, "")) && strcmp(dev->path, path)))
+ continue;
+
+ if ((src && bacmp(src, BDADDR_ANY)) && bacmp(&dev->src, src))
+ continue;
+
+ if ((dst && bacmp(dst, BDADDR_ANY)) && bacmp(&dev->dst, dst))
+ continue;
+
+ if (interface && !strcmp(AUDIO_HEADSET_INTERFACE, interface)
+ && !dev->headset)
+ continue;
+
+ if (interface && !strcmp(AUDIO_GATEWAY_INTERFACE, interface)
+ && !dev->gateway)
+ continue;
+
+ if (interface && !strcmp(AUDIO_SINK_INTERFACE, interface)
+ && !dev->sink)
+ continue;
+
+ if (interface && !strcmp(AUDIO_SOURCE_INTERFACE, interface)
+ && !dev->source)
+ continue;
+
+ if (interface && !strcmp(AUDIO_CONTROL_INTERFACE, interface)
+ && !dev->control)
+ continue;
+
+ if (connected && !audio_device_is_active(dev, interface))
+ continue;
+
+ return dev;
+ }
+
+ return NULL;
+}
+
+struct audio_device *manager_get_device(const bdaddr_t *src,
+ const bdaddr_t *dst,
+ gboolean create)
+{
+ struct audio_device *dev;
+ struct btd_adapter *adapter;
+ struct btd_device *device;
+ char addr[18];
+ const char *path;
+
+ dev = manager_find_device(NULL, src, dst, NULL, FALSE);
+ if (dev)
+ return dev;
+
+ if (!create)
+ return NULL;
+
+ ba2str(src, addr);
+
+ adapter = manager_find_adapter(src);
+ if (!adapter) {
+ error("Unable to get a btd_adapter object for %s",
+ addr);
+ return NULL;
+ }
+
+ ba2str(dst, addr);
+
+ device = adapter_get_device(connection, adapter, addr);
+ if (!device) {
+ error("Unable to get btd_device object for %s", addr);
+ return NULL;
+ }
+
+ path = device_get_path(device);
+
+ dev = audio_device_register(connection, device, path, src, dst);
+ if (!dev)
+ return NULL;
+
+ devices = g_slist_append(devices, dev);
+
+ return dev;
+}
+
+gboolean manager_allow_headset_connection(struct audio_device *device)
+{
+ GSList *l;
+ int connected = 0;
+
+ for (l = devices; l != NULL; l = l->next) {
+ struct audio_device *dev = l->data;
+ struct headset *hs = dev->headset;
+
+ if (dev == device)
+ continue;
+
+ if (bacmp(&dev->src, &device->src))
+ continue;
+
+ if (!hs)
+ continue;
+
+ if (headset_get_state(dev) > HEADSET_STATE_DISCONNECTED)
+ connected++;
+
+ if (connected >= max_connected_headsets)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+void manager_set_fast_connectable(gboolean enable)
+{
+ GSList *l;
+
+ for (l = adapters; l != NULL; l = l->next) {
+ struct audio_adapter *adapter = l->data;
+
+ if (btd_adapter_set_fast_connectable(adapter->btd_adapter,
+ enable))
+ error("Changing fast connectable for hci%d failed",
+ adapter_get_dev_id(adapter->btd_adapter));
+ }
+}
diff --git a/audio/manager.h b/audio/manager.h
new file mode 100644
index 0000000..0bf7663
--- /dev/null
+++ b/audio/manager.h
@@ -0,0 +1,56 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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
+ *
+ */
+
+struct enabled_interfaces {
+ gboolean hfp;
+ gboolean headset;
+ gboolean gateway;
+ gboolean sink;
+ gboolean source;
+ gboolean control;
+ gboolean socket;
+ gboolean media;
+};
+
+int audio_manager_init(DBusConnection *conn, GKeyFile *config,
+ gboolean *enable_sco);
+void audio_manager_exit(void);
+
+gboolean server_is_enabled(bdaddr_t *src, uint16_t svc);
+
+struct audio_device *manager_find_device(const char *path,
+ const bdaddr_t *src,
+ const bdaddr_t *dst,
+ const char *interface,
+ gboolean connected);
+
+struct audio_device *manager_get_device(const bdaddr_t *src,
+ const bdaddr_t *dst,
+ gboolean create);
+
+gboolean manager_allow_headset_connection(struct audio_device *device);
+
+/* TRUE to enable fast connectable and FALSE to disable fast connectable for all
+ * audio adapters. */
+void manager_set_fast_connectable(gboolean enable);
diff --git a/audio/media.c b/audio/media.c
new file mode 100644
index 0000000..0efb0a8
--- /dev/null
+++ b/audio/media.c
@@ -0,0 +1,714 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2007 Nokia Corporation
+ * Copyright (C) 2004-2009 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 <errno.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "../src/adapter.h"
+#include "../src/dbus-common.h"
+
+#include "log.h"
+#include "error.h"
+#include "device.h"
+#include "avdtp.h"
+#include "media.h"
+#include "transport.h"
+#include "a2dp.h"
+#include "headset.h"
+#include "manager.h"
+
+#ifndef DBUS_TYPE_UNIX_FD
+#define DBUS_TYPE_UNIX_FD -1
+#endif
+
+#define MEDIA_INTERFACE "org.bluez.Media"
+#define MEDIA_ENDPOINT_INTERFACE "org.bluez.MediaEndpoint"
+
+#define REQUEST_TIMEOUT (3 * 1000) /* 3 seconds */
+
+struct media_adapter {
+ bdaddr_t src; /* Adapter address */
+ char *path; /* Adapter path */
+ DBusConnection *conn; /* Adapter connection */
+ GSList *endpoints; /* Endpoints list */
+};
+
+struct endpoint_request {
+ DBusMessage *msg;
+ DBusPendingCall *call;
+ media_endpoint_cb_t cb;
+ void *user_data;
+};
+
+struct media_endpoint {
+ struct a2dp_sep *sep;
+ char *sender; /* Endpoint DBus bus id */
+ char *path; /* Endpoint object path */
+ char *uuid; /* Endpoint property UUID */
+ uint8_t codec; /* Endpoint codec */
+ uint8_t *capabilities; /* Endpoint property capabilities */
+ size_t size; /* Endpoint capabilities size */
+ guint hs_watch;
+ guint watch;
+ struct endpoint_request *request;
+ struct media_transport *transport;
+ struct media_adapter *adapter;
+};
+
+static GSList *adapters = NULL;
+
+static void endpoint_request_free(struct endpoint_request *request)
+{
+ if (request->call)
+ dbus_pending_call_unref(request->call);
+
+ dbus_message_unref(request->msg);
+ g_free(request);
+}
+
+static void media_endpoint_cancel(struct media_endpoint *endpoint)
+{
+ struct endpoint_request *request = endpoint->request;
+
+ if (request->call)
+ dbus_pending_call_cancel(request->call);
+
+ endpoint_request_free(request);
+ endpoint->request = NULL;
+}
+
+static void media_endpoint_remove(struct media_endpoint *endpoint)
+{
+ struct media_adapter *adapter = endpoint->adapter;
+
+ if (g_slist_find(adapter->endpoints, endpoint) == NULL)
+ return;
+
+ info("Endpoint unregistered: sender=%s path=%s", endpoint->sender,
+ endpoint->path);
+
+ adapter->endpoints = g_slist_remove(adapter->endpoints, endpoint);
+
+ if (endpoint->sep)
+ a2dp_remove_sep(endpoint->sep);
+
+ if (endpoint->hs_watch)
+ headset_remove_state_cb(endpoint->hs_watch);
+
+ if (endpoint->request)
+ media_endpoint_cancel(endpoint);
+
+ if (endpoint->transport)
+ media_transport_destroy(endpoint->transport);
+
+ g_dbus_remove_watch(adapter->conn, endpoint->watch);
+ g_free(endpoint->capabilities);
+ g_free(endpoint->sender);
+ g_free(endpoint->path);
+ g_free(endpoint->uuid);
+ g_free(endpoint);
+}
+
+static void media_endpoint_exit(DBusConnection *connection, void *user_data)
+{
+ struct media_endpoint *endpoint = user_data;
+
+ endpoint->watch = 0;
+ media_endpoint_remove(endpoint);
+}
+
+static void headset_setconf_cb(struct media_endpoint *endpoint, void *ret,
+ int size, void *user_data)
+{
+ struct audio_device *dev = user_data;
+
+ if (ret != NULL)
+ return;
+
+ headset_set_state(dev, HEADSET_STATE_DISCONNECTED);
+}
+
+static void headset_state_changed(struct audio_device *dev,
+ headset_state_t old_state,
+ headset_state_t new_state,
+ void *user_data)
+{
+ struct media_endpoint *endpoint = user_data;
+
+ DBG("");
+
+ switch (new_state) {
+ case HEADSET_STATE_DISCONNECTED:
+ media_endpoint_clear_configuration(endpoint);
+ break;
+ case HEADSET_STATE_CONNECTING:
+ media_endpoint_set_configuration(endpoint, dev, NULL, 0,
+ headset_setconf_cb, dev);
+ break;
+ case HEADSET_STATE_CONNECTED:
+ break;
+ case HEADSET_STATE_PLAY_IN_PROGRESS:
+ break;
+ case HEADSET_STATE_PLAYING:
+ break;
+ }
+}
+
+static struct media_endpoint *media_endpoint_create(struct media_adapter *adapter,
+ const char *sender,
+ const char *path,
+ const char *uuid,
+ gboolean delay_reporting,
+ uint8_t codec,
+ uint8_t *capabilities,
+ int size,
+ int *err)
+{
+ struct media_endpoint *endpoint;
+
+ endpoint = g_new0(struct media_endpoint, 1);
+ endpoint->sender = g_strdup(sender);
+ endpoint->path = g_strdup(path);
+ endpoint->uuid = g_strdup(uuid);
+ endpoint->codec = codec;
+
+ if (size > 0) {
+ endpoint->capabilities = g_new(uint8_t, size);
+ memcpy(endpoint->capabilities, capabilities, size);
+ endpoint->size = size;
+ }
+
+ endpoint->adapter = adapter;
+
+ if (strcasecmp(uuid, A2DP_SOURCE_UUID) == 0) {
+ endpoint->sep = a2dp_add_sep(&adapter->src,
+ AVDTP_SEP_TYPE_SOURCE, codec,
+ delay_reporting, endpoint, err);
+ if (endpoint->sep == NULL)
+ goto failed;
+ } else if (strcasecmp(uuid, A2DP_SINK_UUID) == 0) {
+ endpoint->sep = a2dp_add_sep(&adapter->src,
+ AVDTP_SEP_TYPE_SINK, codec,
+ delay_reporting, endpoint, err);
+ if (endpoint->sep == NULL)
+ goto failed;
+ } else if (strcasecmp(uuid, HFP_AG_UUID) == 0 ||
+ g_strcmp0(uuid, HSP_AG_UUID) == 0) {
+ struct audio_device *dev;
+
+ endpoint->hs_watch = headset_add_state_cb(headset_state_changed,
+ endpoint);
+ dev = manager_find_device(NULL, &adapter->src, BDADDR_ANY,
+ AUDIO_HEADSET_INTERFACE, TRUE);
+ if (dev)
+ media_endpoint_set_configuration(endpoint, dev, NULL,
+ 0, headset_setconf_cb,
+ dev);
+ } else {
+ if (err)
+ *err = -EINVAL;
+ goto failed;
+ }
+
+ endpoint->watch = g_dbus_add_disconnect_watch(adapter->conn, sender,
+ media_endpoint_exit, endpoint,
+ NULL);
+
+ adapter->endpoints = g_slist_append(adapter->endpoints, endpoint);
+ info("Endpoint registered: sender=%s path=%s", sender, path);
+
+ if (err)
+ *err = 0;
+ return endpoint;
+
+failed:
+ g_free(endpoint);
+ return NULL;
+}
+
+static struct media_endpoint *media_adapter_find_endpoint(
+ struct media_adapter *adapter,
+ const char *sender,
+ const char *path,
+ const char *uuid)
+{
+ GSList *l;
+
+ for (l = adapter->endpoints; l; l = l->next) {
+ struct media_endpoint *endpoint = l->data;
+
+ if (sender && g_strcmp0(endpoint->sender, sender) != 0)
+ continue;
+
+ if (path && g_strcmp0(endpoint->path, path) != 0)
+ continue;
+
+ if (uuid && g_strcmp0(endpoint->uuid, uuid) != 0)
+ continue;
+
+ return endpoint;
+ }
+
+ return NULL;
+}
+
+const char *media_endpoint_get_sender(struct media_endpoint *endpoint)
+{
+ return endpoint->sender;
+}
+
+static int parse_properties(DBusMessageIter *props, const char **uuid,
+ gboolean *delay_reporting, uint8_t *codec,
+ uint8_t **capabilities, int *size)
+{
+ gboolean has_uuid = FALSE;
+ gboolean has_codec = FALSE;
+
+ while (dbus_message_iter_get_arg_type(props) == DBUS_TYPE_DICT_ENTRY) {
+ const char *key;
+ DBusMessageIter value, entry;
+ int var;
+
+ dbus_message_iter_recurse(props, &entry);
+ dbus_message_iter_get_basic(&entry, &key);
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_recurse(&entry, &value);
+
+ var = dbus_message_iter_get_arg_type(&value);
+ if (strcasecmp(key, "UUID") == 0) {
+ if (var != DBUS_TYPE_STRING)
+ return -EINVAL;
+ dbus_message_iter_get_basic(&value, uuid);
+ has_uuid = TRUE;
+ } else if (strcasecmp(key, "Codec") == 0) {
+ if (var != DBUS_TYPE_BYTE)
+ return -EINVAL;
+ dbus_message_iter_get_basic(&value, codec);
+ has_codec = TRUE;
+ } else if (strcasecmp(key, "DelayReporting") == 0) {
+ if (var != DBUS_TYPE_BOOLEAN)
+ return -EINVAL;
+ dbus_message_iter_get_basic(&value, delay_reporting);
+ } else if (strcasecmp(key, "Capabilities") == 0) {
+ DBusMessageIter array;
+
+ if (var != DBUS_TYPE_ARRAY)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(&value, &array);
+ dbus_message_iter_get_fixed_array(&array, capabilities,
+ size);
+ }
+
+ dbus_message_iter_next(props);
+ }
+
+ return (has_uuid && has_codec) ? 0 : -EINVAL;
+}
+
+static DBusMessage *register_endpoint(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct media_adapter *adapter = data;
+ DBusMessageIter args, props;
+ const char *sender, *path, *uuid;
+ gboolean delay_reporting = FALSE;
+ uint8_t codec;
+ uint8_t *capabilities;
+ int size = 0;
+ int err;
+
+ sender = dbus_message_get_sender(msg);
+
+ dbus_message_iter_init(msg, &args);
+
+ dbus_message_iter_get_basic(&args, &path);
+ dbus_message_iter_next(&args);
+
+ if (media_adapter_find_endpoint(adapter, sender, path, NULL) != NULL)
+ return btd_error_already_exists(msg);
+
+ dbus_message_iter_recurse(&args, &props);
+ if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY)
+ return btd_error_invalid_args(msg);
+
+ if (parse_properties(&props, &uuid, &delay_reporting, &codec,
+ &capabilities, &size) < 0)
+ return btd_error_invalid_args(msg);
+
+ if (media_endpoint_create(adapter, sender, path, uuid, delay_reporting,
+ codec, capabilities, size, &err) == FALSE) {
+ if (err == -EPROTONOSUPPORT)
+ return btd_error_not_supported(msg);
+ else
+ return btd_error_invalid_args(msg);
+ }
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *unregister_endpoint(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct media_adapter *adapter = data;
+ struct media_endpoint *endpoint;
+ const char *sender, *path;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ return NULL;
+
+ sender = dbus_message_get_sender(msg);
+
+ endpoint = media_adapter_find_endpoint(adapter, sender, path, NULL);
+ if (endpoint == NULL)
+ return btd_error_does_not_exist(msg);
+
+ media_endpoint_remove(endpoint);
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static GDBusMethodTable media_methods[] = {
+ { "RegisterEndpoint", "oa{sv}", "", register_endpoint },
+ { "UnregisterEndpoint", "o", "", unregister_endpoint },
+ { },
+};
+
+static void path_free(void *data)
+{
+ struct media_adapter *adapter = data;
+
+ g_slist_foreach(adapter->endpoints, (GFunc) media_endpoint_release,
+ NULL);
+ g_slist_free(adapter->endpoints);
+
+ dbus_connection_unref(adapter->conn);
+
+ adapters = g_slist_remove(adapters, adapter);
+
+ g_free(adapter->path);
+ g_free(adapter);
+}
+
+int media_register(DBusConnection *conn, const char *path, const bdaddr_t *src)
+{
+ struct media_adapter *adapter;
+
+ if (DBUS_TYPE_UNIX_FD < 0)
+ return -EPERM;
+
+ adapter = g_new0(struct media_adapter, 1);
+ adapter->conn = dbus_connection_ref(conn);
+ bacpy(&adapter->src, src);
+ adapter->path = g_strdup(path);
+
+ if (!g_dbus_register_interface(conn, path, MEDIA_INTERFACE,
+ media_methods, NULL, NULL,
+ adapter, path_free)) {
+ error("D-Bus failed to register %s path", path);
+ path_free(adapter);
+ return -1;
+ }
+
+ adapters = g_slist_append(adapters, adapter);
+
+ return 0;
+}
+
+void media_unregister(const char *path)
+{
+ GSList *l;
+
+ for (l = adapters; l; l = l->next) {
+ struct media_adapter *adapter = l->data;
+
+ if (g_strcmp0(path, adapter->path) == 0) {
+ g_dbus_unregister_interface(adapter->conn, path,
+ MEDIA_INTERFACE);
+ return;
+ }
+ }
+}
+
+size_t media_endpoint_get_capabilities(struct media_endpoint *endpoint,
+ uint8_t **capabilities)
+{
+ *capabilities = endpoint->capabilities;
+ return endpoint->size;
+}
+
+static void endpoint_reply(DBusPendingCall *call, void *user_data)
+{
+ struct media_endpoint *endpoint = user_data;
+ struct endpoint_request *request = endpoint->request;
+ DBusMessage *reply;
+ DBusError err;
+ gboolean value;
+ void *ret = NULL;
+ int size = -1;
+
+ /* steal_reply will always return non-NULL since the callback
+ * is only called after a reply has been received */
+ reply = dbus_pending_call_steal_reply(call);
+
+ dbus_error_init(&err);
+ if (dbus_set_error_from_message(&err, reply)) {
+ error("Endpoint replied with an error: %s",
+ err.name);
+
+ /* Clear endpoint configuration in case of NO_REPLY error */
+ if (dbus_error_has_name(&err, DBUS_ERROR_NO_REPLY)) {
+ if (request->cb)
+ request->cb(endpoint, NULL, size,
+ request->user_data);
+ media_endpoint_clear_configuration(endpoint);
+ dbus_message_unref(reply);
+ dbus_error_free(&err);
+ return;
+ }
+
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ dbus_error_init(&err);
+ if (dbus_message_is_method_call(request->msg, MEDIA_ENDPOINT_INTERFACE,
+ "SelectConfiguration")) {
+ DBusMessageIter args, array;
+ uint8_t *configuration;
+
+ dbus_message_iter_init(reply, &args);
+
+ dbus_message_iter_recurse(&args, &array);
+
+ dbus_message_iter_get_fixed_array(&array, &configuration, &size);
+
+ ret = configuration;
+ goto done;
+ } else if (!dbus_message_get_args(reply, &err, DBUS_TYPE_INVALID)) {
+ error("Wrong reply signature: %s", err.message);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ size = 1;
+ value = TRUE;
+ ret = &value;
+
+done:
+ dbus_message_unref(reply);
+
+ if (request->cb)
+ request->cb(endpoint, ret, size, request->user_data);
+
+ endpoint_request_free(request);
+ endpoint->request = NULL;
+}
+
+static gboolean media_endpoint_async_call(DBusConnection *conn,
+ DBusMessage *msg,
+ struct media_endpoint *endpoint,
+ media_endpoint_cb_t cb,
+ void *user_data)
+{
+ struct endpoint_request *request;
+
+ if (endpoint->request)
+ return FALSE;
+
+ request = g_new0(struct endpoint_request, 1);
+
+ /* Timeout should be less than avdtp request timeout (4 seconds) */
+ if (dbus_connection_send_with_reply(conn, msg, &request->call,
+ REQUEST_TIMEOUT) == FALSE) {
+ error("D-Bus send failed");
+ g_free(request);
+ return FALSE;
+ }
+
+ dbus_pending_call_set_notify(request->call, endpoint_reply, endpoint, NULL);
+
+ request->msg = msg;
+ request->cb = cb;
+ request->user_data = user_data;
+ endpoint->request = request;
+
+ DBG("Calling %s: name = %s path = %s", dbus_message_get_member(msg),
+ dbus_message_get_destination(msg),
+ dbus_message_get_path(msg));
+
+ return TRUE;
+}
+
+gboolean media_endpoint_set_configuration(struct media_endpoint *endpoint,
+ struct audio_device *device,
+ uint8_t *configuration, size_t size,
+ media_endpoint_cb_t cb,
+ void *user_data)
+{
+ DBusConnection *conn;
+ DBusMessage *msg;
+ const char *path;
+ DBusMessageIter iter;
+
+ if (endpoint->transport != NULL || endpoint->request != NULL)
+ return FALSE;
+
+ conn = endpoint->adapter->conn;
+
+ endpoint->transport = media_transport_create(conn, endpoint, device,
+ configuration, size);
+ if (endpoint->transport == NULL)
+ return FALSE;
+
+ msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
+ MEDIA_ENDPOINT_INTERFACE,
+ "SetConfiguration");
+ if (msg == NULL) {
+ error("Couldn't allocate D-Bus message");
+ return FALSE;
+ }
+
+ dbus_message_iter_init_append(msg, &iter);
+
+ path = media_transport_get_path(endpoint->transport);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
+
+ transport_get_properties(endpoint->transport, &iter);
+
+ return media_endpoint_async_call(conn, msg, endpoint, cb, user_data);
+}
+
+gboolean media_endpoint_select_configuration(struct media_endpoint *endpoint,
+ uint8_t *capabilities,
+ size_t length,
+ media_endpoint_cb_t cb,
+ void *user_data)
+{
+ DBusConnection *conn;
+ DBusMessage *msg;
+
+ if (endpoint->request != NULL)
+ return FALSE;
+
+ conn = endpoint->adapter->conn;
+
+ msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
+ MEDIA_ENDPOINT_INTERFACE,
+ "SelectConfiguration");
+ if (msg == NULL) {
+ error("Couldn't allocate D-Bus message");
+ return FALSE;
+ }
+
+ dbus_message_append_args(msg, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
+ &capabilities, length,
+ DBUS_TYPE_INVALID);
+
+ return media_endpoint_async_call(conn, msg, endpoint, cb, user_data);
+}
+
+void media_endpoint_clear_configuration(struct media_endpoint *endpoint)
+{
+ DBusConnection *conn;
+ DBusMessage *msg;
+ const char *path;
+
+ if (endpoint->transport == NULL)
+ return;
+
+ if (endpoint->request)
+ media_endpoint_cancel(endpoint);
+
+ conn = endpoint->adapter->conn;
+
+ msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
+ MEDIA_ENDPOINT_INTERFACE,
+ "ClearConfiguration");
+ if (msg == NULL) {
+ error("Couldn't allocate D-Bus message");
+ goto done;
+ }
+
+ path = media_transport_get_path(endpoint->transport);
+ dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID);
+ g_dbus_send_message(conn, msg);
+done:
+ media_transport_destroy(endpoint->transport);
+ endpoint->transport = NULL;
+}
+
+void media_endpoint_release(struct media_endpoint *endpoint)
+{
+ DBusMessage *msg;
+
+ DBG("sender=%s path=%s", endpoint->sender, endpoint->path);
+
+ /* already exit */
+ if (endpoint->watch == 0)
+ return;
+
+ msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
+ MEDIA_ENDPOINT_INTERFACE,
+ "Release");
+ if (msg == NULL) {
+ error("Couldn't allocate D-Bus message");
+ return;
+ }
+
+ g_dbus_send_message(endpoint->adapter->conn, msg);
+
+ media_endpoint_remove(endpoint);
+}
+
+struct a2dp_sep *media_endpoint_get_sep(struct media_endpoint *endpoint)
+{
+ return endpoint->sep;
+}
+
+const char *media_endpoint_get_uuid(struct media_endpoint *endpoint)
+{
+ return endpoint->uuid;
+}
+
+uint8_t media_endpoint_get_codec(struct media_endpoint *endpoint)
+{
+ return endpoint->codec;
+}
+
+struct media_transport *media_endpoint_get_transport(
+ struct media_endpoint *endpoint)
+{
+ return endpoint->transport;
+}
diff --git a/audio/media.h b/audio/media.h
new file mode 100644
index 0000000..d089103
--- /dev/null
+++ b/audio/media.h
@@ -0,0 +1,54 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2007 Nokia Corporation
+ * Copyright (C) 2004-2009 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
+ *
+ */
+
+struct media_endpoint;
+
+typedef void (*media_endpoint_cb_t) (struct media_endpoint *endpoint,
+ void *ret, int size, void *user_data);
+
+int media_register(DBusConnection *conn, const char *path, const bdaddr_t *src);
+void media_unregister(const char *path);
+
+const char *media_endpoint_get_sender(struct media_endpoint *endpoint);
+
+size_t media_endpoint_get_capabilities(struct media_endpoint *endpoint,
+ uint8_t **capabilities);
+gboolean media_endpoint_set_configuration(struct media_endpoint *endpoint,
+ struct audio_device *device,
+ uint8_t *configuration, size_t size,
+ media_endpoint_cb_t cb,
+ void *user_data);
+gboolean media_endpoint_select_configuration(struct media_endpoint *endpoint,
+ uint8_t *capabilities,
+ size_t length,
+ media_endpoint_cb_t cb,
+ void *user_data);
+void media_endpoint_clear_configuration(struct media_endpoint *endpoint);
+void media_endpoint_release(struct media_endpoint *endpoint);
+
+struct a2dp_sep *media_endpoint_get_sep(struct media_endpoint *endpoint);
+const char *media_endpoint_get_uuid(struct media_endpoint *endpoint);
+uint8_t media_endpoint_get_codec(struct media_endpoint *endpoint);
+struct media_transport *media_endpoint_get_transport(
+ struct media_endpoint *endpoint);
diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c
new file mode 100644
index 0000000..799f17f
--- /dev/null
+++ b/audio/pcm_bluetooth.c
@@ -0,0 +1,1784 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 <stdint.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <time.h>
+#include <sys/time.h>
+#include <pthread.h>
+#include <signal.h>
+#include <limits.h>
+
+#include <netinet/in.h>
+
+#include <alsa/asoundlib.h>
+#include <alsa/pcm_external.h>
+
+#include "ipc.h"
+#include "sbc.h"
+#include "rtp.h"
+
+//#define ENABLE_DEBUG
+
+#define UINT_SECS_MAX (UINT_MAX / 1000000 - 1)
+
+#define MIN_PERIOD_TIME 1
+
+#define BUFFER_SIZE 2048
+
+#ifdef ENABLE_DEBUG
+#define DBG(fmt, arg...) printf("DEBUG: %s: " fmt "\n" , __FUNCTION__ , ## arg)
+#else
+#define DBG(fmt, arg...)
+#endif
+
+#ifndef SOL_SCO
+#define SOL_SCO 17
+#endif
+
+#ifndef SCO_TXBUFS
+#define SCO_TXBUFS 0x03
+#endif
+
+#ifndef SCO_RXBUFS
+#define SCO_RXBUFS 0x04
+#endif
+
+#ifndef MIN
+# define MIN(x, y) ((x) < (y) ? (x) : (y))
+#endif
+
+#ifndef MAX
+# define MAX(x, y) ((x) > (y) ? (x) : (y))
+#endif
+
+#define MAX_BITPOOL 64
+#define MIN_BITPOOL 2
+
+/* adapted from glibc sys/time.h timersub() macro */
+#define priv_timespecsub(a, b, result) \
+ do { \
+ (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
+ (result)->tv_nsec = (a)->tv_nsec - (b)->tv_nsec; \
+ if ((result)->tv_nsec < 0) { \
+ --(result)->tv_sec; \
+ (result)->tv_nsec += 1000000000; \
+ } \
+ } while (0)
+
+struct bluetooth_a2dp {
+ sbc_capabilities_t sbc_capabilities;
+ sbc_t sbc; /* Codec data */
+ int sbc_initialized; /* Keep track if the encoder is initialized */
+ unsigned int codesize; /* SBC codesize */
+ int samples; /* Number of encoded samples */
+ uint8_t buffer[BUFFER_SIZE]; /* Codec transfer buffer */
+ unsigned int count; /* Codec transfer buffer counter */
+
+ int nsamples; /* Cumulative number of codec samples */
+ uint16_t seq_num; /* Cumulative packet sequence */
+ int frame_count; /* Current frames in buffer*/
+};
+
+struct bluetooth_alsa_config {
+ char device[18]; /* Address of the remote Device */
+ int has_device;
+ uint8_t transport; /* Requested transport */
+ int has_transport;
+ uint16_t rate;
+ int has_rate;
+ uint8_t channel_mode; /* A2DP only */
+ int has_channel_mode;
+ uint8_t allocation_method; /* A2DP only */
+ int has_allocation_method;
+ uint8_t subbands; /* A2DP only */
+ int has_subbands;
+ uint8_t block_length; /* A2DP only */
+ int has_block_length;
+ uint8_t bitpool; /* A2DP only */
+ int has_bitpool;
+ int autoconnect;
+};
+
+struct bluetooth_data {
+ snd_pcm_ioplug_t io;
+ struct bluetooth_alsa_config alsa_config; /* ALSA resource file parameters */
+ volatile snd_pcm_sframes_t hw_ptr;
+ int transport; /* chosen transport SCO or AD2P */
+ unsigned int link_mtu; /* MTU for selected transport channel */
+ volatile struct pollfd stream; /* Audio stream filedescriptor */
+ struct pollfd server; /* Audio daemon filedescriptor */
+ uint8_t buffer[BUFFER_SIZE]; /* Encoded transfer buffer */
+ unsigned int count; /* Transfer buffer counter */
+ struct bluetooth_a2dp a2dp; /* A2DP data */
+
+ pthread_t hw_thread; /* Makes virtual hw pointer move */
+ int pipefd[2]; /* Inter thread communication */
+ int stopped;
+ sig_atomic_t reset; /* Request XRUN handling */
+};
+
+static int audioservice_send(int sk, const bt_audio_msg_header_t *msg);
+static int audioservice_expect(int sk, bt_audio_msg_header_t *outmsg,
+ int expected_type);
+
+static int bluetooth_start(snd_pcm_ioplug_t *io)
+{
+ DBG("bluetooth_start %p", io);
+
+ return 0;
+}
+
+static int bluetooth_stop(snd_pcm_ioplug_t *io)
+{
+ DBG("bluetooth_stop %p", io);
+
+ return 0;
+}
+
+static void *playback_hw_thread(void *param)
+{
+ struct bluetooth_data *data = param;
+ unsigned int prev_periods;
+ double period_time;
+ struct timespec start;
+ struct pollfd fds[2];
+ int poll_timeout;
+
+ data->server.events = POLLIN;
+ /* note: only errors for data->stream.events */
+
+ fds[0] = data->server;
+ fds[1] = data->stream;
+
+ prev_periods = 0;
+ period_time = 1000000.0 * data->io.period_size / data->io.rate;
+ if (period_time > (int) (MIN_PERIOD_TIME * 1000))
+ poll_timeout = (int) (period_time / 1000.0f);
+ else
+ poll_timeout = MIN_PERIOD_TIME;
+
+ clock_gettime(CLOCK_MONOTONIC, &start);
+
+ while (1) {
+ unsigned int dtime, periods;
+ struct timespec cur, delta;
+ int ret;
+
+ if (data->stopped)
+ goto iter_sleep;
+
+ if (data->reset) {
+ DBG("Handle XRUN in hw-thread.");
+ data->reset = 0;
+ clock_gettime(CLOCK_MONOTONIC, &start);
+ prev_periods = 0;
+ }
+
+ clock_gettime(CLOCK_MONOTONIC, &cur);
+
+ priv_timespecsub(&cur, &start, &delta);
+
+ dtime = delta.tv_sec * 1000000 + delta.tv_nsec / 1000;
+ periods = 1.0 * dtime / period_time;
+
+ if (periods > prev_periods) {
+ char c = 'w';
+ int frags = periods - prev_periods, n;
+
+ data->hw_ptr += frags * data->io.period_size;
+ data->hw_ptr %= data->io.buffer_size;
+
+ for (n = 0; n < frags; n++) {
+ /* Notify user that hardware pointer
+ * has moved * */
+ if (write(data->pipefd[1], &c, 1) < 0)
+ pthread_testcancel();
+ }
+
+ /* Reset point of reference to avoid too big values
+ * that wont fit an unsigned int */
+ if ((unsigned int) delta.tv_sec < UINT_SECS_MAX)
+ prev_periods = periods;
+ else {
+ prev_periods = 0;
+ clock_gettime(CLOCK_MONOTONIC, &start);
+ }
+ }
+
+iter_sleep:
+ /* sleep up to one period interval */
+ ret = poll(fds, 2, poll_timeout);
+
+ if (ret < 0) {
+ if (errno != EINTR) {
+ SNDERR("poll error: %s (%d)", strerror(errno),
+ errno);
+ break;
+ }
+ } else if (ret > 0) {
+ ret = (fds[0].revents) ? 0 : 1;
+ SNDERR("poll fd %d revents %d", ret, fds[ret].revents);
+ if (fds[ret].revents & (POLLERR | POLLHUP | POLLNVAL))
+ break;
+ }
+
+ /* Offer opportunity to be canceled by main thread */
+ pthread_testcancel();
+ }
+
+ data->hw_thread = 0;
+ pthread_exit(NULL);
+}
+
+static int bluetooth_playback_start(snd_pcm_ioplug_t *io)
+{
+ struct bluetooth_data *data = io->private_data;
+ int err;
+
+ DBG("%p", io);
+
+ data->stopped = 0;
+
+ if (data->hw_thread)
+ return 0;
+
+ err = pthread_create(&data->hw_thread, 0, playback_hw_thread, data);
+
+ return -err;
+}
+
+static int bluetooth_playback_stop(snd_pcm_ioplug_t *io)
+{
+ struct bluetooth_data *data = io->private_data;
+
+ DBG("%p", io);
+
+ data->stopped = 1;
+
+ return 0;
+}
+
+static snd_pcm_sframes_t bluetooth_pointer(snd_pcm_ioplug_t *io)
+{
+ struct bluetooth_data *data = io->private_data;
+
+ return data->hw_ptr;
+}
+
+static void bluetooth_exit(struct bluetooth_data *data)
+{
+ struct bluetooth_a2dp *a2dp = &data->a2dp;
+
+ if (data->server.fd >= 0)
+ bt_audio_service_close(data->server.fd);
+
+ if (data->stream.fd >= 0)
+ close(data->stream.fd);
+
+ if (data->hw_thread) {
+ pthread_cancel(data->hw_thread);
+ pthread_join(data->hw_thread, 0);
+ }
+
+ if (a2dp->sbc_initialized)
+ sbc_finish(&a2dp->sbc);
+
+ if (data->pipefd[0] > 0)
+ close(data->pipefd[0]);
+
+ if (data->pipefd[1] > 0)
+ close(data->pipefd[1]);
+
+ free(data);
+}
+
+static int bluetooth_close(snd_pcm_ioplug_t *io)
+{
+ struct bluetooth_data *data = io->private_data;
+
+ DBG("%p", io);
+
+ bluetooth_exit(data);
+
+ return 0;
+}
+
+static int bluetooth_prepare(snd_pcm_ioplug_t *io)
+{
+ struct bluetooth_data *data = io->private_data;
+ char c = 'w';
+ char buf[BT_SUGGESTED_BUFFER_SIZE];
+ struct bt_start_stream_req *req = (void *) buf;
+ struct bt_start_stream_rsp *rsp = (void *) buf;
+ struct bt_new_stream_ind *ind = (void *) buf;
+ uint32_t period_count = io->buffer_size / io->period_size;
+ int opt_name, err;
+ struct timeval t = { 0, period_count };
+
+ DBG("Preparing with io->period_size=%lu io->buffer_size=%lu",
+ io->period_size, io->buffer_size);
+
+ data->reset = 0;
+
+ /* As we're gonna receive messages on the server socket, we have to stop the
+ hw thread that is polling on it, if any */
+ if (data->hw_thread) {
+ pthread_cancel(data->hw_thread);
+ pthread_join(data->hw_thread, 0);
+ data->hw_thread = 0;
+ }
+
+ if (io->stream == SND_PCM_STREAM_PLAYBACK)
+ /* If not null for playback, xmms doesn't display time
+ * correctly */
+ data->hw_ptr = 0;
+ else
+ /* ALSA library is really picky on the fact hw_ptr is not null.
+ * If it is, capture won't start */
+ data->hw_ptr = io->period_size;
+
+ /* send start */
+ memset(req, 0, BT_SUGGESTED_BUFFER_SIZE);
+ req->h.type = BT_REQUEST;
+ req->h.name = BT_START_STREAM;
+ req->h.length = sizeof(*req);
+
+ err = audioservice_send(data->server.fd, &req->h);
+ if (err < 0)
+ return err;
+
+ rsp->h.length = sizeof(*rsp);
+ err = audioservice_expect(data->server.fd, &rsp->h,
+ BT_START_STREAM);
+ if (err < 0)
+ return err;
+
+ ind->h.length = sizeof(*ind);
+ err = audioservice_expect(data->server.fd, &ind->h,
+ BT_NEW_STREAM);
+ if (err < 0)
+ return err;
+
+ if (data->stream.fd >= 0)
+ close(data->stream.fd);
+
+ data->stream.fd = bt_audio_service_get_data_fd(data->server.fd);
+ if (data->stream.fd < 0) {
+ return -errno;
+ }
+
+ if (data->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
+ opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ?
+ SO_SNDTIMEO : SO_RCVTIMEO;
+
+ if (setsockopt(data->stream.fd, SOL_SOCKET, opt_name, &t,
+ sizeof(t)) < 0)
+ return -errno;
+ } else {
+ opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ?
+ SCO_TXBUFS : SCO_RXBUFS;
+
+ if (setsockopt(data->stream.fd, SOL_SCO, opt_name, &period_count,
+ sizeof(period_count)) == 0)
+ return 0;
+
+ opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ?
+ SO_SNDBUF : SO_RCVBUF;
+
+ if (setsockopt(data->stream.fd, SOL_SCO, opt_name, &period_count,
+ sizeof(period_count)) == 0)
+ return 0;
+
+ /* FIXME : handle error codes */
+ }
+
+ /* wake up any client polling at us */
+ err = write(data->pipefd[1], &c, 1);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int bluetooth_hsp_hw_params(snd_pcm_ioplug_t *io,
+ snd_pcm_hw_params_t *params)
+{
+ struct bluetooth_data *data = io->private_data;
+ char buf[BT_SUGGESTED_BUFFER_SIZE];
+ struct bt_open_req *open_req = (void *) buf;
+ struct bt_open_rsp *open_rsp = (void *) buf;
+ struct bt_set_configuration_req *req = (void *) buf;
+ struct bt_set_configuration_rsp *rsp = (void *) buf;
+ int err;
+
+ DBG("Preparing with io->period_size=%lu io->buffer_size=%lu",
+ io->period_size, io->buffer_size);
+
+ memset(req, 0, BT_SUGGESTED_BUFFER_SIZE);
+ open_req->h.type = BT_REQUEST;
+ open_req->h.name = BT_OPEN;
+ open_req->h.length = sizeof(*open_req);
+
+ strncpy(open_req->destination, data->alsa_config.device, 18);
+ open_req->seid = BT_A2DP_SEID_RANGE + 1;
+ open_req->lock = (io->stream == SND_PCM_STREAM_PLAYBACK ?
+ BT_WRITE_LOCK : BT_READ_LOCK);
+
+ err = audioservice_send(data->server.fd, &open_req->h);
+ if (err < 0)
+ return err;
+
+ open_rsp->h.length = sizeof(*open_rsp);
+ err = audioservice_expect(data->server.fd, &open_rsp->h,
+ BT_OPEN);
+ if (err < 0)
+ return err;
+
+ memset(req, 0, BT_SUGGESTED_BUFFER_SIZE);
+ req->h.type = BT_REQUEST;
+ req->h.name = BT_SET_CONFIGURATION;
+ req->h.length = sizeof(*req);
+
+ req->codec.transport = BT_CAPABILITIES_TRANSPORT_SCO;
+ req->codec.seid = BT_A2DP_SEID_RANGE + 1;
+ req->codec.length = sizeof(pcm_capabilities_t);
+
+ req->h.length += req->codec.length - sizeof(req->codec);
+ err = audioservice_send(data->server.fd, &req->h);
+ if (err < 0)
+ return err;
+
+ rsp->h.length = sizeof(*rsp);
+ err = audioservice_expect(data->server.fd, &rsp->h,
+ BT_SET_CONFIGURATION);
+ if (err < 0)
+ return err;
+
+ data->transport = BT_CAPABILITIES_TRANSPORT_SCO;
+ data->link_mtu = rsp->link_mtu;
+
+ return 0;
+}
+
+static uint8_t default_bitpool(uint8_t freq, uint8_t mode)
+{
+ switch (freq) {
+ case BT_SBC_SAMPLING_FREQ_16000:
+ case BT_SBC_SAMPLING_FREQ_32000:
+ return 53;
+ case BT_SBC_SAMPLING_FREQ_44100:
+ switch (mode) {
+ case BT_A2DP_CHANNEL_MODE_MONO:
+ case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL:
+ return 31;
+ case BT_A2DP_CHANNEL_MODE_STEREO:
+ case BT_A2DP_CHANNEL_MODE_JOINT_STEREO:
+ return 53;
+ default:
+ DBG("Invalid channel mode %u", mode);
+ return 53;
+ }
+ case BT_SBC_SAMPLING_FREQ_48000:
+ switch (mode) {
+ case BT_A2DP_CHANNEL_MODE_MONO:
+ case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL:
+ return 29;
+ case BT_A2DP_CHANNEL_MODE_STEREO:
+ case BT_A2DP_CHANNEL_MODE_JOINT_STEREO:
+ return 51;
+ default:
+ DBG("Invalid channel mode %u", mode);
+ return 51;
+ }
+ default:
+ DBG("Invalid sampling freq %u", freq);
+ return 53;
+ }
+}
+
+static int bluetooth_a2dp_init(struct bluetooth_data *data,
+ snd_pcm_hw_params_t *params)
+{
+ struct bluetooth_alsa_config *cfg = &data->alsa_config;
+ sbc_capabilities_t *cap = &data->a2dp.sbc_capabilities;
+ unsigned int max_bitpool, min_bitpool, rate, channels;
+ int dir;
+
+ snd_pcm_hw_params_get_rate(params, &rate, &dir);
+ snd_pcm_hw_params_get_channels(params, &channels);
+
+ switch (rate) {
+ case 48000:
+ cap->frequency = BT_SBC_SAMPLING_FREQ_48000;
+ break;
+ case 44100:
+ cap->frequency = BT_SBC_SAMPLING_FREQ_44100;
+ break;
+ case 32000:
+ cap->frequency = BT_SBC_SAMPLING_FREQ_32000;
+ break;
+ case 16000:
+ cap->frequency = BT_SBC_SAMPLING_FREQ_16000;
+ break;
+ default:
+ DBG("Rate %d not supported", rate);
+ return -1;
+ }
+
+ if (cfg->has_channel_mode)
+ cap->channel_mode = cfg->channel_mode;
+ else if (channels == 2) {
+ if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO)
+ cap->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO;
+ else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO)
+ cap->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO;
+ else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL)
+ cap->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL;
+ } else {
+ if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_MONO)
+ cap->channel_mode = BT_A2DP_CHANNEL_MODE_MONO;
+ }
+
+ if (!cap->channel_mode) {
+ DBG("No supported channel modes");
+ return -1;
+ }
+
+ if (cfg->has_block_length)
+ cap->block_length = cfg->block_length;
+ else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_16)
+ cap->block_length = BT_A2DP_BLOCK_LENGTH_16;
+ else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_12)
+ cap->block_length = BT_A2DP_BLOCK_LENGTH_12;
+ else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_8)
+ cap->block_length = BT_A2DP_BLOCK_LENGTH_8;
+ else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_4)
+ cap->block_length = BT_A2DP_BLOCK_LENGTH_4;
+ else {
+ DBG("No supported block lengths");
+ return -1;
+ }
+
+ if (cfg->has_subbands)
+ cap->subbands = cfg->subbands;
+ if (cap->subbands & BT_A2DP_SUBBANDS_8)
+ cap->subbands = BT_A2DP_SUBBANDS_8;
+ else if (cap->subbands & BT_A2DP_SUBBANDS_4)
+ cap->subbands = BT_A2DP_SUBBANDS_4;
+ else {
+ DBG("No supported subbands");
+ return -1;
+ }
+
+ if (cfg->has_allocation_method)
+ cap->allocation_method = cfg->allocation_method;
+ if (cap->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS)
+ cap->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS;
+ else if (cap->allocation_method & BT_A2DP_ALLOCATION_SNR)
+ cap->allocation_method = BT_A2DP_ALLOCATION_SNR;
+
+ if (cfg->has_bitpool)
+ min_bitpool = max_bitpool = cfg->bitpool;
+ else {
+ min_bitpool = MAX(MIN_BITPOOL, cap->min_bitpool);
+ max_bitpool = MIN(default_bitpool(cap->frequency,
+ cap->channel_mode),
+ cap->max_bitpool);
+ }
+
+ cap->min_bitpool = min_bitpool;
+ cap->max_bitpool = max_bitpool;
+
+ return 0;
+}
+
+static void bluetooth_a2dp_setup(struct bluetooth_a2dp *a2dp)
+{
+ sbc_capabilities_t active_capabilities = a2dp->sbc_capabilities;
+
+ if (a2dp->sbc_initialized)
+ sbc_reinit(&a2dp->sbc, 0);
+ else
+ sbc_init(&a2dp->sbc, 0);
+ a2dp->sbc_initialized = 1;
+
+ if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_16000)
+ a2dp->sbc.frequency = SBC_FREQ_16000;
+
+ if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_32000)
+ a2dp->sbc.frequency = SBC_FREQ_32000;
+
+ if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_44100)
+ a2dp->sbc.frequency = SBC_FREQ_44100;
+
+ if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_48000)
+ a2dp->sbc.frequency = SBC_FREQ_48000;
+
+ if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_MONO)
+ a2dp->sbc.mode = SBC_MODE_MONO;
+
+ if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL)
+ a2dp->sbc.mode = SBC_MODE_DUAL_CHANNEL;
+
+ if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_STEREO)
+ a2dp->sbc.mode = SBC_MODE_STEREO;
+
+ if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO)
+ a2dp->sbc.mode = SBC_MODE_JOINT_STEREO;
+
+ a2dp->sbc.allocation = active_capabilities.allocation_method
+ == BT_A2DP_ALLOCATION_SNR ? SBC_AM_SNR
+ : SBC_AM_LOUDNESS;
+
+ switch (active_capabilities.subbands) {
+ case BT_A2DP_SUBBANDS_4:
+ a2dp->sbc.subbands = SBC_SB_4;
+ break;
+ case BT_A2DP_SUBBANDS_8:
+ a2dp->sbc.subbands = SBC_SB_8;
+ break;
+ }
+
+ switch (active_capabilities.block_length) {
+ case BT_A2DP_BLOCK_LENGTH_4:
+ a2dp->sbc.blocks = SBC_BLK_4;
+ break;
+ case BT_A2DP_BLOCK_LENGTH_8:
+ a2dp->sbc.blocks = SBC_BLK_8;
+ break;
+ case BT_A2DP_BLOCK_LENGTH_12:
+ a2dp->sbc.blocks = SBC_BLK_12;
+ break;
+ case BT_A2DP_BLOCK_LENGTH_16:
+ a2dp->sbc.blocks = SBC_BLK_16;
+ break;
+ }
+
+ a2dp->sbc.bitpool = active_capabilities.max_bitpool;
+ a2dp->codesize = sbc_get_codesize(&a2dp->sbc);
+ a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload);
+}
+
+static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io,
+ snd_pcm_hw_params_t *params)
+{
+ struct bluetooth_data *data = io->private_data;
+ struct bluetooth_a2dp *a2dp = &data->a2dp;
+ char buf[BT_SUGGESTED_BUFFER_SIZE];
+ struct bt_open_req *open_req = (void *) buf;
+ struct bt_open_rsp *open_rsp = (void *) buf;
+ struct bt_set_configuration_req *req = (void *) buf;
+ struct bt_set_configuration_rsp *rsp = (void *) buf;
+ int err;
+
+ DBG("Preparing with io->period_size=%lu io->buffer_size=%lu",
+ io->period_size, io->buffer_size);
+
+ memset(req, 0, BT_SUGGESTED_BUFFER_SIZE);
+ open_req->h.type = BT_REQUEST;
+ open_req->h.name = BT_OPEN;
+ open_req->h.length = sizeof(*open_req);
+
+ strncpy(open_req->destination, data->alsa_config.device, 18);
+ open_req->seid = a2dp->sbc_capabilities.capability.seid;
+ open_req->lock = (io->stream == SND_PCM_STREAM_PLAYBACK ?
+ BT_WRITE_LOCK : BT_READ_LOCK);
+
+ err = audioservice_send(data->server.fd, &open_req->h);
+ if (err < 0)
+ return err;
+
+ open_rsp->h.length = sizeof(*open_rsp);
+ err = audioservice_expect(data->server.fd, &open_rsp->h,
+ BT_OPEN);
+ if (err < 0)
+ return err;
+
+ err = bluetooth_a2dp_init(data, params);
+ if (err < 0)
+ return err;
+
+ memset(req, 0, BT_SUGGESTED_BUFFER_SIZE);
+ req->h.type = BT_REQUEST;
+ req->h.name = BT_SET_CONFIGURATION;
+ req->h.length = sizeof(*req);
+
+ memcpy(&req->codec, &a2dp->sbc_capabilities,
+ sizeof(a2dp->sbc_capabilities));
+
+ req->codec.transport = BT_CAPABILITIES_TRANSPORT_A2DP;
+ req->codec.length = sizeof(a2dp->sbc_capabilities);
+ req->h.length += req->codec.length - sizeof(req->codec);
+
+ err = audioservice_send(data->server.fd, &req->h);
+ if (err < 0)
+ return err;
+
+ rsp->h.length = sizeof(*rsp);
+ err = audioservice_expect(data->server.fd, &rsp->h,
+ BT_SET_CONFIGURATION);
+ if (err < 0)
+ return err;
+
+ data->transport = BT_CAPABILITIES_TRANSPORT_A2DP;
+ data->link_mtu = rsp->link_mtu;
+
+ /* Setup SBC encoder now we agree on parameters */
+ bluetooth_a2dp_setup(a2dp);
+
+ DBG("\tallocation=%u\n\tsubbands=%u\n\tblocks=%u\n\tbitpool=%u\n",
+ a2dp->sbc.allocation, a2dp->sbc.subbands, a2dp->sbc.blocks,
+ a2dp->sbc.bitpool);
+
+ return 0;
+}
+
+static int bluetooth_poll_descriptors(snd_pcm_ioplug_t *io,
+ struct pollfd *pfd, unsigned int space)
+{
+ struct bluetooth_data *data = io->private_data;
+
+ assert(io);
+
+ if (space < 1)
+ return 0;
+
+ pfd[0].fd = data->stream.fd;
+ pfd[0].events = POLLIN;
+ pfd[0].revents = 0;
+
+ return 1;
+}
+
+static int bluetooth_poll_revents(snd_pcm_ioplug_t *io ATTRIBUTE_UNUSED,
+ struct pollfd *pfds, unsigned int nfds,
+ unsigned short *revents)
+{
+ assert(pfds && nfds == 1 && revents);
+
+ *revents = pfds[0].revents;
+
+ return 0;
+}
+
+static int bluetooth_playback_poll_descriptors_count(snd_pcm_ioplug_t *io)
+{
+ return 2;
+}
+
+static int bluetooth_playback_poll_descriptors(snd_pcm_ioplug_t *io,
+ struct pollfd *pfd, unsigned int space)
+{
+ struct bluetooth_data *data = io->private_data;
+
+ DBG("");
+
+ assert(data->pipefd[0] >= 0);
+
+ if (space < 2)
+ return 0;
+
+ pfd[0].fd = data->pipefd[0];
+ pfd[0].events = POLLIN;
+ pfd[0].revents = 0;
+ pfd[1].fd = data->stream.fd;
+ pfd[1].events = POLLERR | POLLHUP | POLLNVAL;
+ pfd[1].revents = 0;
+
+ return 2;
+}
+
+static int bluetooth_playback_poll_revents(snd_pcm_ioplug_t *io,
+ struct pollfd *pfds, unsigned int nfds,
+ unsigned short *revents)
+{
+ static char buf[1];
+ int ret;
+
+ DBG("");
+
+ assert(pfds);
+ assert(nfds == 2);
+ assert(revents);
+ assert(pfds[0].fd >= 0);
+ assert(pfds[1].fd >= 0);
+
+ if (io->state != SND_PCM_STATE_PREPARED)
+ ret = read(pfds[0].fd, buf, 1);
+
+ if (pfds[1].revents & (POLLERR | POLLHUP | POLLNVAL))
+ io->state = SND_PCM_STATE_DISCONNECTED;
+
+ *revents = (pfds[0].revents & POLLIN) ? POLLOUT : 0;
+
+ return 0;
+}
+
+
+static snd_pcm_sframes_t bluetooth_hsp_read(snd_pcm_ioplug_t *io,
+ const snd_pcm_channel_area_t *areas,
+ snd_pcm_uframes_t offset,
+ snd_pcm_uframes_t size)
+{
+ struct bluetooth_data *data = io->private_data;
+ snd_pcm_uframes_t frames_to_write, ret;
+ unsigned char *buff;
+ unsigned int frame_size = 0;
+ int nrecv;
+
+ DBG("areas->step=%u areas->first=%u offset=%lu size=%lu io->nonblock=%u",
+ areas->step, areas->first, offset, size, io->nonblock);
+
+ frame_size = areas->step / 8;
+
+ if (data->count > 0)
+ goto proceed;
+
+ nrecv = recv(data->stream.fd, data->buffer, data->link_mtu,
+ io->nonblock ? MSG_DONTWAIT : 0);
+
+ if (nrecv < 0) {
+ ret = (errno == EPIPE) ? -EIO : -errno;
+ goto done;
+ }
+
+ if ((unsigned int) nrecv != data->link_mtu) {
+ ret = -EIO;
+ SNDERR(strerror(-ret));
+ goto done;
+ }
+
+ /* Increment hardware transmition pointer */
+ data->hw_ptr = (data->hw_ptr + data->link_mtu / frame_size) %
+ io->buffer_size;
+
+proceed:
+ buff = (unsigned char *) areas->addr +
+ (areas->first + areas->step * offset) / 8;
+
+ if ((data->count + size * frame_size) <= data->link_mtu)
+ frames_to_write = size;
+ else
+ frames_to_write = (data->link_mtu - data->count) / frame_size;
+
+ memcpy(buff, data->buffer + data->count, frame_size * frames_to_write);
+ data->count += (frame_size * frames_to_write);
+ data->count %= data->link_mtu;
+
+ /* Return written frames count */
+ ret = frames_to_write;
+
+done:
+ DBG("returning %lu", ret);
+ return ret;
+}
+
+static snd_pcm_sframes_t bluetooth_hsp_write(snd_pcm_ioplug_t *io,
+ const snd_pcm_channel_area_t *areas,
+ snd_pcm_uframes_t offset,
+ snd_pcm_uframes_t size)
+{
+ struct bluetooth_data *data = io->private_data;
+ snd_pcm_sframes_t ret = 0;
+ snd_pcm_uframes_t frames_to_read;
+ uint8_t *buff;
+ int rsend, frame_size;
+
+ DBG("areas->step=%u areas->first=%u offset=%lu, size=%lu io->nonblock=%u",
+ areas->step, areas->first, offset, size, io->nonblock);
+
+ if (io->hw_ptr > io->appl_ptr) {
+ ret = bluetooth_playback_stop(io);
+ if (ret == 0)
+ ret = -EPIPE;
+ goto done;
+ }
+
+ frame_size = areas->step / 8;
+ if ((data->count + size * frame_size) <= data->link_mtu)
+ frames_to_read = size;
+ else
+ frames_to_read = (data->link_mtu - data->count) / frame_size;
+
+ DBG("count=%d frames_to_read=%lu", data->count, frames_to_read);
+
+ /* Ready for more data */
+ buff = (uint8_t *) areas->addr +
+ (areas->first + areas->step * offset) / 8;
+ memcpy(data->buffer + data->count, buff, frame_size * frames_to_read);
+
+ /* Remember we have some frames in the pipe now */
+ data->count += frames_to_read * frame_size;
+ if (data->count != data->link_mtu) {
+ ret = frames_to_read;
+ goto done;
+ }
+
+ rsend = send(data->stream.fd, data->buffer, data->link_mtu,
+ io->nonblock ? MSG_DONTWAIT : 0);
+ if (rsend > 0) {
+ /* Reset count pointer */
+ data->count = 0;
+
+ ret = frames_to_read;
+ } else if (rsend < 0)
+ ret = (errno == EPIPE) ? -EIO : -errno;
+ else
+ ret = -EIO;
+
+done:
+ DBG("returning %ld", ret);
+ return ret;
+}
+
+static snd_pcm_sframes_t bluetooth_a2dp_read(snd_pcm_ioplug_t *io,
+ const snd_pcm_channel_area_t *areas,
+ snd_pcm_uframes_t offset, snd_pcm_uframes_t size)
+{
+ snd_pcm_uframes_t ret = 0;
+ return ret;
+}
+
+static int avdtp_write(struct bluetooth_data *data)
+{
+ int ret = 0;
+ struct rtp_header *header;
+ struct rtp_payload *payload;
+ struct bluetooth_a2dp *a2dp = &data->a2dp;
+
+ header = (void *) a2dp->buffer;
+ payload = (void *) (a2dp->buffer + sizeof(*header));
+
+ memset(a2dp->buffer, 0, sizeof(*header) + sizeof(*payload));
+
+ payload->frame_count = a2dp->frame_count;
+ header->v = 2;
+ header->pt = 1;
+ header->sequence_number = htons(a2dp->seq_num);
+ header->timestamp = htonl(a2dp->nsamples);
+ header->ssrc = htonl(1);
+
+ ret = send(data->stream.fd, a2dp->buffer, a2dp->count, MSG_DONTWAIT);
+ if (ret < 0) {
+ DBG("send returned %d errno %s.", ret, strerror(errno));
+ ret = -errno;
+ }
+
+ /* Reset buffer of data to send */
+ a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload);
+ a2dp->frame_count = 0;
+ a2dp->samples = 0;
+ a2dp->seq_num++;
+
+ return ret;
+}
+
+static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io,
+ const snd_pcm_channel_area_t *areas,
+ snd_pcm_uframes_t offset, snd_pcm_uframes_t size)
+{
+ struct bluetooth_data *data = io->private_data;
+ struct bluetooth_a2dp *a2dp = &data->a2dp;
+ snd_pcm_sframes_t ret = 0;
+ unsigned int bytes_left;
+ int frame_size, encoded;
+ ssize_t written;
+ uint8_t *buff;
+
+ DBG("areas->step=%u areas->first=%u offset=%lu size=%lu",
+ areas->step, areas->first, offset, size);
+ DBG("hw_ptr=%lu appl_ptr=%lu diff=%lu", io->hw_ptr, io->appl_ptr,
+ io->appl_ptr - io->hw_ptr);
+
+ /* Calutate starting pointers */
+ frame_size = areas->step / 8;
+ bytes_left = size * frame_size;
+ buff = (uint8_t *) areas->addr +
+ (areas->first + areas->step * (offset)) / 8;
+
+ /* Check for underrun */
+ if (io->hw_ptr > io->appl_ptr) {
+ ret = bluetooth_playback_stop(io);
+ if (ret == 0)
+ ret = -EPIPE;
+ data->reset = 1;
+ return ret;
+ }
+
+ /* Check if we should autostart */
+ if (io->state == SND_PCM_STATE_PREPARED) {
+ snd_pcm_sw_params_t *swparams;
+ snd_pcm_uframes_t threshold;
+
+ snd_pcm_sw_params_malloc(&swparams);
+ if (!snd_pcm_sw_params_current(io->pcm, swparams) &&
+ !snd_pcm_sw_params_get_start_threshold(swparams,
+ &threshold)) {
+ if (io->appl_ptr >= threshold) {
+ ret = snd_pcm_start(io->pcm);
+ if (ret != 0)
+ return ret;
+ }
+ }
+
+ snd_pcm_sw_params_free(swparams);
+ }
+
+ /* Check if we have any left over data from the last write */
+ if (data->count > 0) {
+ unsigned int additional_bytes_needed =
+ a2dp->codesize - data->count;
+ if (additional_bytes_needed > bytes_left)
+ goto out;
+
+ memcpy(data->buffer + data->count, buff,
+ additional_bytes_needed);
+
+ /* Enough data to encode (sbc wants 1k blocks) */
+ encoded = sbc_encode(&a2dp->sbc, data->buffer, a2dp->codesize,
+ a2dp->buffer + a2dp->count,
+ sizeof(a2dp->buffer) - a2dp->count,
+ &written);
+ if (encoded <= 0) {
+ DBG("Encoding error %d", encoded);
+ goto done;
+ }
+
+ /* Increment a2dp buffers */
+ a2dp->count += written;
+ a2dp->frame_count++;
+ a2dp->samples += encoded / frame_size;
+ a2dp->nsamples += encoded / frame_size;
+
+ /* No space left for another frame then send */
+ if (a2dp->count + written >= data->link_mtu) {
+ avdtp_write(data);
+ DBG("sending packet %d, count %d, link_mtu %u",
+ a2dp->seq_num, a2dp->count,
+ data->link_mtu);
+ }
+
+ /* Increment up buff pointer to take into account
+ * the data processed */
+ buff += additional_bytes_needed;
+ bytes_left -= additional_bytes_needed;
+
+ /* Since data has been process mark it as zero */
+ data->count = 0;
+ }
+
+
+ /* Process this buffer in full chunks */
+ while (bytes_left >= a2dp->codesize) {
+ /* Enough data to encode (sbc wants 1k blocks) */
+ encoded = sbc_encode(&a2dp->sbc, buff, a2dp->codesize,
+ a2dp->buffer + a2dp->count,
+ sizeof(a2dp->buffer) - a2dp->count,
+ &written);
+ if (encoded <= 0) {
+ DBG("Encoding error %d", encoded);
+ goto done;
+ }
+
+ /* Increment up buff pointer to take into account
+ * the data processed */
+ buff += a2dp->codesize;
+ bytes_left -= a2dp->codesize;
+
+ /* Increment a2dp buffers */
+ a2dp->count += written;
+ a2dp->frame_count++;
+ a2dp->samples += encoded / frame_size;
+ a2dp->nsamples += encoded / frame_size;
+
+ /* No space left for another frame then send */
+ if (a2dp->count + written >= data->link_mtu) {
+ avdtp_write(data);
+ DBG("sending packet %d, count %d, link_mtu %u",
+ a2dp->seq_num, a2dp->count,
+ data->link_mtu);
+ }
+ }
+
+out:
+ /* Copy the extra to our temp buffer for the next write */
+ if (bytes_left > 0) {
+ memcpy(data->buffer + data->count, buff, bytes_left);
+ data->count += bytes_left;
+ bytes_left = 0;
+ }
+
+done:
+ DBG("returning %ld", size - bytes_left / frame_size);
+
+ return size - bytes_left / frame_size;
+}
+
+static int bluetooth_playback_delay(snd_pcm_ioplug_t *io,
+ snd_pcm_sframes_t *delayp)
+{
+ DBG("");
+
+ /* This updates io->hw_ptr value using pointer() function */
+ snd_pcm_hwsync(io->pcm);
+
+ *delayp = io->appl_ptr - io->hw_ptr;
+ if ((io->state == SND_PCM_STATE_RUNNING) && (*delayp < 0)) {
+ io->callback->stop(io);
+ io->state = SND_PCM_STATE_XRUN;
+ *delayp = 0;
+ }
+
+ /* This should never fail, ALSA API is really not
+ prepared to handle a non zero return value */
+ return 0;
+}
+
+static snd_pcm_ioplug_callback_t bluetooth_hsp_playback = {
+ .start = bluetooth_playback_start,
+ .stop = bluetooth_playback_stop,
+ .pointer = bluetooth_pointer,
+ .close = bluetooth_close,
+ .hw_params = bluetooth_hsp_hw_params,
+ .prepare = bluetooth_prepare,
+ .transfer = bluetooth_hsp_write,
+ .poll_descriptors_count = bluetooth_playback_poll_descriptors_count,
+ .poll_descriptors = bluetooth_playback_poll_descriptors,
+ .poll_revents = bluetooth_playback_poll_revents,
+ .delay = bluetooth_playback_delay,
+};
+
+static snd_pcm_ioplug_callback_t bluetooth_hsp_capture = {
+ .start = bluetooth_start,
+ .stop = bluetooth_stop,
+ .pointer = bluetooth_pointer,
+ .close = bluetooth_close,
+ .hw_params = bluetooth_hsp_hw_params,
+ .prepare = bluetooth_prepare,
+ .transfer = bluetooth_hsp_read,
+ .poll_descriptors = bluetooth_poll_descriptors,
+ .poll_revents = bluetooth_poll_revents,
+};
+
+static snd_pcm_ioplug_callback_t bluetooth_a2dp_playback = {
+ .start = bluetooth_playback_start,
+ .stop = bluetooth_playback_stop,
+ .pointer = bluetooth_pointer,
+ .close = bluetooth_close,
+ .hw_params = bluetooth_a2dp_hw_params,
+ .prepare = bluetooth_prepare,
+ .transfer = bluetooth_a2dp_write,
+ .poll_descriptors_count = bluetooth_playback_poll_descriptors_count,
+ .poll_descriptors = bluetooth_playback_poll_descriptors,
+ .poll_revents = bluetooth_playback_poll_revents,
+ .delay = bluetooth_playback_delay,
+};
+
+static snd_pcm_ioplug_callback_t bluetooth_a2dp_capture = {
+ .start = bluetooth_start,
+ .stop = bluetooth_stop,
+ .pointer = bluetooth_pointer,
+ .close = bluetooth_close,
+ .hw_params = bluetooth_a2dp_hw_params,
+ .prepare = bluetooth_prepare,
+ .transfer = bluetooth_a2dp_read,
+ .poll_descriptors = bluetooth_poll_descriptors,
+ .poll_revents = bluetooth_poll_revents,
+};
+
+#define ARRAY_NELEMS(a) (sizeof((a)) / sizeof((a)[0]))
+
+static int bluetooth_hsp_hw_constraint(snd_pcm_ioplug_t *io)
+{
+ struct bluetooth_data *data = io->private_data;
+ snd_pcm_access_t access_list[] = {
+ SND_PCM_ACCESS_RW_INTERLEAVED,
+ /* Mmap access is really useless fo this driver, but we
+ * support it because some pieces of software out there
+ * insist on using it */
+ SND_PCM_ACCESS_MMAP_INTERLEAVED
+ };
+ unsigned int format_list[] = {
+ SND_PCM_FORMAT_S16
+ };
+ int err;
+
+ /* access type */
+ err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS,
+ ARRAY_NELEMS(access_list), access_list);
+ if (err < 0)
+ return err;
+
+ /* supported formats */
+ err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_FORMAT,
+ ARRAY_NELEMS(format_list), format_list);
+ if (err < 0)
+ return err;
+
+ /* supported channels */
+ err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_CHANNELS,
+ 1, 1);
+ if (err < 0)
+ return err;
+
+ /* supported rate */
+ err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_RATE,
+ 8000, 8000);
+ if (err < 0)
+ return err;
+
+ /* supported block size */
+ err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES,
+ data->link_mtu, data->link_mtu);
+ if (err < 0)
+ return err;
+
+ err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIODS,
+ 2, 200);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io)
+{
+ struct bluetooth_data *data = io->private_data;
+ struct bluetooth_a2dp *a2dp = &data->a2dp;
+ struct bluetooth_alsa_config *cfg = &data->alsa_config;
+ snd_pcm_access_t access_list[] = {
+ SND_PCM_ACCESS_RW_INTERLEAVED,
+ /* Mmap access is really useless fo this driver, but we
+ * support it because some pieces of software out there
+ * insist on using it */
+ SND_PCM_ACCESS_MMAP_INTERLEAVED
+ };
+ unsigned int format_list[] = {
+ SND_PCM_FORMAT_S16
+ };
+ unsigned int rate_list[4];
+ unsigned int rate_count;
+ int err, min_channels, max_channels;
+ unsigned int period_list[] = {
+ 2048,
+ 4096, /* e.g. 23.2msec/period (stereo 16bit at 44.1kHz) */
+ 8192
+ };
+
+ /* access type */
+ err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS,
+ ARRAY_NELEMS(access_list), access_list);
+ if (err < 0)
+ return err;
+
+ /* supported formats */
+ err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_FORMAT,
+ ARRAY_NELEMS(format_list), format_list);
+ if (err < 0)
+ return err;
+
+ /* supported channels */
+ if (cfg->has_channel_mode)
+ a2dp->sbc_capabilities.channel_mode = cfg->channel_mode;
+
+ if (a2dp->sbc_capabilities.channel_mode &
+ BT_A2DP_CHANNEL_MODE_MONO)
+ min_channels = 1;
+ else
+ min_channels = 2;
+
+ if (a2dp->sbc_capabilities.channel_mode &
+ (~BT_A2DP_CHANNEL_MODE_MONO))
+ max_channels = 2;
+ else
+ max_channels = 1;
+
+ err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_CHANNELS,
+ min_channels, max_channels);
+ if (err < 0)
+ return err;
+
+ /* supported buffer sizes
+ * (can be used as 3*8192, 6*4096, 12*2048, ...) */
+ err = snd_pcm_ioplug_set_param_minmax(io,
+ SND_PCM_IOPLUG_HW_BUFFER_BYTES,
+ 8192*3, 8192*3);
+ if (err < 0)
+ return err;
+
+ /* supported block sizes: */
+ err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES,
+ ARRAY_NELEMS(period_list), period_list);
+ if (err < 0)
+ return err;
+
+ /* supported rates */
+ rate_count = 0;
+ if (cfg->has_rate) {
+ rate_list[rate_count] = cfg->rate;
+ rate_count++;
+ } else {
+ if (a2dp->sbc_capabilities.frequency &
+ BT_SBC_SAMPLING_FREQ_16000) {
+ rate_list[rate_count] = 16000;
+ rate_count++;
+ }
+
+ if (a2dp->sbc_capabilities.frequency &
+ BT_SBC_SAMPLING_FREQ_32000) {
+ rate_list[rate_count] = 32000;
+ rate_count++;
+ }
+
+ if (a2dp->sbc_capabilities.frequency &
+ BT_SBC_SAMPLING_FREQ_44100) {
+ rate_list[rate_count] = 44100;
+ rate_count++;
+ }
+
+ if (a2dp->sbc_capabilities.frequency &
+ BT_SBC_SAMPLING_FREQ_48000) {
+ rate_list[rate_count] = 48000;
+ rate_count++;
+ }
+ }
+
+ err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_RATE,
+ rate_count, rate_list);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int bluetooth_parse_config(snd_config_t *conf,
+ struct bluetooth_alsa_config *bt_config)
+{
+ snd_config_iterator_t i, next;
+
+ memset(bt_config, 0, sizeof(struct bluetooth_alsa_config));
+
+ /* Set defaults */
+ bt_config->autoconnect = 1;
+
+ snd_config_for_each(i, next, conf) {
+ snd_config_t *n = snd_config_iterator_entry(i);
+ const char *id, *value;
+
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+
+ if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0)
+ continue;
+
+ if (strcmp(id, "autoconnect") == 0) {
+ int b;
+
+ b = snd_config_get_bool(n);
+ if (b < 0) {
+ SNDERR("Invalid type for %s", id);
+ return -EINVAL;
+ }
+
+ bt_config->autoconnect = b;
+ continue;
+ }
+
+ if (strcmp(id, "device") == 0 || strcmp(id, "bdaddr") == 0) {
+ if (snd_config_get_string(n, &value) < 0) {
+ SNDERR("Invalid type for %s", id);
+ return -EINVAL;
+ }
+
+ bt_config->has_device = 1;
+ strncpy(bt_config->device, value, 18);
+ continue;
+ }
+
+ if (strcmp(id, "profile") == 0) {
+ if (snd_config_get_string(n, &value) < 0) {
+ SNDERR("Invalid type for %s", id);
+ return -EINVAL;
+ }
+
+ if (strcmp(value, "auto") == 0) {
+ bt_config->transport = BT_CAPABILITIES_TRANSPORT_ANY;
+ bt_config->has_transport = 1;
+ } else if (strcmp(value, "voice") == 0 ||
+ strcmp(value, "hfp") == 0) {
+ bt_config->transport = BT_CAPABILITIES_TRANSPORT_SCO;
+ bt_config->has_transport = 1;
+ } else if (strcmp(value, "hifi") == 0 ||
+ strcmp(value, "a2dp") == 0) {
+ bt_config->transport = BT_CAPABILITIES_TRANSPORT_A2DP;
+ bt_config->has_transport = 1;
+ }
+ continue;
+ }
+
+ if (strcmp(id, "rate") == 0) {
+ if (snd_config_get_string(n, &value) < 0) {
+ SNDERR("Invalid type for %s", id);
+ return -EINVAL;
+ }
+
+ bt_config->rate = atoi(value);
+ bt_config->has_rate = 1;
+ continue;
+ }
+
+ if (strcmp(id, "mode") == 0) {
+ if (snd_config_get_string(n, &value) < 0) {
+ SNDERR("Invalid type for %s", id);
+ return -EINVAL;
+ }
+
+ if (strcmp(value, "mono") == 0) {
+ bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_MONO;
+ bt_config->has_channel_mode = 1;
+ } else if (strcmp(value, "dual") == 0) {
+ bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL;
+ bt_config->has_channel_mode = 1;
+ } else if (strcmp(value, "stereo") == 0) {
+ bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO;
+ bt_config->has_channel_mode = 1;
+ } else if (strcmp(value, "joint") == 0) {
+ bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO;
+ bt_config->has_channel_mode = 1;
+ }
+ continue;
+ }
+
+ if (strcmp(id, "allocation") == 0) {
+ if (snd_config_get_string(n, &value) < 0) {
+ SNDERR("Invalid type for %s", id);
+ return -EINVAL;
+ }
+
+ if (strcmp(value, "loudness") == 0) {
+ bt_config->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS;
+ bt_config->has_allocation_method = 1;
+ } else if (strcmp(value, "snr") == 0) {
+ bt_config->allocation_method = BT_A2DP_ALLOCATION_SNR;
+ bt_config->has_allocation_method = 1;
+ }
+ continue;
+ }
+
+ if (strcmp(id, "subbands") == 0) {
+ if (snd_config_get_string(n, &value) < 0) {
+ SNDERR("Invalid type for %s", id);
+ return -EINVAL;
+ }
+
+ bt_config->subbands = atoi(value);
+ bt_config->has_subbands = 1;
+ continue;
+ }
+
+ if (strcmp(id, "blocks") == 0) {
+ if (snd_config_get_string(n, &value) < 0) {
+ SNDERR("Invalid type for %s", id);
+ return -EINVAL;
+ }
+
+ bt_config->block_length = atoi(value);
+ bt_config->has_block_length = 1;
+ continue;
+ }
+
+ if (strcmp(id, "bitpool") == 0) {
+ if (snd_config_get_string(n, &value) < 0) {
+ SNDERR("Invalid type for %s", id);
+ return -EINVAL;
+ }
+
+ bt_config->bitpool = atoi(value);
+ bt_config->has_bitpool = 1;
+ continue;
+ }
+
+ SNDERR("Unknown field %s", id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int audioservice_send(int sk, const bt_audio_msg_header_t *msg)
+{
+ int err;
+ uint16_t length;
+
+ length = msg->length ? msg->length : BT_SUGGESTED_BUFFER_SIZE;
+
+ DBG("sending %s:%s", bt_audio_strtype(msg->type),
+ bt_audio_strname(msg->name));
+ if (send(sk, msg, length, 0) > 0)
+ err = 0;
+ else {
+ err = -errno;
+ SNDERR("Error sending data to audio service: %s(%d)",
+ strerror(errno), errno);
+ }
+
+ return err;
+}
+
+static int audioservice_recv(int sk, bt_audio_msg_header_t *inmsg)
+{
+ int err;
+ ssize_t ret;
+ const char *type, *name;
+ uint16_t length;
+
+ length = inmsg->length ? inmsg->length : BT_SUGGESTED_BUFFER_SIZE;
+
+ DBG("trying to receive msg from audio service...");
+
+ ret = recv(sk, inmsg, length, 0);
+ if (ret < 0) {
+ err = -errno;
+ SNDERR("Error receiving IPC data from bluetoothd: %s (%d)",
+ strerror(errno), errno);
+ } else if ((size_t) ret < sizeof(bt_audio_msg_header_t)) {
+ SNDERR("Too short (%d bytes) IPC packet from bluetoothd", ret);
+ err = -EINVAL;
+ } else {
+ type = bt_audio_strtype(inmsg->type);
+ name = bt_audio_strname(inmsg->name);
+ if (type && name) {
+ DBG("Received %s - %s", type, name);
+ err = 0;
+ } else {
+ err = -EINVAL;
+ SNDERR("Bogus message type %d - name %d"
+ " received from audio service",
+ inmsg->type, inmsg->name);
+ }
+
+ }
+
+ return err;
+}
+
+static int audioservice_expect(int sk, bt_audio_msg_header_t *rsp,
+ int expected_name)
+{
+ bt_audio_error_t *error;
+ int err = audioservice_recv(sk, rsp);
+
+ if (err != 0)
+ return err;
+
+ if (rsp->name != expected_name) {
+ err = -EINVAL;
+ SNDERR("Bogus message %s received while %s was expected",
+ bt_audio_strname(rsp->name),
+ bt_audio_strname(expected_name));
+ }
+
+ if (rsp->type == BT_ERROR) {
+ error = (void *) rsp;
+ SNDERR("%s failed : %s(%d)",
+ bt_audio_strname(rsp->name),
+ strerror(error->posix_errno),
+ error->posix_errno);
+ return -error->posix_errno;
+ }
+
+ return err;
+}
+
+static int bluetooth_parse_capabilities(struct bluetooth_data *data,
+ struct bt_get_capabilities_rsp *rsp)
+{
+ int bytes_left = rsp->h.length - sizeof(*rsp);
+ codec_capabilities_t *codec = (void *) rsp->data;
+
+ data->transport = codec->transport;
+
+ if (codec->transport != BT_CAPABILITIES_TRANSPORT_A2DP)
+ return 0;
+
+ while (bytes_left > 0) {
+ if ((codec->type == BT_A2DP_SBC_SINK) &&
+ !(codec->lock & BT_WRITE_LOCK))
+ break;
+
+ bytes_left -= codec->length;
+ codec = (void *) codec + codec->length;
+ }
+
+ if (bytes_left <= 0 ||
+ codec->length != sizeof(data->a2dp.sbc_capabilities))
+ return -EINVAL;
+
+ memcpy(&data->a2dp.sbc_capabilities, codec, codec->length);
+
+ return 0;
+}
+
+static int bluetooth_init(struct bluetooth_data *data,
+ snd_pcm_stream_t stream, snd_config_t *conf)
+{
+ int sk, err;
+ struct bluetooth_alsa_config *alsa_conf = &data->alsa_config;
+ char buf[BT_SUGGESTED_BUFFER_SIZE];
+ struct bt_get_capabilities_req *req = (void *) buf;
+ struct bt_get_capabilities_rsp *rsp = (void *) buf;
+
+ memset(data, 0, sizeof(struct bluetooth_data));
+
+ err = bluetooth_parse_config(conf, alsa_conf);
+ if (err < 0)
+ return err;
+
+ data->server.fd = -1;
+ data->stream.fd = -1;
+
+ sk = bt_audio_service_open();
+ if (sk <= 0) {
+ err = -errno;
+ goto failed;
+ }
+
+ data->server.fd = sk;
+ data->server.events = POLLIN;
+
+ data->pipefd[0] = -1;
+ data->pipefd[1] = -1;
+
+ if (pipe(data->pipefd) < 0) {
+ err = -errno;
+ goto failed;
+ }
+ if (fcntl(data->pipefd[0], F_SETFL, O_NONBLOCK) < 0) {
+ err = -errno;
+ goto failed;
+ }
+ if (fcntl(data->pipefd[1], F_SETFL, O_NONBLOCK) < 0) {
+ err = -errno;
+ goto failed;
+ }
+
+ memset(req, 0, BT_SUGGESTED_BUFFER_SIZE);
+ req->h.type = BT_REQUEST;
+ req->h.name = BT_GET_CAPABILITIES;
+ req->h.length = sizeof(*req);
+
+ if (alsa_conf->autoconnect)
+ req->flags |= BT_FLAG_AUTOCONNECT;
+ strncpy(req->destination, alsa_conf->device, 18);
+ if (alsa_conf->has_transport)
+ req->transport = alsa_conf->transport;
+ else
+ req->transport = BT_CAPABILITIES_TRANSPORT_ANY;
+
+ err = audioservice_send(data->server.fd, &req->h);
+ if (err < 0)
+ goto failed;
+
+ rsp->h.length = 0;
+ err = audioservice_expect(data->server.fd, &rsp->h,
+ BT_GET_CAPABILITIES);
+ if (err < 0)
+ goto failed;
+
+ bluetooth_parse_capabilities(data, rsp);
+
+ return 0;
+
+failed:
+ if (sk >= 0)
+ bt_audio_service_close(sk);
+ return err;
+}
+
+SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth);
+
+SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth)
+{
+ struct bluetooth_data *data;
+ int err;
+
+ DBG("Bluetooth PCM plugin (%s)",
+ stream == SND_PCM_STREAM_PLAYBACK ? "Playback" : "Capture");
+
+ data = malloc(sizeof(struct bluetooth_data));
+ if (!data) {
+ err = -ENOMEM;
+ goto error;
+ }
+
+ err = bluetooth_init(data, stream, conf);
+ if (err < 0)
+ goto error;
+
+ data->io.version = SND_PCM_IOPLUG_VERSION;
+ data->io.name = "Bluetooth Audio Device";
+ data->io.mmap_rw = 0; /* No direct mmap communication */
+ data->io.private_data = data;
+
+ if (data->transport == BT_CAPABILITIES_TRANSPORT_A2DP)
+ data->io.callback = stream == SND_PCM_STREAM_PLAYBACK ?
+ &bluetooth_a2dp_playback :
+ &bluetooth_a2dp_capture;
+ else
+ data->io.callback = stream == SND_PCM_STREAM_PLAYBACK ?
+ &bluetooth_hsp_playback :
+ &bluetooth_hsp_capture;
+
+ err = snd_pcm_ioplug_create(&data->io, name, stream, mode);
+ if (err < 0)
+ goto error;
+
+ if (data->transport == BT_CAPABILITIES_TRANSPORT_A2DP)
+ err = bluetooth_a2dp_hw_constraint(&data->io);
+ else
+ err = bluetooth_hsp_hw_constraint(&data->io);
+
+ if (err < 0) {
+ snd_pcm_ioplug_delete(&data->io);
+ goto error;
+ }
+
+ *pcmp = data->io.pcm;
+
+ return 0;
+
+error:
+ if (data)
+ bluetooth_exit(data);
+
+ return err;
+}
+
+SND_PCM_PLUGIN_SYMBOL(bluetooth);
diff --git a/audio/rtp.h b/audio/rtp.h
new file mode 100644
index 0000000..45fddcf
--- /dev/null
+++ b/audio/rtp.h
@@ -0,0 +1,76 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct rtp_header {
+ unsigned cc:4;
+ unsigned x:1;
+ unsigned p:1;
+ unsigned v:2;
+
+ unsigned pt:7;
+ unsigned m:1;
+
+ uint16_t sequence_number;
+ uint32_t timestamp;
+ uint32_t ssrc;
+ uint32_t csrc[0];
+} __attribute__ ((packed));
+
+struct rtp_payload {
+ unsigned frame_count:4;
+ unsigned rfa0:1;
+ unsigned is_last_fragment:1;
+ unsigned is_first_fragment:1;
+ unsigned is_fragmented:1;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct rtp_header {
+ unsigned v:2;
+ unsigned p:1;
+ unsigned x:1;
+ unsigned cc:4;
+
+ unsigned m:1;
+ unsigned pt:7;
+
+ uint16_t sequence_number;
+ uint32_t timestamp;
+ uint32_t ssrc;
+ uint32_t csrc[0];
+} __attribute__ ((packed));
+
+struct rtp_payload {
+ unsigned is_fragmented:1;
+ unsigned is_first_fragment:1;
+ unsigned is_last_fragment:1;
+ unsigned rfa0:1;
+ unsigned frame_count:4;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
diff --git a/audio/sink.c b/audio/sink.c
new file mode 100644
index 0000000..2d5db18
--- /dev/null
+++ b/audio/sink.c
@@ -0,0 +1,743 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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 <stdint.h>
+#include <errno.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+
+#include "device.h"
+#include "avdtp.h"
+#include "media.h"
+#include "a2dp.h"
+#include "error.h"
+#include "sink.h"
+#include "dbus-common.h"
+#include "../src/adapter.h"
+#include "../src/device.h"
+
+#define STREAM_SETUP_RETRY_TIMER 2
+
+struct pending_request {
+ DBusConnection *conn;
+ DBusMessage *msg;
+ unsigned int id;
+};
+
+struct sink {
+ struct audio_device *dev;
+ struct avdtp *session;
+ struct avdtp_stream *stream;
+ unsigned int cb_id;
+ guint retry_id;
+ avdtp_session_state_t session_state;
+ avdtp_state_t stream_state;
+ sink_state_t state;
+ struct pending_request *connect;
+ struct pending_request *disconnect;
+ DBusConnection *conn;
+};
+
+struct sink_state_callback {
+ sink_state_cb cb;
+ void *user_data;
+ unsigned int id;
+};
+
+static GSList *sink_callbacks = NULL;
+
+static unsigned int avdtp_callback_id = 0;
+
+static char *str_state[] = {
+ "SINK_STATE_DISCONNECTED",
+ "SINK_STATE_CONNECTING",
+ "SINK_STATE_CONNECTED",
+ "SINK_STATE_PLAYING",
+};
+
+static const char *state2str(sink_state_t state)
+{
+ switch (state) {
+ case SINK_STATE_DISCONNECTED:
+ return "disconnected";
+ case SINK_STATE_CONNECTING:
+ return "connecting";
+ case SINK_STATE_CONNECTED:
+ return "connected";
+ case SINK_STATE_PLAYING:
+ return "playing";
+ default:
+ error("Invalid sink state %d", state);
+ return NULL;
+ }
+}
+
+static void sink_set_state(struct audio_device *dev, sink_state_t new_state)
+{
+ struct sink *sink = dev->sink;
+ const char *state_str;
+ sink_state_t old_state = sink->state;
+ GSList *l;
+
+ sink->state = new_state;
+
+ state_str = state2str(new_state);
+ if (state_str)
+ emit_property_changed(dev->conn, dev->path,
+ AUDIO_SINK_INTERFACE, "State",
+ DBUS_TYPE_STRING, &state_str);
+
+ DBG("State changed %s: %s -> %s", dev->path, str_state[old_state],
+ str_state[new_state]);
+
+ for (l = sink_callbacks; l != NULL; l = l->next) {
+ struct sink_state_callback *cb = l->data;
+ cb->cb(dev, old_state, new_state, cb->user_data);
+ }
+}
+
+static void avdtp_state_callback(struct audio_device *dev,
+ struct avdtp *session,
+ avdtp_session_state_t old_state,
+ avdtp_session_state_t new_state,
+ void *user_data)
+{
+ struct sink *sink = dev->sink;
+
+ if (sink == NULL)
+ return;
+
+ switch (new_state) {
+ case AVDTP_SESSION_STATE_DISCONNECTED:
+ if (sink->state != SINK_STATE_CONNECTING) {
+ gboolean value = FALSE;
+ g_dbus_emit_signal(dev->conn, dev->path,
+ AUDIO_SINK_INTERFACE, "Disconnected",
+ DBUS_TYPE_INVALID);
+ emit_property_changed(dev->conn, dev->path,
+ AUDIO_SINK_INTERFACE, "Connected",
+ DBUS_TYPE_BOOLEAN, &value);
+ }
+ sink_set_state(dev, SINK_STATE_DISCONNECTED);
+ break;
+ case AVDTP_SESSION_STATE_CONNECTING:
+ sink_set_state(dev, SINK_STATE_CONNECTING);
+ break;
+ case AVDTP_SESSION_STATE_CONNECTED:
+ break;
+ }
+
+ sink->session_state = new_state;
+}
+
+static void pending_request_free(struct audio_device *dev,
+ struct pending_request *pending)
+{
+ if (pending->conn)
+ dbus_connection_unref(pending->conn);
+ if (pending->msg)
+ dbus_message_unref(pending->msg);
+ if (pending->id)
+ a2dp_cancel(dev, pending->id);
+
+ g_free(pending);
+}
+
+static void stream_state_changed(struct avdtp_stream *stream,
+ avdtp_state_t old_state,
+ avdtp_state_t new_state,
+ struct avdtp_error *err,
+ void *user_data)
+{
+ struct audio_device *dev = user_data;
+ struct sink *sink = dev->sink;
+ gboolean value;
+
+ if (err)
+ return;
+
+ switch (new_state) {
+ case AVDTP_STATE_IDLE:
+ if (sink->disconnect) {
+ DBusMessage *reply;
+ struct pending_request *p;
+
+ p = sink->disconnect;
+ sink->disconnect = NULL;
+
+ reply = dbus_message_new_method_return(p->msg);
+ g_dbus_send_message(p->conn, reply);
+ pending_request_free(dev, p);
+ }
+
+ if (sink->session) {
+ avdtp_unref(sink->session);
+ sink->session = NULL;
+ }
+ sink->stream = NULL;
+ sink->cb_id = 0;
+ break;
+ case AVDTP_STATE_OPEN:
+ if (old_state == AVDTP_STATE_CONFIGURED &&
+ sink->state == SINK_STATE_CONNECTING) {
+ value = TRUE;
+ g_dbus_emit_signal(dev->conn, dev->path,
+ AUDIO_SINK_INTERFACE,
+ "Connected",
+ DBUS_TYPE_INVALID);
+ emit_property_changed(dev->conn, dev->path,
+ AUDIO_SINK_INTERFACE,
+ "Connected",
+ DBUS_TYPE_BOOLEAN, &value);
+ } else if (old_state == AVDTP_STATE_STREAMING) {
+ value = FALSE;
+ g_dbus_emit_signal(dev->conn, dev->path,
+ AUDIO_SINK_INTERFACE,
+ "Stopped",
+ DBUS_TYPE_INVALID);
+ emit_property_changed(dev->conn, dev->path,
+ AUDIO_SINK_INTERFACE,
+ "Playing",
+ DBUS_TYPE_BOOLEAN, &value);
+ }
+ sink_set_state(dev, SINK_STATE_CONNECTED);
+ break;
+ case AVDTP_STATE_STREAMING:
+ value = TRUE;
+ g_dbus_emit_signal(dev->conn, dev->path, AUDIO_SINK_INTERFACE,
+ "Playing", DBUS_TYPE_INVALID);
+ emit_property_changed(dev->conn, dev->path,
+ AUDIO_SINK_INTERFACE, "Playing",
+ DBUS_TYPE_BOOLEAN, &value);
+ sink_set_state(dev, SINK_STATE_PLAYING);
+ break;
+ case AVDTP_STATE_CONFIGURED:
+ case AVDTP_STATE_CLOSING:
+ case AVDTP_STATE_ABORTING:
+ default:
+ break;
+ }
+
+ sink->stream_state = new_state;
+}
+
+static void error_failed(DBusConnection *conn, DBusMessage *msg,
+ const char *desc)
+{
+ DBusMessage *reply = btd_error_failed(msg, desc);
+ g_dbus_send_message(conn, reply);
+}
+
+static gboolean stream_setup_retry(gpointer user_data)
+{
+ struct sink *sink = user_data;
+ struct pending_request *pending = sink->connect;
+
+ sink->retry_id = 0;
+
+ if (sink->stream_state >= AVDTP_STATE_OPEN) {
+ DBG("Stream successfully created, after XCASE connect:connect");
+ if (pending->msg) {
+ DBusMessage *reply;
+ reply = dbus_message_new_method_return(pending->msg);
+ g_dbus_send_message(pending->conn, reply);
+ }
+ } else {
+ DBG("Stream setup failed, after XCASE connect:connect");
+ if (pending->msg)
+ error_failed(pending->conn, pending->msg, "Stream setup failed");
+ }
+
+ sink->connect = NULL;
+ pending_request_free(sink->dev, pending);
+
+ return FALSE;
+}
+
+static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep,
+ struct avdtp_stream *stream,
+ struct avdtp_error *err, void *user_data)
+{
+ struct sink *sink = user_data;
+ struct pending_request *pending;
+
+ pending = sink->connect;
+
+ pending->id = 0;
+
+ if (stream) {
+ DBG("Stream successfully created");
+
+ if (pending->msg) {
+ DBusMessage *reply;
+ reply = dbus_message_new_method_return(pending->msg);
+ g_dbus_send_message(pending->conn, reply);
+ }
+
+ sink->connect = NULL;
+ pending_request_free(sink->dev, pending);
+
+ return;
+ }
+
+ avdtp_unref(sink->session);
+ sink->session = NULL;
+ if (avdtp_error_category(err) == AVDTP_ERRNO
+ && avdtp_error_posix_errno(err) != EHOSTDOWN) {
+ DBG("connect:connect XCASE detected");
+ sink->retry_id = g_timeout_add_seconds(STREAM_SETUP_RETRY_TIMER,
+ stream_setup_retry,
+ sink);
+ } else {
+ if (pending->msg)
+ error_failed(pending->conn, pending->msg, "Stream setup failed");
+ sink->connect = NULL;
+ pending_request_free(sink->dev, pending);
+ DBG("Stream setup failed : %s", avdtp_strerror(err));
+ }
+}
+
+static void select_complete(struct avdtp *session, struct a2dp_sep *sep,
+ GSList *caps, void *user_data)
+{
+ struct sink *sink = user_data;
+ struct pending_request *pending;
+ int id;
+
+ pending = sink->connect;
+ pending->id = 0;
+
+ id = a2dp_config(session, sep, stream_setup_complete, caps, sink);
+ if (id == 0)
+ goto failed;
+
+ pending->id = id;
+ return;
+
+failed:
+ if (pending->msg)
+ error_failed(pending->conn, pending->msg, "Stream setup failed");
+ pending_request_free(sink->dev, pending);
+ sink->connect = NULL;
+ avdtp_unref(sink->session);
+ sink->session = NULL;
+}
+
+static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp_error *err,
+ void *user_data)
+{
+ struct sink *sink = user_data;
+ struct pending_request *pending;
+ int id;
+
+ if (!sink->connect) {
+ avdtp_unref(sink->session);
+ sink->session = NULL;
+ return;
+ }
+
+ pending = sink->connect;
+
+ if (err) {
+ avdtp_unref(sink->session);
+ sink->session = NULL;
+ if (avdtp_error_category(err) == AVDTP_ERRNO
+ && avdtp_error_posix_errno(err) != EHOSTDOWN) {
+ DBG("connect:connect XCASE detected");
+ sink->retry_id =
+ g_timeout_add_seconds(STREAM_SETUP_RETRY_TIMER,
+ stream_setup_retry,
+ sink);
+ } else
+ goto failed;
+ return;
+ }
+
+ DBG("Discovery complete");
+
+ id = a2dp_select_capabilities(sink->session, AVDTP_SEP_TYPE_SINK, NULL,
+ select_complete, sink);
+ if (id == 0)
+ goto failed;
+
+ pending->id = id;
+ return;
+
+failed:
+ if (pending->msg)
+ error_failed(pending->conn, pending->msg, "Stream setup failed");
+ pending_request_free(sink->dev, pending);
+ sink->connect = NULL;
+ avdtp_unref(sink->session);
+ sink->session = NULL;
+}
+
+gboolean sink_setup_stream(struct sink *sink, struct avdtp *session)
+{
+ if (sink->connect || sink->disconnect)
+ return FALSE;
+
+ if (session && !sink->session)
+ sink->session = avdtp_ref(session);
+
+ if (!sink->session)
+ return FALSE;
+
+ avdtp_set_auto_disconnect(sink->session, FALSE);
+
+ if (avdtp_discover(sink->session, discovery_complete, sink) < 0)
+ return FALSE;
+
+ sink->connect = g_new0(struct pending_request, 1);
+
+ return TRUE;
+}
+
+static DBusMessage *sink_connect(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct audio_device *dev = data;
+ struct sink *sink = dev->sink;
+ struct pending_request *pending;
+
+ if (!sink->session)
+ sink->session = avdtp_get(&dev->src, &dev->dst);
+
+ if (!sink->session)
+ return btd_error_failed(msg, "Unable to get a session");
+
+ if (sink->connect || sink->disconnect)
+ return btd_error_busy(msg);
+
+ if (sink->stream_state >= AVDTP_STATE_OPEN)
+ return btd_error_already_connected(msg);
+
+ if (!sink_setup_stream(sink, NULL))
+ return btd_error_failed(msg, "Failed to create a stream");
+
+ dev->auto_connect = FALSE;
+
+ pending = sink->connect;
+
+ pending->conn = dbus_connection_ref(conn);
+ pending->msg = dbus_message_ref(msg);
+
+ DBG("stream creation in progress");
+
+ return NULL;
+}
+
+static DBusMessage *sink_disconnect(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct audio_device *device = data;
+ struct sink *sink = device->sink;
+ struct pending_request *pending;
+ int err;
+
+ if (!sink->session)
+ return btd_error_not_connected(msg);
+
+ if (sink->connect || sink->disconnect)
+ return btd_error_busy(msg);
+
+ if (sink->stream_state < AVDTP_STATE_OPEN) {
+ DBusMessage *reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+ avdtp_unref(sink->session);
+ sink->session = NULL;
+ return reply;
+ }
+
+ err = avdtp_close(sink->session, sink->stream, FALSE);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+ pending = g_new0(struct pending_request, 1);
+ pending->conn = dbus_connection_ref(conn);
+ pending->msg = dbus_message_ref(msg);
+ sink->disconnect = pending;
+
+ return NULL;
+}
+
+static DBusMessage *sink_is_connected(DBusConnection *conn,
+ DBusMessage *msg,
+ void *data)
+{
+ struct audio_device *device = data;
+ struct sink *sink = device->sink;
+ DBusMessage *reply;
+ dbus_bool_t connected;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ connected = (sink->stream_state >= AVDTP_STATE_CONFIGURED);
+
+ dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &connected,
+ DBUS_TYPE_INVALID);
+
+ return reply;
+}
+
+static DBusMessage *sink_get_properties(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct audio_device *device = data;
+ struct sink *sink = device->sink;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ const char *state;
+ gboolean value;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ /* Playing */
+ value = (sink->stream_state == AVDTP_STATE_STREAMING);
+ dict_append_entry(&dict, "Playing", DBUS_TYPE_BOOLEAN, &value);
+
+ /* Connected */
+ value = (sink->stream_state >= AVDTP_STATE_CONFIGURED);
+ dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &value);
+
+ /* State */
+ state = state2str(sink->state);
+ if (state)
+ dict_append_entry(&dict, "State", DBUS_TYPE_STRING, &state);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static GDBusMethodTable sink_methods[] = {
+ { "Connect", "", "", sink_connect,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "Disconnect", "", "", sink_disconnect,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "IsConnected", "", "b", sink_is_connected,
+ G_DBUS_METHOD_FLAG_DEPRECATED },
+ { "GetProperties", "", "a{sv}",sink_get_properties },
+ { NULL, NULL, NULL, NULL }
+};
+
+static GDBusSignalTable sink_signals[] = {
+ { "Connected", "", G_DBUS_SIGNAL_FLAG_DEPRECATED },
+ { "Disconnected", "", G_DBUS_SIGNAL_FLAG_DEPRECATED },
+ { "Playing", "", G_DBUS_SIGNAL_FLAG_DEPRECATED },
+ { "Stopped", "", G_DBUS_SIGNAL_FLAG_DEPRECATED },
+ { "PropertyChanged", "sv" },
+ { NULL, NULL }
+};
+
+static void sink_free(struct audio_device *dev)
+{
+ struct sink *sink = dev->sink;
+
+ if (sink->cb_id)
+ avdtp_stream_remove_cb(sink->session, sink->stream,
+ sink->cb_id);
+
+ if (sink->session)
+ avdtp_unref(sink->session);
+
+ if (sink->connect)
+ pending_request_free(dev, sink->connect);
+
+ if (sink->disconnect)
+ pending_request_free(dev, sink->disconnect);
+
+ if (sink->retry_id)
+ g_source_remove(sink->retry_id);
+
+ g_free(sink);
+ dev->sink = NULL;
+}
+
+static void path_unregister(void *data)
+{
+ struct audio_device *dev = data;
+
+ DBG("Unregistered interface %s on path %s",
+ AUDIO_SINK_INTERFACE, dev->path);
+
+ sink_free(dev);
+}
+
+void sink_unregister(struct audio_device *dev)
+{
+ g_dbus_unregister_interface(dev->conn, dev->path,
+ AUDIO_SINK_INTERFACE);
+}
+
+struct sink *sink_init(struct audio_device *dev)
+{
+ struct sink *sink;
+
+ if (!g_dbus_register_interface(dev->conn, dev->path,
+ AUDIO_SINK_INTERFACE,
+ sink_methods, sink_signals, NULL,
+ dev, path_unregister))
+ return NULL;
+
+ DBG("Registered interface %s on path %s",
+ AUDIO_SINK_INTERFACE, dev->path);
+
+ if (avdtp_callback_id == 0)
+ avdtp_callback_id = avdtp_add_state_cb(avdtp_state_callback,
+ NULL);
+
+ sink = g_new0(struct sink, 1);
+
+ sink->dev = dev;
+
+ return sink;
+}
+
+gboolean sink_is_active(struct audio_device *dev)
+{
+ struct sink *sink = dev->sink;
+
+ if (sink->session)
+ return TRUE;
+
+ return FALSE;
+}
+
+avdtp_state_t sink_get_state(struct audio_device *dev)
+{
+ struct sink *sink = dev->sink;
+
+ return sink->stream_state;
+}
+
+gboolean sink_new_stream(struct audio_device *dev, struct avdtp *session,
+ struct avdtp_stream *stream)
+{
+ struct sink *sink = dev->sink;
+
+ if (sink->stream)
+ return FALSE;
+
+ if (!sink->session)
+ sink->session = avdtp_ref(session);
+
+ sink->stream = stream;
+
+ sink->cb_id = avdtp_stream_add_cb(session, stream,
+ stream_state_changed, dev);
+
+ return TRUE;
+}
+
+gboolean sink_shutdown(struct sink *sink)
+{
+ if (!sink->session)
+ return FALSE;
+
+ avdtp_set_device_disconnect(sink->session, TRUE);
+
+ /* cancel pending connect */
+ if (sink->connect) {
+ struct pending_request *pending = sink->connect;
+
+ if (pending->msg)
+ error_failed(pending->conn, pending->msg,
+ "Stream setup failed");
+ pending_request_free(sink->dev, pending);
+ sink->connect = NULL;
+
+ avdtp_unref(sink->session);
+ sink->session = NULL;
+
+ return TRUE;
+ }
+
+ /* disconnect already ongoing */
+ if (sink->disconnect)
+ return TRUE;
+
+ if (!sink->stream)
+ return FALSE;
+
+ if (avdtp_close(sink->session, sink->stream, FALSE) < 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+unsigned int sink_add_state_cb(sink_state_cb cb, void *user_data)
+{
+ struct sink_state_callback *state_cb;
+ static unsigned int id = 0;
+
+ state_cb = g_new(struct sink_state_callback, 1);
+ state_cb->cb = cb;
+ state_cb->user_data = user_data;
+ state_cb->id = ++id;
+
+ sink_callbacks = g_slist_append(sink_callbacks, state_cb);
+
+ return state_cb->id;
+}
+
+gboolean sink_remove_state_cb(unsigned int id)
+{
+ GSList *l;
+
+ for (l = sink_callbacks; l != NULL; l = l->next) {
+ struct sink_state_callback *cb = l->data;
+ if (cb && cb->id == id) {
+ sink_callbacks = g_slist_remove(sink_callbacks, cb);
+ g_free(cb);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
diff --git a/audio/sink.h b/audio/sink.h
new file mode 100644
index 0000000..7b1902b
--- /dev/null
+++ b/audio/sink.h
@@ -0,0 +1,49 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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
+ *
+ */
+
+#define AUDIO_SINK_INTERFACE "org.bluez.AudioSink"
+
+typedef enum {
+ SINK_STATE_DISCONNECTED,
+ SINK_STATE_CONNECTING,
+ SINK_STATE_CONNECTED,
+ SINK_STATE_PLAYING,
+} sink_state_t;
+
+typedef void (*sink_state_cb) (struct audio_device *dev,
+ sink_state_t old_state,
+ sink_state_t new_state,
+ void *user_data);
+
+unsigned int sink_add_state_cb(sink_state_cb cb, void *user_data);
+gboolean sink_remove_state_cb(unsigned int id);
+
+struct sink *sink_init(struct audio_device *dev);
+void sink_unregister(struct audio_device *dev);
+gboolean sink_is_active(struct audio_device *dev);
+avdtp_state_t sink_get_state(struct audio_device *dev);
+gboolean sink_new_stream(struct audio_device *dev, struct avdtp *session,
+ struct avdtp_stream *stream);
+gboolean sink_setup_stream(struct sink *sink, struct avdtp *session);
+gboolean sink_shutdown(struct sink *sink);
diff --git a/audio/source.c b/audio/source.c
new file mode 100644
index 0000000..6d266f2
--- /dev/null
+++ b/audio/source.c
@@ -0,0 +1,633 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2009 Joao Paulo Rechi Vita
+ *
+ *
+ * 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 <stdint.h>
+#include <errno.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+
+#include "device.h"
+#include "avdtp.h"
+#include "media.h"
+#include "a2dp.h"
+#include "error.h"
+#include "source.h"
+#include "dbus-common.h"
+#include "../src/adapter.h"
+#include "../src/device.h"
+
+#define STREAM_SETUP_RETRY_TIMER 2
+
+struct pending_request {
+ DBusConnection *conn;
+ DBusMessage *msg;
+ unsigned int id;
+};
+
+struct source {
+ struct audio_device *dev;
+ struct avdtp *session;
+ struct avdtp_stream *stream;
+ unsigned int cb_id;
+ guint retry_id;
+ avdtp_session_state_t session_state;
+ avdtp_state_t stream_state;
+ source_state_t state;
+ struct pending_request *connect;
+ struct pending_request *disconnect;
+ DBusConnection *conn;
+};
+
+struct source_state_callback {
+ source_state_cb cb;
+ void *user_data;
+ unsigned int id;
+};
+
+static GSList *source_callbacks = NULL;
+
+static unsigned int avdtp_callback_id = 0;
+
+static const char *state2str(source_state_t state)
+{
+ switch (state) {
+ case SOURCE_STATE_DISCONNECTED:
+ return "disconnected";
+ case SOURCE_STATE_CONNECTING:
+ return "connecting";
+ case SOURCE_STATE_CONNECTED:
+ return "connected";
+ case SOURCE_STATE_PLAYING:
+ return "playing";
+ default:
+ error("Invalid source state %d", state);
+ return NULL;
+ }
+}
+
+static void source_set_state(struct audio_device *dev, source_state_t new_state)
+{
+ struct source *source = dev->source;
+ const char *state_str;
+ source_state_t old_state = source->state;
+ GSList *l;
+
+ source->state = new_state;
+
+ state_str = state2str(new_state);
+ if (state_str)
+ emit_property_changed(dev->conn, dev->path,
+ AUDIO_SOURCE_INTERFACE, "State",
+ DBUS_TYPE_STRING, &state_str);
+
+ for (l = source_callbacks; l != NULL; l = l->next) {
+ struct source_state_callback *cb = l->data;
+ cb->cb(dev, old_state, new_state, cb->user_data);
+ }
+}
+
+static void avdtp_state_callback(struct audio_device *dev,
+ struct avdtp *session,
+ avdtp_session_state_t old_state,
+ avdtp_session_state_t new_state,
+ void *user_data)
+{
+ struct source *source = dev->source;
+
+ if (source == NULL)
+ return;
+
+ switch (new_state) {
+ case AVDTP_SESSION_STATE_DISCONNECTED:
+ source_set_state(dev, SOURCE_STATE_DISCONNECTED);
+ break;
+ case AVDTP_SESSION_STATE_CONNECTING:
+ source_set_state(dev, SOURCE_STATE_CONNECTING);
+ break;
+ case AVDTP_SESSION_STATE_CONNECTED:
+ break;
+ }
+
+ source->session_state = new_state;
+}
+
+static void pending_request_free(struct audio_device *dev,
+ struct pending_request *pending)
+{
+ if (pending->conn)
+ dbus_connection_unref(pending->conn);
+ if (pending->msg)
+ dbus_message_unref(pending->msg);
+ if (pending->id)
+ a2dp_cancel(dev, pending->id);
+
+ g_free(pending);
+}
+
+static void stream_state_changed(struct avdtp_stream *stream,
+ avdtp_state_t old_state,
+ avdtp_state_t new_state,
+ struct avdtp_error *err,
+ void *user_data)
+{
+ struct audio_device *dev = user_data;
+ struct source *source = dev->source;
+
+ if (err)
+ return;
+
+ switch (new_state) {
+ case AVDTP_STATE_IDLE:
+ if (source->disconnect) {
+ DBusMessage *reply;
+ struct pending_request *p;
+
+ p = source->disconnect;
+ source->disconnect = NULL;
+
+ reply = dbus_message_new_method_return(p->msg);
+ g_dbus_send_message(p->conn, reply);
+ pending_request_free(dev, p);
+ }
+
+ if (source->session) {
+ avdtp_unref(source->session);
+ source->session = NULL;
+ }
+ source->stream = NULL;
+ source->cb_id = 0;
+ break;
+ case AVDTP_STATE_OPEN:
+ source_set_state(dev, SOURCE_STATE_CONNECTED);
+ break;
+ case AVDTP_STATE_STREAMING:
+ source_set_state(dev, SOURCE_STATE_PLAYING);
+ break;
+ case AVDTP_STATE_CONFIGURED:
+ case AVDTP_STATE_CLOSING:
+ case AVDTP_STATE_ABORTING:
+ default:
+ break;
+ }
+
+ source->stream_state = new_state;
+}
+
+static void error_failed(DBusConnection *conn, DBusMessage *msg,
+ const char *desc)
+{
+ DBusMessage *reply = btd_error_failed(msg, desc);
+ g_dbus_send_message(conn, reply);
+}
+
+static gboolean stream_setup_retry(gpointer user_data)
+{
+ struct source *source = user_data;
+ struct pending_request *pending = source->connect;
+
+ source->retry_id = 0;
+
+ if (source->stream_state >= AVDTP_STATE_OPEN) {
+ DBG("Stream successfully created, after XCASE connect:connect");
+ if (pending->msg) {
+ DBusMessage *reply;
+ reply = dbus_message_new_method_return(pending->msg);
+ g_dbus_send_message(pending->conn, reply);
+ }
+ } else {
+ DBG("Stream setup failed, after XCASE connect:connect");
+ if (pending->msg)
+ error_failed(pending->conn, pending->msg, "Stream setup failed");
+ }
+
+ source->connect = NULL;
+ pending_request_free(source->dev, pending);
+
+ return FALSE;
+}
+
+static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep,
+ struct avdtp_stream *stream,
+ struct avdtp_error *err, void *user_data)
+{
+ struct source *source = user_data;
+ struct pending_request *pending;
+
+ pending = source->connect;
+
+ pending->id = 0;
+
+ if (stream) {
+ DBG("Stream successfully created");
+
+ if (pending->msg) {
+ DBusMessage *reply;
+ reply = dbus_message_new_method_return(pending->msg);
+ g_dbus_send_message(pending->conn, reply);
+ }
+
+ source->connect = NULL;
+ pending_request_free(source->dev, pending);
+
+ return;
+ }
+
+ avdtp_unref(source->session);
+ source->session = NULL;
+ if (avdtp_error_category(err) == AVDTP_ERRNO
+ && avdtp_error_posix_errno(err) != EHOSTDOWN) {
+ DBG("connect:connect XCASE detected");
+ source->retry_id = g_timeout_add_seconds(STREAM_SETUP_RETRY_TIMER,
+ stream_setup_retry,
+ source);
+ } else {
+ if (pending->msg)
+ error_failed(pending->conn, pending->msg, "Stream setup failed");
+ source->connect = NULL;
+ pending_request_free(source->dev, pending);
+ DBG("Stream setup failed : %s", avdtp_strerror(err));
+ }
+}
+
+static void select_complete(struct avdtp *session, struct a2dp_sep *sep,
+ GSList *caps, void *user_data)
+{
+ struct source *source = user_data;
+ struct pending_request *pending;
+ int id;
+
+ pending = source->connect;
+
+ pending->id = 0;
+
+ if (caps == NULL)
+ goto failed;
+
+ id = a2dp_config(session, sep, stream_setup_complete, caps, source);
+ if (id == 0)
+ goto failed;
+
+ pending->id = id;
+ return;
+
+failed:
+ if (pending->msg)
+ error_failed(pending->conn, pending->msg, "Stream setup failed");
+ pending_request_free(source->dev, pending);
+ source->connect = NULL;
+ avdtp_unref(source->session);
+ source->session = NULL;
+}
+
+static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp_error *err,
+ void *user_data)
+{
+ struct source *source = user_data;
+ struct pending_request *pending;
+ int id;
+
+ pending = source->connect;
+
+ if (err) {
+ avdtp_unref(source->session);
+ source->session = NULL;
+ if (avdtp_error_category(err) == AVDTP_ERRNO
+ && avdtp_error_posix_errno(err) != EHOSTDOWN) {
+ DBG("connect:connect XCASE detected");
+ source->retry_id =
+ g_timeout_add_seconds(STREAM_SETUP_RETRY_TIMER,
+ stream_setup_retry,
+ source);
+ } else
+ goto failed;
+ return;
+ }
+
+ DBG("Discovery complete");
+
+ id = a2dp_select_capabilities(source->session, AVDTP_SEP_TYPE_SOURCE, NULL,
+ select_complete, source);
+ if (id == 0)
+ goto failed;
+
+ pending->id = id;
+ return;
+
+failed:
+ if (pending->msg)
+ error_failed(pending->conn, pending->msg, "Stream setup failed");
+ pending_request_free(source->dev, pending);
+ source->connect = NULL;
+ avdtp_unref(source->session);
+ source->session = NULL;
+}
+
+gboolean source_setup_stream(struct source *source, struct avdtp *session)
+{
+ if (source->connect || source->disconnect)
+ return FALSE;
+
+ if (session && !source->session)
+ source->session = avdtp_ref(session);
+
+ if (!source->session)
+ return FALSE;
+
+ avdtp_set_auto_disconnect(source->session, FALSE);
+
+ if (avdtp_discover(source->session, discovery_complete, source) < 0)
+ return FALSE;
+
+ source->connect = g_new0(struct pending_request, 1);
+
+ return TRUE;
+}
+
+static DBusMessage *source_connect(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct audio_device *dev = data;
+ struct source *source = dev->source;
+ struct pending_request *pending;
+
+ if (!source->session)
+ source->session = avdtp_get(&dev->src, &dev->dst);
+
+ if (!source->session)
+ return btd_error_failed(msg, "Unable to get a session");
+
+ if (source->connect || source->disconnect)
+ return btd_error_busy(msg);
+
+ if (source->stream_state >= AVDTP_STATE_OPEN)
+ return btd_error_already_connected(msg);
+
+ if (!source_setup_stream(source, NULL))
+ return btd_error_failed(msg, "Failed to create a stream");
+
+ dev->auto_connect = FALSE;
+
+ pending = source->connect;
+
+ pending->conn = dbus_connection_ref(conn);
+ pending->msg = dbus_message_ref(msg);
+
+ DBG("stream creation in progress");
+
+ return NULL;
+}
+
+static DBusMessage *source_disconnect(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct audio_device *device = data;
+ struct source *source = device->source;
+ struct pending_request *pending;
+ int err;
+
+ if (!source->session)
+ return btd_error_not_connected(msg);
+
+ if (source->connect || source->disconnect)
+ return btd_error_busy(msg);
+
+ if (source->stream_state < AVDTP_STATE_OPEN) {
+ DBusMessage *reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+ avdtp_unref(source->session);
+ source->session = NULL;
+ return reply;
+ }
+
+ err = avdtp_close(source->session, source->stream, FALSE);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+ pending = g_new0(struct pending_request, 1);
+ pending->conn = dbus_connection_ref(conn);
+ pending->msg = dbus_message_ref(msg);
+ source->disconnect = pending;
+
+ return NULL;
+}
+
+static DBusMessage *source_get_properties(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct audio_device *device = data;
+ struct source *source = device->source;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ const char *state;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ /* State */
+ state = state2str(source->state);
+ if (state)
+ dict_append_entry(&dict, "State", DBUS_TYPE_STRING, &state);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static GDBusMethodTable source_methods[] = {
+ { "Connect", "", "", source_connect,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "Disconnect", "", "", source_disconnect,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "GetProperties", "", "a{sv}",source_get_properties },
+ { NULL, NULL, NULL, NULL }
+};
+
+static GDBusSignalTable source_signals[] = {
+ { "PropertyChanged", "sv" },
+ { NULL, NULL }
+};
+
+static void source_free(struct audio_device *dev)
+{
+ struct source *source = dev->source;
+
+ if (source->cb_id)
+ avdtp_stream_remove_cb(source->session, source->stream,
+ source->cb_id);
+
+ if (source->session)
+ avdtp_unref(source->session);
+
+ if (source->connect)
+ pending_request_free(dev, source->connect);
+
+ if (source->disconnect)
+ pending_request_free(dev, source->disconnect);
+
+ if (source->retry_id)
+ g_source_remove(source->retry_id);
+
+ g_free(source);
+ dev->source = NULL;
+}
+
+static void path_unregister(void *data)
+{
+ struct audio_device *dev = data;
+
+ DBG("Unregistered interface %s on path %s",
+ AUDIO_SOURCE_INTERFACE, dev->path);
+
+ source_free(dev);
+}
+
+void source_unregister(struct audio_device *dev)
+{
+ g_dbus_unregister_interface(dev->conn, dev->path,
+ AUDIO_SOURCE_INTERFACE);
+}
+
+struct source *source_init(struct audio_device *dev)
+{
+ struct source *source;
+
+ if (!g_dbus_register_interface(dev->conn, dev->path,
+ AUDIO_SOURCE_INTERFACE,
+ source_methods, source_signals, NULL,
+ dev, path_unregister))
+ return NULL;
+
+ DBG("Registered interface %s on path %s",
+ AUDIO_SOURCE_INTERFACE, dev->path);
+
+ if (avdtp_callback_id == 0)
+ avdtp_callback_id = avdtp_add_state_cb(avdtp_state_callback,
+ NULL);
+
+ source = g_new0(struct source, 1);
+
+ source->dev = dev;
+
+ return source;
+}
+
+gboolean source_is_active(struct audio_device *dev)
+{
+ struct source *source = dev->source;
+
+ if (source->session)
+ return TRUE;
+
+ return FALSE;
+}
+
+avdtp_state_t source_get_state(struct audio_device *dev)
+{
+ struct source *source = dev->source;
+
+ return source->stream_state;
+}
+
+gboolean source_new_stream(struct audio_device *dev, struct avdtp *session,
+ struct avdtp_stream *stream)
+{
+ struct source *source = dev->source;
+
+ if (source->stream)
+ return FALSE;
+
+ if (!source->session)
+ source->session = avdtp_ref(session);
+
+ source->stream = stream;
+
+ source->cb_id = avdtp_stream_add_cb(session, stream,
+ stream_state_changed, dev);
+
+ return TRUE;
+}
+
+gboolean source_shutdown(struct source *source)
+{
+ if (!source->stream)
+ return FALSE;
+
+ if (avdtp_close(source->session, source->stream, FALSE) < 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+unsigned int source_add_state_cb(source_state_cb cb, void *user_data)
+{
+ struct source_state_callback *state_cb;
+ static unsigned int id = 0;
+
+ state_cb = g_new(struct source_state_callback, 1);
+ state_cb->cb = cb;
+ state_cb->user_data = user_data;
+ state_cb->id = ++id;
+
+ source_callbacks = g_slist_append(source_callbacks, state_cb);
+
+ return state_cb->id;
+}
+
+gboolean source_remove_state_cb(unsigned int id)
+{
+ GSList *l;
+
+ for (l = source_callbacks; l != NULL; l = l->next) {
+ struct source_state_callback *cb = l->data;
+ if (cb && cb->id == id) {
+ source_callbacks = g_slist_remove(source_callbacks, cb);
+ g_free(cb);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
diff --git a/audio/source.h b/audio/source.h
new file mode 100644
index 0000000..7837284
--- /dev/null
+++ b/audio/source.h
@@ -0,0 +1,50 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2009 Joao Paulo Rechi Vita
+ *
+ *
+ * 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
+ *
+ */
+
+#define AUDIO_SOURCE_INTERFACE "org.bluez.AudioSource"
+
+typedef enum {
+ SOURCE_STATE_DISCONNECTED,
+ SOURCE_STATE_CONNECTING,
+ SOURCE_STATE_CONNECTED,
+ SOURCE_STATE_PLAYING,
+} source_state_t;
+
+typedef void (*source_state_cb) (struct audio_device *dev,
+ source_state_t old_state,
+ source_state_t new_state,
+ void *user_data);
+
+unsigned int source_add_state_cb(source_state_cb cb, void *user_data);
+gboolean source_remove_state_cb(unsigned int id);
+
+struct source *source_init(struct audio_device *dev);
+void source_unregister(struct audio_device *dev);
+gboolean source_is_active(struct audio_device *dev);
+avdtp_state_t source_get_state(struct audio_device *dev);
+gboolean source_new_stream(struct audio_device *dev, struct avdtp *session,
+ struct avdtp_stream *stream);
+gboolean source_setup_stream(struct source *source, struct avdtp *session);
+gboolean source_shutdown(struct source *source);
diff --git a/audio/telephony-dummy.c b/audio/telephony-dummy.c
new file mode 100644
index 0000000..1f89079
--- /dev/null
+++ b/audio/telephony-dummy.c
@@ -0,0 +1,433 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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 <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+#include "telephony.h"
+#include "error.h"
+
+#define TELEPHONY_DUMMY_IFACE "org.bluez.TelephonyTest"
+#define TELEPHONY_DUMMY_PATH "/org/bluez/test"
+
+static DBusConnection *connection = NULL;
+
+static const char *chld_str = "0,1,1x,2,2x,3,4";
+static char *subscriber_number = NULL;
+static char *active_call_number = NULL;
+static int active_call_status = 0;
+static int active_call_dir = 0;
+
+static gboolean events_enabled = FALSE;
+
+static struct indicator dummy_indicators[] =
+{
+ { "battchg", "0-5", 5, TRUE },
+ { "signal", "0-5", 5, TRUE },
+ { "service", "0,1", 1, TRUE },
+ { "call", "0,1", 0, TRUE },
+ { "callsetup", "0-3", 0, TRUE },
+ { "callheld", "0-2", 0, FALSE },
+ { "roam", "0,1", 0, TRUE },
+ { NULL }
+};
+
+void telephony_device_connected(void *telephony_device)
+{
+ DBG("telephony-dummy: device %p connected", telephony_device);
+}
+
+void telephony_device_disconnected(void *telephony_device)
+{
+ DBG("telephony-dummy: device %p disconnected", telephony_device);
+ events_enabled = FALSE;
+}
+
+void telephony_event_reporting_req(void *telephony_device, int ind)
+{
+ events_enabled = ind == 1 ? TRUE : FALSE;
+
+ telephony_event_reporting_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_response_and_hold_req(void *telephony_device, int rh)
+{
+ telephony_response_and_hold_rsp(telephony_device,
+ CME_ERROR_NOT_SUPPORTED);
+}
+
+void telephony_last_dialed_number_req(void *telephony_device)
+{
+ telephony_last_dialed_number_rsp(telephony_device, CME_ERROR_NONE);
+
+ /* Notify outgoing call set-up successfully initiated */
+ telephony_update_indicator(dummy_indicators, "callsetup",
+ EV_CALLSETUP_OUTGOING);
+ telephony_update_indicator(dummy_indicators, "callsetup",
+ EV_CALLSETUP_ALERTING);
+
+ active_call_status = CALL_STATUS_ALERTING;
+ active_call_dir = CALL_DIR_OUTGOING;
+}
+
+void telephony_terminate_call_req(void *telephony_device)
+{
+ g_free(active_call_number);
+ active_call_number = NULL;
+
+ telephony_terminate_call_rsp(telephony_device, CME_ERROR_NONE);
+
+ if (telephony_get_indicator(dummy_indicators, "callsetup") > 0)
+ telephony_update_indicator(dummy_indicators, "callsetup",
+ EV_CALLSETUP_INACTIVE);
+ else
+ telephony_update_indicator(dummy_indicators, "call",
+ EV_CALL_INACTIVE);
+}
+
+void telephony_answer_call_req(void *telephony_device)
+{
+ telephony_answer_call_rsp(telephony_device, CME_ERROR_NONE);
+
+ telephony_update_indicator(dummy_indicators, "call", EV_CALL_ACTIVE);
+ telephony_update_indicator(dummy_indicators, "callsetup",
+ EV_CALLSETUP_INACTIVE);
+
+ active_call_status = CALL_STATUS_ACTIVE;
+}
+
+void telephony_dial_number_req(void *telephony_device, const char *number)
+{
+ g_free(active_call_number);
+ active_call_number = g_strdup(number);
+
+ DBG("telephony-dummy: dial request to %s", active_call_number);
+
+ telephony_dial_number_rsp(telephony_device, CME_ERROR_NONE);
+
+ /* Notify outgoing call set-up successfully initiated */
+ telephony_update_indicator(dummy_indicators, "callsetup",
+ EV_CALLSETUP_OUTGOING);
+ telephony_update_indicator(dummy_indicators, "callsetup",
+ EV_CALLSETUP_ALERTING);
+
+ active_call_status = CALL_STATUS_ALERTING;
+ active_call_dir = CALL_DIR_OUTGOING;
+}
+
+void telephony_transmit_dtmf_req(void *telephony_device, char tone)
+{
+ DBG("telephony-dummy: transmit dtmf: %c", tone);
+ telephony_transmit_dtmf_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_subscriber_number_req(void *telephony_device)
+{
+ DBG("telephony-dummy: subscriber number request");
+ if (subscriber_number)
+ telephony_subscriber_number_ind(subscriber_number,
+ NUMBER_TYPE_TELEPHONY,
+ SUBSCRIBER_SERVICE_VOICE);
+ telephony_subscriber_number_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_list_current_calls_req(void *telephony_device)
+{
+ DBG("telephony-dummy: list current calls request");
+ if (active_call_number)
+ telephony_list_current_call_ind(1, active_call_dir,
+ active_call_status,
+ CALL_MODE_VOICE,
+ CALL_MULTIPARTY_NO,
+ active_call_number,
+ NUMBER_TYPE_TELEPHONY);
+ telephony_list_current_calls_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_operator_selection_req(void *telephony_device)
+{
+ telephony_operator_selection_ind(OPERATOR_MODE_AUTO, "DummyOperator");
+ telephony_operator_selection_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_call_hold_req(void *telephony_device, const char *cmd)
+{
+ DBG("telephony-dymmy: got call hold request %s", cmd);
+ telephony_call_hold_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_nr_and_ec_req(void *telephony_device, gboolean enable)
+{
+ DBG("telephony-dummy: got %s NR and EC request",
+ enable ? "enable" : "disable");
+
+ telephony_nr_and_ec_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_voice_dial_req(void *telephony_device, gboolean enable)
+{
+ DBG("telephony-dummy: got %s voice dial request",
+ enable ? "enable" : "disable");
+
+ g_dbus_emit_signal(connection, TELEPHONY_DUMMY_PATH,
+ TELEPHONY_DUMMY_IFACE, "VoiceDial",
+ DBUS_TYPE_INVALID);
+
+ telephony_voice_dial_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_key_press_req(void *telephony_device, const char *keys)
+{
+ DBG("telephony-dummy: got key press request for %s", keys);
+ telephony_key_press_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+/* D-Bus method handlers */
+static DBusMessage *outgoing_call(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ const char *number;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ DBG("telephony-dummy: outgoing call to %s", number);
+
+ g_free(active_call_number);
+ active_call_number = g_strdup(number);
+
+ telephony_update_indicator(dummy_indicators, "callsetup",
+ EV_CALLSETUP_OUTGOING);
+ telephony_update_indicator(dummy_indicators, "callsetup",
+ EV_CALLSETUP_ALERTING);
+
+ active_call_status = CALL_STATUS_ALERTING;
+ active_call_dir = CALL_DIR_OUTGOING;
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *incoming_call(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ const char *number;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ DBG("telephony-dummy: incoming call to %s", number);
+
+ g_free(active_call_number);
+ active_call_number = g_strdup(number);
+
+ telephony_update_indicator(dummy_indicators, "callsetup",
+ EV_CALLSETUP_INCOMING);
+
+ active_call_status = CALL_STATUS_INCOMING;
+ active_call_dir = CALL_DIR_INCOMING;
+
+ telephony_incoming_call_ind(number, NUMBER_TYPE_TELEPHONY);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *cancel_call(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ DBG("telephony-dummy: cancel call");
+
+ g_free(active_call_number);
+ active_call_number = NULL;
+
+ if (telephony_get_indicator(dummy_indicators, "callsetup") > 0) {
+ telephony_update_indicator(dummy_indicators, "callsetup",
+ EV_CALLSETUP_INACTIVE);
+ telephony_calling_stopped_ind();
+ }
+
+ if (telephony_get_indicator(dummy_indicators, "call") > 0)
+ telephony_update_indicator(dummy_indicators, "call",
+ EV_CALL_INACTIVE);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *signal_strength(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ dbus_uint32_t strength;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &strength,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ if (strength > 5)
+ return btd_error_invalid_args(msg);
+
+ telephony_update_indicator(dummy_indicators, "signal", strength);
+
+ DBG("telephony-dummy: signal strength set to %u", strength);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *battery_level(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ dbus_uint32_t level;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &level,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ if (level > 5)
+ return btd_error_invalid_args(msg);
+
+ telephony_update_indicator(dummy_indicators, "battchg", level);
+
+ DBG("telephony-dummy: battery level set to %u", level);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *roaming_status(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ dbus_bool_t roaming;
+ int val;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_BOOLEAN, &roaming,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ val = roaming ? EV_ROAM_ACTIVE : EV_ROAM_INACTIVE;
+
+ telephony_update_indicator(dummy_indicators, "roam", val);
+
+ DBG("telephony-dummy: roaming status set to %d", val);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *registration_status(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ dbus_bool_t registration;
+ int val;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_BOOLEAN, &registration,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ val = registration ? EV_SERVICE_PRESENT : EV_SERVICE_NONE;
+
+ telephony_update_indicator(dummy_indicators, "service", val);
+
+ DBG("telephony-dummy: registration status set to %d", val);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *set_subscriber_number(DBusConnection *conn,
+ DBusMessage *msg,
+ void *data)
+{
+ const char *number;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ g_free(subscriber_number);
+ subscriber_number = g_strdup(number);
+
+ DBG("telephony-dummy: subscriber number set to %s", number);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static GDBusMethodTable dummy_methods[] = {
+ { "OutgoingCall", "s", "", outgoing_call },
+ { "IncomingCall", "s", "", incoming_call },
+ { "CancelCall", "", "", cancel_call },
+ { "SignalStrength", "u", "", signal_strength },
+ { "BatteryLevel", "u", "", battery_level },
+ { "RoamingStatus", "b", "", roaming_status },
+ { "RegistrationStatus", "b", "", registration_status },
+ { "SetSubscriberNumber","s", "", set_subscriber_number },
+ { }
+};
+
+static GDBusSignalTable dummy_signals[] = {
+ { "VoiceDial", "" },
+ { }
+};
+
+int telephony_init(void)
+{
+ uint32_t features = AG_FEATURE_REJECT_A_CALL |
+ AG_FEATURE_ENHANCED_CALL_STATUS |
+ AG_FEATURE_EXTENDED_ERROR_RESULT_CODES;
+
+ DBG("");
+
+ connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+
+ if (g_dbus_register_interface(connection, TELEPHONY_DUMMY_PATH,
+ TELEPHONY_DUMMY_IFACE,
+ dummy_methods, dummy_signals,
+ NULL, NULL, NULL) == FALSE) {
+ error("telephony-dummy interface %s init failed on path %s",
+ TELEPHONY_DUMMY_IFACE, TELEPHONY_DUMMY_PATH);
+ return -1;
+ }
+
+ telephony_ready_ind(features, dummy_indicators, BTRH_NOT_SUPPORTED,
+ chld_str);
+
+ return 0;
+}
+
+void telephony_exit(void)
+{
+ DBG("");
+
+ g_dbus_unregister_interface(connection, TELEPHONY_DUMMY_PATH,
+ TELEPHONY_DUMMY_IFACE);
+ dbus_connection_unref(connection);
+ connection = NULL;
+
+ telephony_deinit();
+}
diff --git a/audio/telephony-maemo5.c b/audio/telephony-maemo5.c
new file mode 100644
index 0000000..350df9e
--- /dev/null
+++ b/audio/telephony-maemo5.c
@@ -0,0 +1,2104 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2008-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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 <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <string.h>
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+#include "telephony.h"
+#include "error.h"
+
+/* SSC D-Bus definitions */
+#define SSC_DBUS_NAME "com.nokia.phone.SSC"
+#define SSC_DBUS_IFACE "com.nokia.phone.SSC"
+#define SSC_DBUS_PATH "/com/nokia/phone/SSC"
+
+/* libcsnet D-Bus definitions */
+#define NETWORK_BUS_NAME "com.nokia.phone.net"
+#define NETWORK_INTERFACE "Phone.Net"
+#define NETWORK_PATH "/com/nokia/phone/net"
+
+/* Mask bits for supported services */
+#define NETWORK_MASK_GPRS_SUPPORT 0x01
+#define NETWORK_MASK_CS_SERVICES 0x02
+#define NETWORK_MASK_EGPRS_SUPPORT 0x04
+#define NETWORK_MASK_HSDPA_AVAIL 0x08
+#define NETWORK_MASK_HSUPA_AVAIL 0x10
+
+/* network get cell info: cell type */
+#define NETWORK_UNKNOWN_CELL 0
+#define NETWORK_GSM_CELL 1
+#define NETWORK_WCDMA_CELL 2
+
+enum net_registration_status {
+ NETWORK_REG_STATUS_HOME = 0x00,
+ NETWORK_REG_STATUS_ROAM,
+ NETWORK_REG_STATUS_ROAM_BLINK,
+ NETWORK_REG_STATUS_NOSERV,
+ NETWORK_REG_STATUS_NOSERV_SEARCHING,
+ NETWORK_REG_STATUS_NOSERV_NOTSEARCHING,
+ NETWORK_REG_STATUS_NOSERV_NOSIM,
+ NETWORK_REG_STATUS_POWER_OFF = 0x08,
+ NETWORK_REG_STATUS_NSPS,
+ NETWORK_REG_STATUS_NSPS_NO_COVERAGE,
+ NETWORK_REG_STATUS_NOSERV_SIM_REJECTED_BY_NW
+};
+
+enum network_types {
+ NETWORK_GSM_HOME_PLMN = 0,
+ NETWORK_GSM_PREFERRED_PLMN,
+ NETWORK_GSM_FORBIDDEN_PLMN,
+ NETWORK_GSM_OTHER_PLMN,
+ NETWORK_GSM_NO_PLMN_AVAIL
+};
+
+enum network_alpha_tag_name_type {
+ NETWORK_HARDCODED_LATIN_OPER_NAME = 0,
+ NETWORK_HARDCODED_USC2_OPER_NAME,
+ NETWORK_NITZ_SHORT_OPER_NAME,
+ NETWORK_NITZ_FULL_OPER_NAME,
+};
+
+#define TELEPHONY_MAEMO_PATH "/com/nokia/MaemoTelephony"
+#define TELEPHONY_MAEMO_INTERFACE "com.nokia.MaemoTelephony"
+
+#define CALLERID_BASE "/var/lib/bluetooth/maemo-callerid-"
+#define ALLOWED_FLAG_FILE "/var/lib/bluetooth/maemo-callerid-allowed"
+#define RESTRICTED_FLAG_FILE "/var/lib/bluetooth/maemo-callerid-restricted"
+#define NONE_FLAG_FILE "/var/lib/bluetooth/maemo-callerid-none"
+
+static uint32_t callerid = 0;
+
+/* CSD CALL plugin D-Bus definitions */
+#define CSD_CALL_BUS_NAME "com.nokia.csd.Call"
+#define CSD_CALL_INTERFACE "com.nokia.csd.Call"
+#define CSD_CALL_INSTANCE "com.nokia.csd.Call.Instance"
+#define CSD_CALL_CONFERENCE "com.nokia.csd.Call.Conference"
+#define CSD_CALL_PATH "/com/nokia/csd/call"
+#define CSD_CALL_CONFERENCE_PATH "/com/nokia/csd/call/conference"
+
+/* Call status values as exported by the CSD CALL plugin */
+#define CSD_CALL_STATUS_IDLE 0
+#define CSD_CALL_STATUS_CREATE 1
+#define CSD_CALL_STATUS_COMING 2
+#define CSD_CALL_STATUS_PROCEEDING 3
+#define CSD_CALL_STATUS_MO_ALERTING 4
+#define CSD_CALL_STATUS_MT_ALERTING 5
+#define CSD_CALL_STATUS_WAITING 6
+#define CSD_CALL_STATUS_ANSWERED 7
+#define CSD_CALL_STATUS_ACTIVE 8
+#define CSD_CALL_STATUS_MO_RELEASE 9
+#define CSD_CALL_STATUS_MT_RELEASE 10
+#define CSD_CALL_STATUS_HOLD_INITIATED 11
+#define CSD_CALL_STATUS_HOLD 12
+#define CSD_CALL_STATUS_RETRIEVE_INITIATED 13
+#define CSD_CALL_STATUS_RECONNECT_PENDING 14
+#define CSD_CALL_STATUS_TERMINATED 15
+#define CSD_CALL_STATUS_SWAP_INITIATED 16
+
+#define CALL_FLAG_NONE 0
+#define CALL_FLAG_PRESENTATION_ALLOWED 0x01
+#define CALL_FLAG_PRESENTATION_RESTRICTED 0x02
+
+/* SIM Phonebook D-Bus definitions */
+#define SIM_PHONEBOOK_BUS_NAME "com.nokia.phone.SIM"
+#define SIM_PHONEBOOK_INTERFACE "Phone.Sim.Phonebook"
+#define SIM_PHONEBOOK_PATH "/com/nokia/phone/SIM/phonebook"
+
+#define PHONEBOOK_INDEX_FIRST_ENTRY 0xFFFF
+#define PHONEBOOK_INDEX_NEXT_FREE_LOCATION 0xFFFE
+
+enum sim_phonebook_type {
+ SIM_PHONEBOOK_TYPE_ADN = 0x0,
+ SIM_PHONEBOOK_TYPE_SDN,
+ SIM_PHONEBOOK_TYPE_FDN,
+ SIM_PHONEBOOK_TYPE_VMBX,
+ SIM_PHONEBOOK_TYPE_MBDN,
+ SIM_PHONEBOOK_TYPE_EN,
+ SIM_PHONEBOOK_TYPE_MSISDN
+};
+
+enum sim_phonebook_location_type {
+ SIM_PHONEBOOK_LOCATION_EXACT = 0x0,
+ SIM_PHONEBOOK_LOCATION_NEXT
+};
+
+struct csd_call {
+ char *object_path;
+ int status;
+ gboolean originating;
+ gboolean emergency;
+ gboolean on_hold;
+ gboolean conference;
+ char *number;
+ gboolean setup;
+};
+
+static struct {
+ uint8_t status;
+ uint16_t lac;
+ uint32_t cell_id;
+ uint32_t operator_code;
+ uint32_t country_code;
+ uint8_t network_type;
+ uint8_t supported_services;
+ uint16_t signals_bar;
+ char *operator_name;
+} net = {
+ .status = NETWORK_REG_STATUS_NOSERV,
+ .lac = 0,
+ .cell_id = 0,
+ .operator_code = 0,
+ .country_code = 0,
+ .network_type = NETWORK_GSM_NO_PLMN_AVAIL,
+ .supported_services = 0,
+ .signals_bar = 0,
+ .operator_name = NULL,
+};
+
+static DBusConnection *connection = NULL;
+
+static GSList *calls = NULL;
+
+/* Reference count for determining the call indicator status */
+static GSList *active_calls = NULL;
+
+static char *msisdn = NULL; /* Subscriber number */
+static char *vmbx = NULL; /* Voice mailbox number */
+
+/* HAL battery namespace key values */
+static int battchg_cur = -1; /* "battery.charge_level.current" */
+static int battchg_last = -1; /* "battery.charge_level.last_full" */
+static int battchg_design = -1; /* "battery.charge_level.design" */
+
+static gboolean get_calls_active = FALSE;
+
+static gboolean events_enabled = FALSE;
+
+/* Supported set of call hold operations */
+static const char *chld_str = "0,1,1x,2,2x,3,4";
+
+static char *last_dialed_number = NULL;
+
+/* Timer for tracking call creation requests */
+static guint create_request_timer = 0;
+
+static struct indicator maemo_indicators[] =
+{
+ { "battchg", "0-5", 5, TRUE },
+ { "signal", "0-5", 0, TRUE },
+ { "service", "0,1", 0, TRUE },
+ { "call", "0,1", 0, TRUE },
+ { "callsetup", "0-3", 0, TRUE },
+ { "callheld", "0-2", 0, FALSE },
+ { "roam", "0,1", 0, TRUE },
+ { NULL }
+};
+
+static char *call_status_str[] = {
+ "IDLE",
+ "CREATE",
+ "COMING",
+ "PROCEEDING",
+ "MO_ALERTING",
+ "MT_ALERTING",
+ "WAITING",
+ "ANSWERED",
+ "ACTIVE",
+ "MO_RELEASE",
+ "MT_RELEASE",
+ "HOLD_INITIATED",
+ "HOLD",
+ "RETRIEVE_INITIATED",
+ "RECONNECT_PENDING",
+ "TERMINATED",
+ "SWAP_INITIATED",
+ "???"
+};
+
+static struct csd_call *find_call(const char *path)
+{
+ GSList *l;
+
+ for (l = calls; l != NULL; l = l->next) {
+ struct csd_call *call = l->data;
+
+ if (g_str_equal(call->object_path, path))
+ return call;
+ }
+
+ return NULL;
+}
+
+static struct csd_call *find_non_held_call(void)
+{
+ GSList *l;
+
+ for (l = calls; l != NULL; l = l->next) {
+ struct csd_call *call = l->data;
+
+ if (call->status == CSD_CALL_STATUS_IDLE)
+ continue;
+
+ if (call->status != CSD_CALL_STATUS_HOLD)
+ return call;
+ }
+
+ return NULL;
+}
+
+static struct csd_call *find_non_idle_call(void)
+{
+ GSList *l;
+
+ for (l = calls; l != NULL; l = l->next) {
+ struct csd_call *call = l->data;
+
+ if (call->status != CSD_CALL_STATUS_IDLE)
+ return call;
+ }
+
+ return NULL;
+}
+
+static struct csd_call *find_call_with_status(int status)
+{
+ GSList *l;
+
+ for (l = calls; l != NULL; l = l->next) {
+ struct csd_call *call = l->data;
+
+ if (call->status == status)
+ return call;
+ }
+
+ return NULL;
+}
+
+static int release_conference(void)
+{
+ DBusMessage *msg;
+
+ DBG("telephony-maemo: releasing conference call");
+
+ msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
+ CSD_CALL_CONFERENCE_PATH,
+ CSD_CALL_INSTANCE,
+ "Release");
+ if (!msg) {
+ error("Unable to allocate new D-Bus message");
+ return -ENOMEM;
+ }
+
+ g_dbus_send_message(connection, msg);
+
+ return 0;
+}
+
+static int release_call(struct csd_call *call)
+{
+ DBusMessage *msg;
+
+ msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
+ call->object_path,
+ CSD_CALL_INSTANCE,
+ "Release");
+ if (!msg) {
+ error("Unable to allocate new D-Bus message");
+ return -ENOMEM;
+ }
+
+ g_dbus_send_message(connection, msg);
+
+ return 0;
+}
+
+static int answer_call(struct csd_call *call)
+{
+ DBusMessage *msg;
+
+ msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
+ call->object_path,
+ CSD_CALL_INSTANCE,
+ "Answer");
+ if (!msg) {
+ error("Unable to allocate new D-Bus message");
+ return -ENOMEM;
+ }
+
+ g_dbus_send_message(connection, msg);
+
+ return 0;
+}
+
+static int split_call(struct csd_call *call)
+{
+ DBusMessage *msg;
+
+ msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
+ call->object_path,
+ CSD_CALL_INSTANCE,
+ "Split");
+ if (!msg) {
+ error("Unable to allocate new D-Bus message");
+ return -ENOMEM;
+ }
+
+ g_dbus_send_message(connection, msg);
+
+ return 0;
+}
+
+static int unhold_call(struct csd_call *call)
+{
+ DBusMessage *msg;
+
+ msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+ CSD_CALL_INTERFACE,
+ "Unhold");
+ if (!msg) {
+ error("Unable to allocate new D-Bus message");
+ return -ENOMEM;
+ }
+
+ g_dbus_send_message(connection, msg);
+
+ return 0;
+}
+
+static int hold_call(struct csd_call *call)
+{
+ DBusMessage *msg;
+
+ msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+ CSD_CALL_INTERFACE,
+ "Hold");
+ if (!msg) {
+ error("Unable to allocate new D-Bus message");
+ return -ENOMEM;
+ }
+
+ g_dbus_send_message(connection, msg);
+
+ return 0;
+}
+
+static int swap_calls(void)
+{
+ DBusMessage *msg;
+
+ msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+ CSD_CALL_INTERFACE,
+ "Swap");
+ if (!msg) {
+ error("Unable to allocate new D-Bus message");
+ return -ENOMEM;
+ }
+
+ g_dbus_send_message(connection, msg);
+
+ return 0;
+}
+
+static int create_conference(void)
+{
+ DBusMessage *msg;
+
+ msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+ CSD_CALL_INTERFACE,
+ "Conference");
+ if (!msg) {
+ error("Unable to allocate new D-Bus message");
+ return -ENOMEM;
+ }
+
+ g_dbus_send_message(connection, msg);
+
+ return 0;
+}
+
+static int call_transfer(void)
+{
+ DBusMessage *msg;
+
+ msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+ CSD_CALL_INTERFACE,
+ "Transfer");
+ if (!msg) {
+ error("Unable to allocate new D-Bus message");
+ return -ENOMEM;
+ }
+
+ g_dbus_send_message(connection, msg);
+
+ return 0;
+}
+
+static int number_type(const char *number)
+{
+ if (number == NULL)
+ return NUMBER_TYPE_TELEPHONY;
+
+ if (number[0] == '+' || strncmp(number, "00", 2) == 0)
+ return NUMBER_TYPE_INTERNATIONAL;
+
+ return NUMBER_TYPE_TELEPHONY;
+}
+
+void telephony_device_connected(void *telephony_device)
+{
+ struct csd_call *coming;
+
+ DBG("telephony-maemo: device %p connected", telephony_device);
+
+ coming = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING);
+ if (coming) {
+ if (find_call_with_status(CSD_CALL_STATUS_ACTIVE))
+ telephony_call_waiting_ind(coming->number,
+ number_type(coming->number));
+ else
+ telephony_incoming_call_ind(coming->number,
+ number_type(coming->number));
+ }
+}
+
+void telephony_device_disconnected(void *telephony_device)
+{
+ DBG("telephony-maemo: device %p disconnected", telephony_device);
+ events_enabled = FALSE;
+}
+
+void telephony_event_reporting_req(void *telephony_device, int ind)
+{
+ events_enabled = ind == 1 ? TRUE : FALSE;
+
+ telephony_event_reporting_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_response_and_hold_req(void *telephony_device, int rh)
+{
+ telephony_response_and_hold_rsp(telephony_device,
+ CME_ERROR_NOT_SUPPORTED);
+}
+
+void telephony_last_dialed_number_req(void *telephony_device)
+{
+ DBG("telephony-maemo: last dialed number request");
+
+ if (last_dialed_number)
+ telephony_dial_number_req(telephony_device,
+ last_dialed_number);
+ else
+ telephony_last_dialed_number_rsp(telephony_device,
+ CME_ERROR_NOT_ALLOWED);
+}
+
+void telephony_terminate_call_req(void *telephony_device)
+{
+ struct csd_call *call;
+ int err;
+
+ call = find_call_with_status(CSD_CALL_STATUS_ACTIVE);
+ if (!call)
+ call = find_non_idle_call();
+
+ if (!call) {
+ error("No active call");
+ telephony_terminate_call_rsp(telephony_device,
+ CME_ERROR_NOT_ALLOWED);
+ return;
+ }
+
+ if (call->conference)
+ err = release_conference();
+ else
+ err = release_call(call);
+
+ if (err < 0)
+ telephony_terminate_call_rsp(telephony_device,
+ CME_ERROR_AG_FAILURE);
+ else
+ telephony_terminate_call_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_answer_call_req(void *telephony_device)
+{
+ struct csd_call *call;
+
+ call = find_call_with_status(CSD_CALL_STATUS_COMING);
+ if (!call)
+ call = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING);
+
+ if (!call)
+ call = find_call_with_status(CSD_CALL_STATUS_PROCEEDING);
+
+ if (!call)
+ call = find_call_with_status(CSD_CALL_STATUS_WAITING);
+
+ if (!call) {
+ telephony_answer_call_rsp(telephony_device,
+ CME_ERROR_NOT_ALLOWED);
+ return;
+ }
+
+ if (answer_call(call) < 0)
+ telephony_answer_call_rsp(telephony_device,
+ CME_ERROR_AG_FAILURE);
+ else
+ telephony_answer_call_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+static int send_method_call(const char *dest, const char *path,
+ const char *interface, const char *method,
+ DBusPendingCallNotifyFunction cb,
+ void *user_data, int type, ...)
+{
+ DBusMessage *msg;
+ DBusPendingCall *call;
+ va_list args;
+
+ msg = dbus_message_new_method_call(dest, path, interface, method);
+ if (!msg) {
+ error("Unable to allocate new D-Bus %s message", method);
+ return -ENOMEM;
+ }
+
+ va_start(args, type);
+
+ if (!dbus_message_append_args_valist(msg, type, args)) {
+ dbus_message_unref(msg);
+ va_end(args);
+ return -EIO;
+ }
+
+ va_end(args);
+
+ if (!cb) {
+ g_dbus_send_message(connection, msg);
+ return 0;
+ }
+
+ if (!dbus_connection_send_with_reply(connection, msg, &call, -1)) {
+ error("Sending %s failed", method);
+ dbus_message_unref(msg);
+ return -EIO;
+ }
+
+ dbus_pending_call_set_notify(call, cb, user_data, NULL);
+ dbus_pending_call_unref(call);
+ dbus_message_unref(msg);
+
+ return 0;
+}
+
+static const char *memory_dial_lookup(int location)
+{
+ if (location == 1)
+ return vmbx;
+ else
+ return NULL;
+}
+
+void telephony_dial_number_req(void *telephony_device, const char *number)
+{
+ uint32_t flags = callerid;
+ int ret;
+
+ DBG("telephony-maemo: dial request to %s", number);
+
+ if (strncmp(number, "*31#", 4) == 0) {
+ number += 4;
+ flags = CALL_FLAG_PRESENTATION_ALLOWED;
+ } else if (strncmp(number, "#31#", 4) == 0) {
+ number += 4;
+ flags = CALL_FLAG_PRESENTATION_RESTRICTED;
+ } else if (number[0] == '>') {
+ const char *location = &number[1];
+
+ number = memory_dial_lookup(strtol(&number[1], NULL, 0));
+ if (!number) {
+ error("No number at memory location %s", location);
+ telephony_dial_number_rsp(telephony_device,
+ CME_ERROR_INVALID_INDEX);
+ return;
+ }
+ }
+
+ ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+ CSD_CALL_INTERFACE, "CreateWith",
+ NULL, NULL,
+ DBUS_TYPE_STRING, &number,
+ DBUS_TYPE_UINT32, &flags,
+ DBUS_TYPE_INVALID);
+ if (ret < 0) {
+ telephony_dial_number_rsp(telephony_device,
+ CME_ERROR_AG_FAILURE);
+ return;
+ }
+
+ telephony_dial_number_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_transmit_dtmf_req(void *telephony_device, char tone)
+{
+ int ret;
+ char buf[2] = { tone, '\0' }, *buf_ptr = buf;
+
+ DBG("telephony-maemo: transmit dtmf: %s", buf);
+
+ ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+ CSD_CALL_INTERFACE, "SendDTMF",
+ NULL, NULL,
+ DBUS_TYPE_STRING, &buf_ptr,
+ DBUS_TYPE_INVALID);
+ if (ret < 0) {
+ telephony_transmit_dtmf_rsp(telephony_device,
+ CME_ERROR_AG_FAILURE);
+ return;
+ }
+
+ telephony_transmit_dtmf_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_subscriber_number_req(void *telephony_device)
+{
+ DBG("telephony-maemo: subscriber number request");
+ if (msisdn)
+ telephony_subscriber_number_ind(msisdn,
+ number_type(msisdn),
+ SUBSCRIBER_SERVICE_VOICE);
+ telephony_subscriber_number_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+static int csd_status_to_hfp(struct csd_call *call)
+{
+ switch (call->status) {
+ case CSD_CALL_STATUS_IDLE:
+ case CSD_CALL_STATUS_MO_RELEASE:
+ case CSD_CALL_STATUS_MT_RELEASE:
+ case CSD_CALL_STATUS_TERMINATED:
+ return -1;
+ case CSD_CALL_STATUS_CREATE:
+ return CALL_STATUS_DIALING;
+ case CSD_CALL_STATUS_WAITING:
+ return CALL_STATUS_WAITING;
+ case CSD_CALL_STATUS_PROCEEDING:
+ /* PROCEEDING can happen in outgoing/incoming */
+ if (call->originating)
+ return CALL_STATUS_DIALING;
+ else
+ return CALL_STATUS_INCOMING;
+ case CSD_CALL_STATUS_COMING:
+ return CALL_STATUS_INCOMING;
+ case CSD_CALL_STATUS_MO_ALERTING:
+ return CALL_STATUS_ALERTING;
+ case CSD_CALL_STATUS_MT_ALERTING:
+ return CALL_STATUS_INCOMING;
+ case CSD_CALL_STATUS_ANSWERED:
+ case CSD_CALL_STATUS_ACTIVE:
+ case CSD_CALL_STATUS_RECONNECT_PENDING:
+ case CSD_CALL_STATUS_SWAP_INITIATED:
+ case CSD_CALL_STATUS_HOLD_INITIATED:
+ return CALL_STATUS_ACTIVE;
+ case CSD_CALL_STATUS_RETRIEVE_INITIATED:
+ case CSD_CALL_STATUS_HOLD:
+ return CALL_STATUS_HELD;
+ default:
+ return -1;
+ }
+}
+
+void telephony_list_current_calls_req(void *telephony_device)
+{
+ GSList *l;
+ int i;
+
+ DBG("telephony-maemo: list current calls request");
+
+ for (l = calls, i = 1; l != NULL; l = l->next, i++) {
+ struct csd_call *call = l->data;
+ int status, direction, multiparty;
+
+ status = csd_status_to_hfp(call);
+ if (status < 0)
+ continue;
+
+ direction = call->originating ?
+ CALL_DIR_OUTGOING : CALL_DIR_INCOMING;
+
+ multiparty = call->conference ?
+ CALL_MULTIPARTY_YES : CALL_MULTIPARTY_NO;
+
+ telephony_list_current_call_ind(i, direction, status,
+ CALL_MODE_VOICE, multiparty,
+ call->number,
+ number_type(call->number));
+ }
+
+ telephony_list_current_calls_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_operator_selection_req(void *telephony_device)
+{
+ telephony_operator_selection_ind(OPERATOR_MODE_AUTO,
+ net.operator_name ? net.operator_name : "");
+ telephony_operator_selection_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+static void foreach_call_with_status(int status,
+ int (*func)(struct csd_call *call))
+{
+ GSList *l;
+
+ for (l = calls; l != NULL; l = l->next) {
+ struct csd_call *call = l->data;
+
+ if (call->status == status)
+ func(call);
+ }
+}
+
+void telephony_call_hold_req(void *telephony_device, const char *cmd)
+{
+ const char *idx;
+ struct csd_call *call;
+ int err = 0;
+
+ DBG("telephony-maemo: got call hold request %s", cmd);
+
+ if (strlen(cmd) > 1)
+ idx = &cmd[1];
+ else
+ idx = NULL;
+
+ if (idx)
+ call = g_slist_nth_data(calls, strtol(idx, NULL, 0) - 1);
+ else
+ call = NULL;
+
+ switch (cmd[0]) {
+ case '0':
+ foreach_call_with_status(CSD_CALL_STATUS_HOLD, release_call);
+ foreach_call_with_status(CSD_CALL_STATUS_WAITING,
+ release_call);
+ break;
+ case '1':
+ if (idx) {
+ if (call)
+ err = release_call(call);
+ break;
+ }
+ foreach_call_with_status(CSD_CALL_STATUS_ACTIVE, release_call);
+ call = find_call_with_status(CSD_CALL_STATUS_WAITING);
+ if (call)
+ err = answer_call(call);
+ break;
+ case '2':
+ if (idx) {
+ if (call)
+ err = split_call(call);
+ } else {
+ struct csd_call *held, *wait;
+
+ call = find_call_with_status(CSD_CALL_STATUS_ACTIVE);
+ held = find_call_with_status(CSD_CALL_STATUS_HOLD);
+ wait = find_call_with_status(CSD_CALL_STATUS_WAITING);
+
+ if (wait)
+ err = answer_call(wait);
+ else if (call && held)
+ err = swap_calls();
+ else {
+ if (call)
+ err = hold_call(call);
+ if (held)
+ err = unhold_call(held);
+ }
+ }
+ break;
+ case '3':
+ if (find_call_with_status(CSD_CALL_STATUS_HOLD) ||
+ find_call_with_status(CSD_CALL_STATUS_WAITING))
+ err = create_conference();
+ break;
+ case '4':
+ err = call_transfer();
+ break;
+ default:
+ DBG("Unknown call hold request");
+ break;
+ }
+
+ if (err)
+ telephony_call_hold_rsp(telephony_device,
+ CME_ERROR_AG_FAILURE);
+ else
+ telephony_call_hold_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_nr_and_ec_req(void *telephony_device, gboolean enable)
+{
+ DBG("telephony-maemo: got %s NR and EC request",
+ enable ? "enable" : "disable");
+ telephony_nr_and_ec_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_key_press_req(void *telephony_device, const char *keys)
+{
+ struct csd_call *active, *waiting;
+ int err;
+
+ DBG("telephony-maemo: got key press request for %s", keys);
+
+ waiting = find_call_with_status(CSD_CALL_STATUS_COMING);
+ if (!waiting)
+ waiting = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING);
+ if (!waiting)
+ waiting = find_call_with_status(CSD_CALL_STATUS_PROCEEDING);
+
+ active = find_call_with_status(CSD_CALL_STATUS_ACTIVE);
+
+ if (waiting)
+ err = answer_call(waiting);
+ else if (active)
+ err = release_call(active);
+ else
+ err = 0;
+
+ if (err < 0)
+ telephony_key_press_rsp(telephony_device,
+ CME_ERROR_AG_FAILURE);
+ else
+ telephony_key_press_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_voice_dial_req(void *telephony_device, gboolean enable)
+{
+ DBG("telephony-maemo: got %s voice dial request",
+ enable ? "enable" : "disable");
+
+ telephony_voice_dial_rsp(telephony_device, CME_ERROR_NOT_SUPPORTED);
+}
+
+static void handle_incoming_call(DBusMessage *msg)
+{
+ const char *number, *call_path;
+ struct csd_call *call;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_OBJECT_PATH, &call_path,
+ DBUS_TYPE_STRING, &number,
+ DBUS_TYPE_INVALID)) {
+ error("Unexpected parameters in Call.Coming() signal");
+ return;
+ }
+
+ call = find_call(call_path);
+ if (!call) {
+ error("Didn't find any matching call object for %s",
+ call_path);
+ return;
+ }
+
+ DBG("Incoming call to %s from number %s", call_path, number);
+
+ g_free(call->number);
+ call->number = g_strdup(number);
+
+ telephony_update_indicator(maemo_indicators, "callsetup",
+ EV_CALLSETUP_INCOMING);
+
+ if (find_call_with_status(CSD_CALL_STATUS_ACTIVE))
+ telephony_call_waiting_ind(call->number,
+ number_type(call->number));
+ else
+ telephony_incoming_call_ind(call->number,
+ number_type(call->number));
+}
+
+static void handle_outgoing_call(DBusMessage *msg)
+{
+ const char *number, *call_path;
+ struct csd_call *call;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_OBJECT_PATH, &call_path,
+ DBUS_TYPE_STRING, &number,
+ DBUS_TYPE_INVALID)) {
+ error("Unexpected parameters in Call.Created() signal");
+ return;
+ }
+
+ call = find_call(call_path);
+ if (!call) {
+ error("Didn't find any matching call object for %s",
+ call_path);
+ return;
+ }
+
+ DBG("Outgoing call from %s to number %s", call_path, number);
+
+ g_free(call->number);
+ call->number = g_strdup(number);
+
+ g_free(last_dialed_number);
+ last_dialed_number = g_strdup(number);
+
+ if (create_request_timer) {
+ g_source_remove(create_request_timer);
+ create_request_timer = 0;
+ }
+}
+
+static gboolean create_timeout(gpointer user_data)
+{
+ telephony_update_indicator(maemo_indicators, "callsetup",
+ EV_CALLSETUP_INACTIVE);
+ create_request_timer = 0;
+ return FALSE;
+}
+
+static void handle_create_requested(DBusMessage *msg)
+{
+ DBG("Call.CreateRequested()");
+
+ if (create_request_timer)
+ g_source_remove(create_request_timer);
+
+ create_request_timer = g_timeout_add_seconds(5, create_timeout, NULL);
+
+ telephony_update_indicator(maemo_indicators, "callsetup",
+ EV_CALLSETUP_OUTGOING);
+}
+
+static void handle_call_status(DBusMessage *msg, const char *call_path)
+{
+ struct csd_call *call;
+ dbus_uint32_t status, cause_type, cause;
+ int callheld = telephony_get_indicator(maemo_indicators, "callheld");
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_UINT32, &status,
+ DBUS_TYPE_UINT32, &cause_type,
+ DBUS_TYPE_UINT32, &cause,
+ DBUS_TYPE_INVALID)) {
+ error("Unexpected paramters in Instance.CallStatus() signal");
+ return;
+ }
+
+ call = find_call(call_path);
+ if (!call) {
+ error("Didn't find any matching call object for %s",
+ call_path);
+ return;
+ }
+
+ if (status > 16) {
+ error("Invalid call status %u", status);
+ return;
+ }
+
+ DBG("Call %s changed from %s to %s", call_path,
+ call_status_str[call->status], call_status_str[status]);
+
+ if (call->status == (int) status) {
+ DBG("Ignoring CSD Call state change to existing state");
+ return;
+ }
+
+ call->status = (int) status;
+
+ switch (status) {
+ case CSD_CALL_STATUS_IDLE:
+ if (call->setup) {
+ telephony_update_indicator(maemo_indicators,
+ "callsetup",
+ EV_CALLSETUP_INACTIVE);
+ if (!call->originating)
+ telephony_calling_stopped_ind();
+ }
+
+ g_free(call->number);
+ call->number = NULL;
+ call->originating = FALSE;
+ call->emergency = FALSE;
+ call->on_hold = FALSE;
+ call->conference = FALSE;
+ call->setup = FALSE;
+ break;
+ case CSD_CALL_STATUS_CREATE:
+ call->originating = TRUE;
+ call->setup = TRUE;
+ break;
+ case CSD_CALL_STATUS_COMING:
+ call->originating = FALSE;
+ call->setup = TRUE;
+ break;
+ case CSD_CALL_STATUS_PROCEEDING:
+ break;
+ case CSD_CALL_STATUS_MO_ALERTING:
+ telephony_update_indicator(maemo_indicators, "callsetup",
+ EV_CALLSETUP_ALERTING);
+ break;
+ case CSD_CALL_STATUS_MT_ALERTING:
+ break;
+ case CSD_CALL_STATUS_WAITING:
+ break;
+ case CSD_CALL_STATUS_ANSWERED:
+ break;
+ case CSD_CALL_STATUS_ACTIVE:
+ if (call->on_hold) {
+ call->on_hold = FALSE;
+ if (find_call_with_status(CSD_CALL_STATUS_HOLD))
+ telephony_update_indicator(maemo_indicators,
+ "callheld",
+ EV_CALLHELD_MULTIPLE);
+ else
+ telephony_update_indicator(maemo_indicators,
+ "callheld",
+ EV_CALLHELD_NONE);
+ } else {
+ if (!g_slist_find(active_calls, call))
+ active_calls = g_slist_prepend(active_calls, call);
+ if (g_slist_length(active_calls) == 1)
+ telephony_update_indicator(maemo_indicators,
+ "call",
+ EV_CALL_ACTIVE);
+ /* Upgrade callheld status if necessary */
+ if (callheld == EV_CALLHELD_ON_HOLD)
+ telephony_update_indicator(maemo_indicators,
+ "callheld",
+ EV_CALLHELD_MULTIPLE);
+ telephony_update_indicator(maemo_indicators,
+ "callsetup",
+ EV_CALLSETUP_INACTIVE);
+ if (!call->originating)
+ telephony_calling_stopped_ind();
+ call->setup = FALSE;
+ }
+ break;
+ case CSD_CALL_STATUS_MO_RELEASE:
+ case CSD_CALL_STATUS_MT_RELEASE:
+ active_calls = g_slist_remove(active_calls, call);
+ if (g_slist_length(active_calls) == 0)
+ telephony_update_indicator(maemo_indicators, "call",
+ EV_CALL_INACTIVE);
+ break;
+ case CSD_CALL_STATUS_HOLD_INITIATED:
+ break;
+ case CSD_CALL_STATUS_HOLD:
+ call->on_hold = TRUE;
+ if (find_non_held_call())
+ telephony_update_indicator(maemo_indicators,
+ "callheld",
+ EV_CALLHELD_MULTIPLE);
+ else
+ telephony_update_indicator(maemo_indicators,
+ "callheld",
+ EV_CALLHELD_ON_HOLD);
+ break;
+ case CSD_CALL_STATUS_RETRIEVE_INITIATED:
+ break;
+ case CSD_CALL_STATUS_RECONNECT_PENDING:
+ break;
+ case CSD_CALL_STATUS_TERMINATED:
+ if (call->on_hold &&
+ !find_call_with_status(CSD_CALL_STATUS_HOLD))
+ telephony_update_indicator(maemo_indicators,
+ "callheld",
+ EV_CALLHELD_NONE);
+ else if (callheld == EV_CALLHELD_MULTIPLE &&
+ find_call_with_status(CSD_CALL_STATUS_HOLD))
+ telephony_update_indicator(maemo_indicators,
+ "callheld",
+ EV_CALLHELD_ON_HOLD);
+ break;
+ case CSD_CALL_STATUS_SWAP_INITIATED:
+ break;
+ default:
+ error("Unknown call status %u", status);
+ break;
+ }
+}
+
+static void handle_conference(DBusMessage *msg, gboolean joined)
+{
+ const char *path;
+ struct csd_call *call;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID)) {
+ error("Unexpected parameters in Conference.%s",
+ dbus_message_get_member(msg));
+ return;
+ }
+
+ call = find_call(path);
+ if (!call) {
+ error("Conference signal for unknown call %s", path);
+ return;
+ }
+
+ DBG("Call %s %s the conference", path, joined ? "joined" : "left");
+
+ call->conference = joined;
+}
+
+static void get_operator_name_reply(DBusPendingCall *pending_call,
+ void *user_data)
+{
+ DBusMessage *reply;
+ DBusError err;
+ const char *name;
+ dbus_int32_t net_err;
+
+ reply = dbus_pending_call_steal_reply(pending_call);
+
+ dbus_error_init(&err);
+ if (dbus_set_error_from_message(&err, reply)) {
+ error("get_operator_name failed: %s, %s",
+ err.name, err.message);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ dbus_error_init(&err);
+ if (!dbus_message_get_args(reply, &err,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INT32, &net_err,
+ DBUS_TYPE_INVALID)) {
+ error("Unexpected get_operator_name reply parameters: %s, %s",
+ err.name, err.message);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ if (net_err != 0) {
+ error("get_operator_name failed with code %d", net_err);
+ goto done;
+ }
+
+ if (strlen(name) == 0)
+ goto done;
+
+ g_free(net.operator_name);
+ net.operator_name = g_strdup(name);
+
+ DBG("telephony-maemo: operator name updated: %s", name);
+
+done:
+ dbus_message_unref(reply);
+}
+
+static void resolve_operator_name(uint32_t operator, uint32_t country)
+{
+ uint8_t name_type = NETWORK_HARDCODED_LATIN_OPER_NAME;
+
+ send_method_call(NETWORK_BUS_NAME, NETWORK_PATH,
+ NETWORK_INTERFACE, "get_operator_name",
+ get_operator_name_reply, NULL,
+ DBUS_TYPE_BYTE, &name_type,
+ DBUS_TYPE_UINT32, &operator,
+ DBUS_TYPE_UINT32, &country,
+ DBUS_TYPE_INVALID);
+}
+
+static void update_registration_status(uint8_t status, uint16_t lac,
+ uint32_t cell_id,
+ uint32_t operator_code,
+ uint32_t country_code,
+ uint8_t network_type,
+ uint8_t supported_services)
+{
+ if (net.status != status) {
+ switch (status) {
+ case NETWORK_REG_STATUS_HOME:
+ telephony_update_indicator(maemo_indicators, "roam",
+ EV_ROAM_INACTIVE);
+ if (net.status >= NETWORK_REG_STATUS_NOSERV)
+ telephony_update_indicator(maemo_indicators,
+ "service",
+ EV_SERVICE_PRESENT);
+ break;
+ case NETWORK_REG_STATUS_ROAM:
+ case NETWORK_REG_STATUS_ROAM_BLINK:
+ telephony_update_indicator(maemo_indicators, "roam",
+ EV_ROAM_ACTIVE);
+ if (net.status >= NETWORK_REG_STATUS_NOSERV)
+ telephony_update_indicator(maemo_indicators,
+ "service",
+ EV_SERVICE_PRESENT);
+ break;
+ case NETWORK_REG_STATUS_NOSERV:
+ case NETWORK_REG_STATUS_NOSERV_SEARCHING:
+ case NETWORK_REG_STATUS_NOSERV_NOTSEARCHING:
+ case NETWORK_REG_STATUS_NOSERV_NOSIM:
+ case NETWORK_REG_STATUS_POWER_OFF:
+ case NETWORK_REG_STATUS_NSPS:
+ case NETWORK_REG_STATUS_NSPS_NO_COVERAGE:
+ case NETWORK_REG_STATUS_NOSERV_SIM_REJECTED_BY_NW:
+ if (net.status < NETWORK_REG_STATUS_NOSERV)
+ telephony_update_indicator(maemo_indicators,
+ "service",
+ EV_SERVICE_NONE);
+ break;
+ }
+
+ net.status = status;
+ }
+
+ net.lac = lac;
+ net.cell_id = cell_id;
+
+ if (net.operator_code != operator_code ||
+ net.country_code != country_code) {
+ g_free(net.operator_name);
+ net.operator_name = NULL;
+ resolve_operator_name(operator_code, country_code);
+ net.operator_code = operator_code;
+ net.country_code = country_code;
+ }
+
+ net.network_type = network_type;
+ net.supported_services = supported_services;
+}
+
+static void handle_registration_status_change(DBusMessage *msg)
+{
+ uint8_t status;
+ dbus_uint16_t lac, network_type, supported_services;
+ dbus_uint32_t cell_id, operator_code, country_code;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_BYTE, &status,
+ DBUS_TYPE_UINT16, &lac,
+ DBUS_TYPE_UINT32, &cell_id,
+ DBUS_TYPE_UINT32, &operator_code,
+ DBUS_TYPE_UINT32, &country_code,
+ DBUS_TYPE_BYTE, &network_type,
+ DBUS_TYPE_BYTE, &supported_services,
+ DBUS_TYPE_INVALID)) {
+ error("Unexpected parameters in registration_status_change");
+ return;
+ }
+
+ update_registration_status(status, lac, cell_id, operator_code,
+ country_code, network_type,
+ supported_services);
+}
+
+static void update_signal_strength(uint8_t signals_bar)
+{
+ int signal;
+
+ if (signals_bar > 100) {
+ DBG("signals_bar greater than expected: %u", signals_bar);
+ signals_bar = 100;
+ }
+
+ if (net.signals_bar == signals_bar)
+ return;
+
+ /* A simple conversion from 0-100 to 0-5 (used by HFP) */
+ signal = (signals_bar + 20) / 21;
+
+ telephony_update_indicator(maemo_indicators, "signal", signal);
+
+ net.signals_bar = signals_bar;
+
+ DBG("Signal strength updated: %u/100, %d/5", signals_bar, signal);
+}
+
+static void handle_signal_strength_change(DBusMessage *msg)
+{
+ uint8_t signals_bar, rssi_in_dbm;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_BYTE, &signals_bar,
+ DBUS_TYPE_BYTE, &rssi_in_dbm,
+ DBUS_TYPE_INVALID)) {
+ error("Unexpected parameters in signal_strength_change");
+ return;
+ }
+
+ update_signal_strength(signals_bar);
+}
+
+static gboolean iter_get_basic_args(DBusMessageIter *iter,
+ int first_arg_type, ...)
+{
+ int type;
+ va_list ap;
+
+ va_start(ap, first_arg_type);
+
+ for (type = first_arg_type; type != DBUS_TYPE_INVALID;
+ type = va_arg(ap, int)) {
+ void *value = va_arg(ap, void *);
+ int real_type = dbus_message_iter_get_arg_type(iter);
+
+ if (real_type != type) {
+ error("iter_get_basic_args: expected %c but got %c",
+ (char) type, (char) real_type);
+ break;
+ }
+
+ dbus_message_iter_get_basic(iter, value);
+ dbus_message_iter_next(iter);
+ }
+
+ va_end(ap);
+
+ return type == DBUS_TYPE_INVALID ? TRUE : FALSE;
+}
+
+static void hal_battery_level_reply(DBusPendingCall *call, void *user_data)
+{
+ DBusError err;
+ DBusMessage *reply;
+ dbus_int32_t level;
+ int *value = user_data;
+
+ reply = dbus_pending_call_steal_reply(call);
+
+ dbus_error_init(&err);
+ if (dbus_set_error_from_message(&err, reply)) {
+ error("hald replied with an error: %s, %s",
+ err.name, err.message);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ dbus_error_init(&err);
+ if (dbus_message_get_args(reply, &err,
+ DBUS_TYPE_INT32, &level,
+ DBUS_TYPE_INVALID) == FALSE) {
+ error("Unable to parse GetPropertyInteger reply: %s, %s",
+ err.name, err.message);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ *value = (int) level;
+
+ if (value == &battchg_last)
+ DBG("telephony-maemo: battery.charge_level.last_full is %d",
+ *value);
+ else if (value == &battchg_design)
+ DBG("telephony-maemo: battery.charge_level.design is %d",
+ *value);
+ else
+ DBG("telephony-maemo: battery.charge_level.current is %d",
+ *value);
+
+ if ((battchg_design > 0 || battchg_last > 0) && battchg_cur >= 0) {
+ int new, max;
+
+ if (battchg_last > 0)
+ max = battchg_last;
+ else
+ max = battchg_design;
+
+ new = battchg_cur * 5 / max;
+
+ telephony_update_indicator(maemo_indicators, "battchg", new);
+ }
+done:
+ dbus_message_unref(reply);
+}
+
+static void hal_get_integer(const char *path, const char *key, void *user_data)
+{
+ send_method_call("org.freedesktop.Hal", path,
+ "org.freedesktop.Hal.Device",
+ "GetPropertyInteger",
+ hal_battery_level_reply, user_data,
+ DBUS_TYPE_STRING, &key,
+ DBUS_TYPE_INVALID);
+}
+
+static void handle_hal_property_modified(DBusMessage *msg)
+{
+ DBusMessageIter iter, array;
+ dbus_int32_t num_changes;
+ const char *path;
+
+ path = dbus_message_get_path(msg);
+
+ dbus_message_iter_init(msg, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INT32) {
+ error("Unexpected signature in hal PropertyModified signal");
+ return;
+ }
+
+ dbus_message_iter_get_basic(&iter, &num_changes);
+ dbus_message_iter_next(&iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+ error("Unexpected signature in hal PropertyModified signal");
+ return;
+ }
+
+ dbus_message_iter_recurse(&iter, &array);
+
+ while (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_INVALID) {
+ DBusMessageIter prop;
+ const char *name;
+ dbus_bool_t added, removed;
+
+ dbus_message_iter_recurse(&array, &prop);
+
+ if (!iter_get_basic_args(&prop,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_BOOLEAN, &added,
+ DBUS_TYPE_BOOLEAN, &removed,
+ DBUS_TYPE_INVALID)) {
+ error("Invalid hal PropertyModified parameters");
+ break;
+ }
+
+ if (g_str_equal(name, "battery.charge_level.last_full"))
+ hal_get_integer(path, name, &battchg_last);
+ else if (g_str_equal(name, "battery.charge_level.current"))
+ hal_get_integer(path, name, &battchg_cur);
+ else if (g_str_equal(name, "battery.charge_level.design"))
+ hal_get_integer(path, name, &battchg_design);
+
+ dbus_message_iter_next(&array);
+ }
+}
+
+static void csd_call_free(struct csd_call *call)
+{
+ if (!call)
+ return;
+
+ g_free(call->object_path);
+ g_free(call->number);
+
+ g_free(call);
+}
+
+static void parse_call_list(DBusMessageIter *iter)
+{
+ do {
+ DBusMessageIter call_iter;
+ struct csd_call *call;
+ const char *object_path, *number;
+ dbus_uint32_t status;
+ dbus_bool_t originating, terminating, emerg, on_hold, conf;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRUCT) {
+ error("Unexpected signature in GetCallInfoAll reply");
+ break;
+ }
+
+ dbus_message_iter_recurse(iter, &call_iter);
+
+ if (!iter_get_basic_args(&call_iter,
+ DBUS_TYPE_OBJECT_PATH, &object_path,
+ DBUS_TYPE_UINT32, &status,
+ DBUS_TYPE_BOOLEAN, &originating,
+ DBUS_TYPE_BOOLEAN, &terminating,
+ DBUS_TYPE_BOOLEAN, &emerg,
+ DBUS_TYPE_BOOLEAN, &on_hold,
+ DBUS_TYPE_BOOLEAN, &conf,
+ DBUS_TYPE_STRING, &number,
+ DBUS_TYPE_INVALID)) {
+ error("Parsing call D-Bus parameters failed");
+ break;
+ }
+
+ call = find_call(object_path);
+ if (!call) {
+ call = g_new0(struct csd_call, 1);
+ call->object_path = g_strdup(object_path);
+ call->status = (int) status;
+ calls = g_slist_append(calls, call);
+ DBG("telephony-maemo: new csd call instance at %s",
+ object_path);
+ }
+
+ if (call->status == CSD_CALL_STATUS_IDLE)
+ continue;
+
+ /* CSD gives incorrect call_hold property sometimes */
+ if ((call->status != CSD_CALL_STATUS_HOLD && on_hold) ||
+ (call->status == CSD_CALL_STATUS_HOLD &&
+ !on_hold)) {
+ error("Conflicting call status and on_hold property!");
+ on_hold = call->status == CSD_CALL_STATUS_HOLD;
+ }
+
+ call->originating = originating;
+ call->on_hold = on_hold;
+ call->conference = conf;
+ g_free(call->number);
+ call->number = g_strdup(number);
+
+ } while (dbus_message_iter_next(iter));
+}
+
+static void signal_strength_reply(DBusPendingCall *call, void *user_data)
+{
+ DBusError err;
+ DBusMessage *reply;
+ uint8_t signals_bar, rssi_in_dbm;
+ dbus_int32_t net_err;
+
+ reply = dbus_pending_call_steal_reply(call);
+
+ dbus_error_init(&err);
+ if (dbus_set_error_from_message(&err, reply)) {
+ error("Unable to get signal strength: %s, %s",
+ err.name, err.message);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ dbus_error_init(&err);
+ if (!dbus_message_get_args(reply, &err,
+ DBUS_TYPE_BYTE, &signals_bar,
+ DBUS_TYPE_BYTE, &rssi_in_dbm,
+ DBUS_TYPE_INT32, &net_err,
+ DBUS_TYPE_INVALID)) {
+ error("Unable to parse signal_strength reply: %s, %s",
+ err.name, err.message);
+ dbus_error_free(&err);
+ return;
+ }
+
+ if (net_err != 0) {
+ error("get_signal_strength failed with code %d", net_err);
+ return;
+ }
+
+ update_signal_strength(signals_bar);
+
+done:
+ dbus_message_unref(reply);
+}
+
+static int get_signal_strength(void)
+{
+ return send_method_call(NETWORK_BUS_NAME, NETWORK_PATH,
+ NETWORK_INTERFACE, "get_signal_strength",
+ signal_strength_reply, NULL,
+ DBUS_TYPE_INVALID);
+}
+
+static void registration_status_reply(DBusPendingCall *call, void *user_data)
+{
+ DBusError err;
+ DBusMessage *reply;
+ uint8_t status;
+ dbus_uint16_t lac, network_type, supported_services;
+ dbus_uint32_t cell_id, operator_code, country_code;
+ dbus_int32_t net_err;
+
+ reply = dbus_pending_call_steal_reply(call);
+
+ dbus_error_init(&err);
+ if (dbus_set_error_from_message(&err, reply)) {
+ error("Unable to get registration status: %s, %s",
+ err.name, err.message);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ dbus_error_init(&err);
+ if (!dbus_message_get_args(reply, &err,
+ DBUS_TYPE_BYTE, &status,
+ DBUS_TYPE_UINT16, &lac,
+ DBUS_TYPE_UINT32, &cell_id,
+ DBUS_TYPE_UINT32, &operator_code,
+ DBUS_TYPE_UINT32, &country_code,
+ DBUS_TYPE_BYTE, &network_type,
+ DBUS_TYPE_BYTE, &supported_services,
+ DBUS_TYPE_INT32, &net_err,
+ DBUS_TYPE_INVALID)) {
+ error("Unable to parse registration_status_change reply:"
+ " %s, %s", err.name, err.message);
+ dbus_error_free(&err);
+ return;
+ }
+
+ if (net_err != 0) {
+ error("get_registration_status failed with code %d", net_err);
+ return;
+ }
+
+ update_registration_status(status, lac, cell_id, operator_code,
+ country_code, network_type,
+ supported_services);
+
+ get_signal_strength();
+
+done:
+ dbus_message_unref(reply);
+}
+
+static int get_registration_status(void)
+{
+ return send_method_call(NETWORK_BUS_NAME, NETWORK_PATH,
+ NETWORK_INTERFACE, "get_registration_status",
+ registration_status_reply, NULL,
+ DBUS_TYPE_INVALID);
+}
+
+static void call_info_reply(DBusPendingCall *call, void *user_data)
+{
+ DBusError err;
+ DBusMessage *reply;
+ DBusMessageIter iter, sub;;
+
+ get_calls_active = FALSE;
+
+ reply = dbus_pending_call_steal_reply(call);
+
+ dbus_error_init(&err);
+ if (dbus_set_error_from_message(&err, reply)) {
+ error("csd replied with an error: %s, %s",
+ err.name, err.message);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ dbus_message_iter_init(reply, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+ error("Unexpected signature in GetCallInfoAll return");
+ goto done;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ parse_call_list(&sub);
+
+ get_registration_status();
+
+done:
+ dbus_message_unref(reply);
+}
+
+static void hal_find_device_reply(DBusPendingCall *call, void *user_data)
+{
+ DBusError err;
+ DBusMessage *reply;
+ DBusMessageIter iter, sub;
+ const char *path;
+ char match_string[256];
+ int type;
+
+ reply = dbus_pending_call_steal_reply(call);
+
+ dbus_error_init(&err);
+ if (dbus_set_error_from_message(&err, reply)) {
+ error("hald replied with an error: %s, %s",
+ err.name, err.message);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ dbus_message_iter_init(reply, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+ error("Unexpected signature in FindDeviceByCapability return");
+ goto done;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ type = dbus_message_iter_get_arg_type(&sub);
+
+ if (type != DBUS_TYPE_OBJECT_PATH && type != DBUS_TYPE_STRING) {
+ error("No hal device with battery capability found");
+ goto done;
+ }
+
+ dbus_message_iter_get_basic(&sub, &path);
+
+ DBG("telephony-maemo: found battery device at %s", path);
+
+ snprintf(match_string, sizeof(match_string),
+ "type='signal',"
+ "path='%s',"
+ "interface='org.freedesktop.Hal.Device',"
+ "member='PropertyModified'", path);
+ dbus_bus_add_match(connection, match_string, NULL);
+
+ hal_get_integer(path, "battery.charge_level.last_full", &battchg_last);
+ hal_get_integer(path, "battery.charge_level.current", &battchg_cur);
+ hal_get_integer(path, "battery.charge_level.design", &battchg_design);
+
+done:
+ dbus_message_unref(reply);
+}
+
+static void phonebook_read_reply(DBusPendingCall *call, void *user_data)
+{
+ DBusError derr;
+ DBusMessage *reply;
+ const char *name, *number;
+ char **number_type = user_data;
+ dbus_int32_t current_location, err;
+
+ reply = dbus_pending_call_steal_reply(call);
+
+ dbus_error_init(&derr);
+ if (dbus_set_error_from_message(&derr, reply)) {
+ error("SIM.Phonebook replied with an error: %s, %s",
+ derr.name, derr.message);
+ dbus_error_free(&derr);
+ goto done;
+ }
+
+ dbus_error_init(&derr);
+ if (dbus_message_get_args(reply, &derr,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_STRING, &number,
+ DBUS_TYPE_INT32, &current_location,
+ DBUS_TYPE_INT32, &err,
+ DBUS_TYPE_INVALID) == FALSE) {
+ error("Unable to parse SIM.Phonebook.read arguments: %s, %s",
+ derr.name, derr.message);
+ dbus_error_free(&derr);
+ goto done;
+ }
+
+ if (err != 0) {
+ error("SIM.Phonebook.read failed with error %d", err);
+ if (number_type == &vmbx)
+ vmbx = g_strdup(getenv("VMBX_NUMBER"));
+ goto done;
+ }
+
+ if (number_type == &msisdn) {
+ g_free(msisdn);
+ msisdn = g_strdup(number);
+ DBG("Got MSISDN %s (%s)", number, name);
+ } else {
+ g_free(vmbx);
+ vmbx = g_strdup(number);
+ DBG("Got voice mailbox number %s (%s)", number, name);
+ }
+
+done:
+ dbus_message_unref(reply);
+}
+
+static void csd_init(void)
+{
+ dbus_uint32_t location;
+ uint8_t pb_type, location_type;
+ int ret;
+
+ ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+ CSD_CALL_INTERFACE, "GetCallInfoAll",
+ call_info_reply, NULL, DBUS_TYPE_INVALID);
+ if (ret < 0) {
+ error("Unable to sent GetCallInfoAll method call");
+ return;
+ }
+
+ get_calls_active = TRUE;
+
+ pb_type = SIM_PHONEBOOK_TYPE_MSISDN;
+ location = PHONEBOOK_INDEX_FIRST_ENTRY;
+ location_type = SIM_PHONEBOOK_LOCATION_NEXT;
+
+ ret = send_method_call(SIM_PHONEBOOK_BUS_NAME, SIM_PHONEBOOK_PATH,
+ SIM_PHONEBOOK_INTERFACE, "read",
+ phonebook_read_reply, &msisdn,
+ DBUS_TYPE_BYTE, &pb_type,
+ DBUS_TYPE_INT32, &location,
+ DBUS_TYPE_BYTE, &location_type,
+ DBUS_TYPE_INVALID);
+ if (ret < 0) {
+ error("Unable to send " SIM_PHONEBOOK_INTERFACE ".read()");
+ return;
+ }
+
+ pb_type = SIM_PHONEBOOK_TYPE_MBDN;
+ location = PHONEBOOK_INDEX_FIRST_ENTRY;
+ location_type = SIM_PHONEBOOK_LOCATION_NEXT;
+
+ ret = send_method_call(SIM_PHONEBOOK_BUS_NAME, SIM_PHONEBOOK_PATH,
+ SIM_PHONEBOOK_INTERFACE, "read",
+ phonebook_read_reply, &vmbx,
+ DBUS_TYPE_BYTE, &pb_type,
+ DBUS_TYPE_INT32, &location,
+ DBUS_TYPE_BYTE, &location_type,
+ DBUS_TYPE_INVALID);
+ if (ret < 0) {
+ error("Unable to send " SIM_PHONEBOOK_INTERFACE ".read()");
+ return;
+ }
+}
+
+static uint32_t get_callflag(const char *callerid_setting)
+{
+ if (callerid_setting != NULL) {
+ if (g_str_equal(callerid_setting, "allowed"))
+ return CALL_FLAG_PRESENTATION_ALLOWED;
+ else if (g_str_equal(callerid_setting, "restricted"))
+ return CALL_FLAG_PRESENTATION_RESTRICTED;
+ else
+ return CALL_FLAG_NONE;
+ } else
+ return CALL_FLAG_NONE;
+}
+
+static void generate_flag_file(const char *filename)
+{
+ int fd;
+
+ if (g_file_test(ALLOWED_FLAG_FILE, G_FILE_TEST_EXISTS) ||
+ g_file_test(RESTRICTED_FLAG_FILE, G_FILE_TEST_EXISTS) ||
+ g_file_test(NONE_FLAG_FILE, G_FILE_TEST_EXISTS))
+ return;
+
+ fd = open(filename, O_WRONLY | O_CREAT, 0);
+ if (fd >= 0)
+ close(fd);
+}
+
+static void save_callerid_to_file(const char *callerid_setting)
+{
+ char callerid_file[FILENAME_MAX];
+
+ snprintf(callerid_file, sizeof(callerid_file), "%s%s",
+ CALLERID_BASE, callerid_setting);
+
+ if (g_file_test(ALLOWED_FLAG_FILE, G_FILE_TEST_EXISTS))
+ rename(ALLOWED_FLAG_FILE, callerid_file);
+ else if (g_file_test(RESTRICTED_FLAG_FILE, G_FILE_TEST_EXISTS))
+ rename(RESTRICTED_FLAG_FILE, callerid_file);
+ else if (g_file_test(NONE_FLAG_FILE, G_FILE_TEST_EXISTS))
+ rename(NONE_FLAG_FILE, callerid_file);
+ else
+ generate_flag_file(callerid_file);
+}
+
+static uint32_t callerid_from_file(void)
+{
+ if (g_file_test(ALLOWED_FLAG_FILE, G_FILE_TEST_EXISTS))
+ return CALL_FLAG_PRESENTATION_ALLOWED;
+ else if (g_file_test(RESTRICTED_FLAG_FILE, G_FILE_TEST_EXISTS))
+ return CALL_FLAG_PRESENTATION_RESTRICTED;
+ else if (g_file_test(NONE_FLAG_FILE, G_FILE_TEST_EXISTS))
+ return CALL_FLAG_NONE;
+ else
+ return CALL_FLAG_NONE;
+}
+
+static DBusMessage *set_callerid(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ const char *callerid_setting;
+
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING,
+ &callerid_setting,
+ DBUS_TYPE_INVALID) == FALSE)
+ return btd_error_invalid_args(msg);
+
+ if (g_str_equal(callerid_setting, "allowed") ||
+ g_str_equal(callerid_setting, "restricted") ||
+ g_str_equal(callerid_setting, "none")) {
+ save_callerid_to_file(callerid_setting);
+ callerid = get_callflag(callerid_setting);
+ DBG("telephony-maemo setting callerid flag: %s",
+ callerid_setting);
+ return dbus_message_new_method_return(msg);
+ }
+
+ error("telephony-maemo: invalid argument %s for method call"
+ " SetCallerId", callerid_setting);
+ return btd_error_invalid_args(msg);
+}
+
+static GDBusMethodTable telephony_maemo_methods[] = {
+ {"SetCallerId", "s", "", set_callerid,
+ G_DBUS_METHOD_FLAG_ASYNC},
+ { }
+};
+
+static void handle_modem_state(DBusMessage *msg)
+{
+ const char *state;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &state,
+ DBUS_TYPE_INVALID)) {
+ error("Unexpected modem state parameters");
+ return;
+ }
+
+ DBG("SSC modem state: %s", state);
+
+ if (calls != NULL || get_calls_active)
+ return;
+
+ if (g_str_equal(state, "cmt_ready") || g_str_equal(state, "online"))
+ csd_init();
+}
+
+static void modem_state_reply(DBusPendingCall *call, void *user_data)
+{
+ DBusMessage *reply = dbus_pending_call_steal_reply(call);
+ DBusError err;
+
+ dbus_error_init(&err);
+ if (dbus_set_error_from_message(&err, reply)) {
+ error("get_modem_status: %s, %s", err.name, err.message);
+ dbus_error_free(&err);
+ } else
+ handle_modem_state(reply);
+
+ dbus_message_unref(reply);
+}
+
+static DBusHandlerResult signal_filter(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ const char *path = dbus_message_get_path(msg);
+
+ if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_SIGNAL)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE, "Coming"))
+ handle_incoming_call(msg);
+ else if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE, "Created"))
+ handle_outgoing_call(msg);
+ else if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE,
+ "CreateRequested"))
+ handle_create_requested(msg);
+ else if (dbus_message_is_signal(msg, CSD_CALL_INSTANCE, "CallStatus"))
+ handle_call_status(msg, path);
+ else if (dbus_message_is_signal(msg, CSD_CALL_CONFERENCE, "Joined"))
+ handle_conference(msg, TRUE);
+ else if (dbus_message_is_signal(msg, CSD_CALL_CONFERENCE, "Left"))
+ handle_conference(msg, FALSE);
+ else if (dbus_message_is_signal(msg, NETWORK_INTERFACE,
+ "registration_status_change"))
+ handle_registration_status_change(msg);
+ else if (dbus_message_is_signal(msg, NETWORK_INTERFACE,
+ "signal_strength_change"))
+ handle_signal_strength_change(msg);
+ else if (dbus_message_is_signal(msg, "org.freedesktop.Hal.Device",
+ "PropertyModified"))
+ handle_hal_property_modified(msg);
+ else if (dbus_message_is_signal(msg, SSC_DBUS_IFACE,
+ "modem_state_changed_ind"))
+ handle_modem_state(msg);
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+int telephony_init(void)
+{
+ const char *battery_cap = "battery";
+ uint32_t features = AG_FEATURE_EC_ANDOR_NR |
+ AG_FEATURE_INBAND_RINGTONE |
+ AG_FEATURE_REJECT_A_CALL |
+ AG_FEATURE_ENHANCED_CALL_STATUS |
+ AG_FEATURE_ENHANCED_CALL_CONTROL |
+ AG_FEATURE_EXTENDED_ERROR_RESULT_CODES |
+ AG_FEATURE_THREE_WAY_CALLING;
+
+ connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+
+ if (!dbus_connection_add_filter(connection, signal_filter,
+ NULL, NULL))
+ error("Can't add signal filter");
+
+ dbus_bus_add_match(connection,
+ "type=signal,interface=" CSD_CALL_INTERFACE, NULL);
+ dbus_bus_add_match(connection,
+ "type=signal,interface=" CSD_CALL_INSTANCE, NULL);
+ dbus_bus_add_match(connection,
+ "type=signal,interface=" CSD_CALL_CONFERENCE, NULL);
+ dbus_bus_add_match(connection,
+ "type=signal,interface=" NETWORK_INTERFACE, NULL);
+ dbus_bus_add_match(connection,
+ "type=signal,interface=" SSC_DBUS_IFACE
+ ",member=modem_state_changed_ind", NULL);
+
+ if (send_method_call(SSC_DBUS_NAME, SSC_DBUS_PATH, SSC_DBUS_IFACE,
+ "get_modem_state", modem_state_reply,
+ NULL, DBUS_TYPE_INVALID) < 0)
+ error("Unable to send " SSC_DBUS_IFACE ".get_modem_state()");
+
+ generate_flag_file(NONE_FLAG_FILE);
+ callerid = callerid_from_file();
+
+ if (!g_dbus_register_interface(connection, TELEPHONY_MAEMO_PATH,
+ TELEPHONY_MAEMO_INTERFACE, telephony_maemo_methods,
+ NULL, NULL, NULL, NULL)) {
+ error("telephony-maemo interface %s init failed on path %s",
+ TELEPHONY_MAEMO_INTERFACE, TELEPHONY_MAEMO_PATH);
+ }
+
+ DBG("telephony-maemo registering %s interface on path %s",
+ TELEPHONY_MAEMO_INTERFACE, TELEPHONY_MAEMO_PATH);
+
+ telephony_ready_ind(features, maemo_indicators, BTRH_NOT_SUPPORTED,
+ chld_str);
+ if (send_method_call("org.freedesktop.Hal",
+ "/org/freedesktop/Hal/Manager",
+ "org.freedesktop.Hal.Manager",
+ "FindDeviceByCapability",
+ hal_find_device_reply, NULL,
+ DBUS_TYPE_STRING, &battery_cap,
+ DBUS_TYPE_INVALID) < 0)
+ error("Unable to send HAL method call");
+
+ return 0;
+}
+
+void telephony_exit(void)
+{
+ g_slist_foreach(calls, (GFunc) csd_call_free, NULL);
+ g_slist_free(calls);
+ calls = NULL;
+
+ dbus_connection_remove_filter(connection, signal_filter, NULL);
+
+ dbus_connection_unref(connection);
+ connection = NULL;
+
+ telephony_deinit();
+}
diff --git a/audio/telephony-maemo6.c b/audio/telephony-maemo6.c
new file mode 100644
index 0000000..0cef7dd
--- /dev/null
+++ b/audio/telephony-maemo6.c
@@ -0,0 +1,1993 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2008-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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 <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <string.h>
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+#include "telephony.h"
+#include "error.h"
+
+/* SSC D-Bus definitions */
+#define SSC_DBUS_NAME "com.nokia.phone.SSC"
+#define SSC_DBUS_IFACE "com.nokia.phone.SSC"
+#define SSC_DBUS_PATH "/com/nokia/phone/SSC"
+
+/* libcsnet D-Bus definitions */
+#define CSD_CSNET_BUS_NAME "com.nokia.csd.CSNet"
+#define CSD_CSNET_PATH "/com/nokia/csd/csnet"
+#define CSD_CSNET_IFACE "com.nokia.csd.CSNet"
+#define CSD_CSNET_REGISTRATION "com.nokia.csd.CSNet.NetworkRegistration"
+#define CSD_CSNET_OPERATOR "com.nokia.csd.CSNet.NetworkOperator"
+#define CSD_CSNET_SIGNAL "com.nokia.csd.CSNet.SignalStrength"
+
+enum net_registration_status {
+ NETWORK_REG_STATUS_HOME,
+ NETWORK_REG_STATUS_ROAMING,
+ NETWORK_REG_STATUS_OFFLINE,
+ NETWORK_REG_STATUS_SEARCHING,
+ NETWORK_REG_STATUS_NO_SIM,
+ NETWORK_REG_STATUS_POWEROFF,
+ NETWORK_REG_STATUS_POWERSAFE,
+ NETWORK_REG_STATUS_NO_COVERAGE,
+ NETWORK_REG_STATUS_REJECTED,
+ NETWORK_REG_STATUS_UNKOWN
+};
+
+/* CSD CALL plugin D-Bus definitions */
+#define CSD_CALL_BUS_NAME "com.nokia.csd.Call"
+#define CSD_CALL_INTERFACE "com.nokia.csd.Call"
+#define CSD_CALL_INSTANCE "com.nokia.csd.Call.Instance"
+#define CSD_CALL_CONFERENCE "com.nokia.csd.Call.Conference"
+#define CSD_CALL_PATH "/com/nokia/csd/call"
+#define CSD_CALL_CONFERENCE_PATH "/com/nokia/csd/call/conference"
+
+/* Call status values as exported by the CSD CALL plugin */
+#define CSD_CALL_STATUS_IDLE 0
+#define CSD_CALL_STATUS_CREATE 1
+#define CSD_CALL_STATUS_COMING 2
+#define CSD_CALL_STATUS_PROCEEDING 3
+#define CSD_CALL_STATUS_MO_ALERTING 4
+#define CSD_CALL_STATUS_MT_ALERTING 5
+#define CSD_CALL_STATUS_WAITING 6
+#define CSD_CALL_STATUS_ANSWERED 7
+#define CSD_CALL_STATUS_ACTIVE 8
+#define CSD_CALL_STATUS_MO_RELEASE 9
+#define CSD_CALL_STATUS_MT_RELEASE 10
+#define CSD_CALL_STATUS_HOLD_INITIATED 11
+#define CSD_CALL_STATUS_HOLD 12
+#define CSD_CALL_STATUS_RETRIEVE_INITIATED 13
+#define CSD_CALL_STATUS_RECONNECT_PENDING 14
+#define CSD_CALL_STATUS_TERMINATED 15
+#define CSD_CALL_STATUS_SWAP_INITIATED 16
+
+#define CALL_FLAG_NONE 0
+#define CALL_FLAG_PRESENTATION_ALLOWED 0x01
+#define CALL_FLAG_PRESENTATION_RESTRICTED 0x02
+
+/* SIM Phonebook D-Bus definitions */
+#define CSD_SIMPB_BUS_NAME "com.nokia.csd.SIM"
+#define CSD_SIMPB_INTERFACE "com.nokia.csd.SIM.Phonebook"
+#define CSD_SIMPB_PATH "/com/nokia/csd/sim/phonebook"
+
+#define CSD_SIMPB_TYPE_ADN "ADN"
+#define CSD_SIMPB_TYPE_FDN "FDN"
+#define CSD_SIMPB_TYPE_SDN "SDN"
+#define CSD_SIMPB_TYPE_VMBX "VMBX"
+#define CSD_SIMPB_TYPE_MBDN "MBDN"
+#define CSD_SIMPB_TYPE_EN "EN"
+#define CSD_SIMPB_TYPE_MSISDN "MSISDN"
+
+struct csd_call {
+ char *object_path;
+ int status;
+ gboolean originating;
+ gboolean emergency;
+ gboolean on_hold;
+ gboolean conference;
+ char *number;
+ gboolean setup;
+};
+
+static struct {
+ char *operator_name;
+ uint8_t status;
+ int32_t signal_bars;
+} net = {
+ .operator_name = NULL,
+ .status = NETWORK_REG_STATUS_UNKOWN,
+ /* Init as 0 meaning inactive mode. In modem power off state
+ * can be be -1, but we treat all values as 0s regardless
+ * inactive or power off. */
+ .signal_bars = 0,
+};
+
+struct pending_req {
+ DBusPendingCall *call;
+ void *user_data;
+};
+
+static int get_property(const char *iface, const char *prop);
+
+static DBusConnection *connection = NULL;
+
+static GSList *calls = NULL;
+static GSList *watches = NULL;
+static GSList *pending = NULL;
+
+/* Reference count for determining the call indicator status */
+static GSList *active_calls = NULL;
+
+static char *msisdn = NULL; /* Subscriber number */
+static char *vmbx = NULL; /* Voice mailbox number */
+
+/* HAL battery namespace key values */
+static int battchg_cur = -1; /* "battery.charge_level.current" */
+static int battchg_last = -1; /* "battery.charge_level.last_full" */
+static int battchg_design = -1; /* "battery.charge_level.design" */
+
+static gboolean get_calls_active = FALSE;
+
+static gboolean events_enabled = FALSE;
+
+/* Supported set of call hold operations */
+static const char *chld_str = "0,1,1x,2,2x,3,4";
+
+/* Timer for tracking call creation requests */
+static guint create_request_timer = 0;
+
+static struct indicator maemo_indicators[] =
+{
+ { "battchg", "0-5", 5, TRUE },
+ /* signal strength in terms of bars */
+ { "signal", "0-5", 0, TRUE },
+ { "service", "0,1", 0, TRUE },
+ { "call", "0,1", 0, TRUE },
+ { "callsetup", "0-3", 0, TRUE },
+ { "callheld", "0-2", 0, FALSE },
+ { "roam", "0,1", 0, TRUE },
+ { NULL }
+};
+
+static char *call_status_str[] = {
+ "IDLE",
+ "CREATE",
+ "COMING",
+ "PROCEEDING",
+ "MO_ALERTING",
+ "MT_ALERTING",
+ "WAITING",
+ "ANSWERED",
+ "ACTIVE",
+ "MO_RELEASE",
+ "MT_RELEASE",
+ "HOLD_INITIATED",
+ "HOLD",
+ "RETRIEVE_INITIATED",
+ "RECONNECT_PENDING",
+ "TERMINATED",
+ "SWAP_INITIATED",
+ "???"
+};
+
+static struct csd_call *find_call(const char *path)
+{
+ GSList *l;
+
+ for (l = calls; l != NULL; l = l->next) {
+ struct csd_call *call = l->data;
+
+ if (g_str_equal(call->object_path, path))
+ return call;
+ }
+
+ return NULL;
+}
+
+static struct csd_call *find_non_held_call(void)
+{
+ GSList *l;
+
+ for (l = calls; l != NULL; l = l->next) {
+ struct csd_call *call = l->data;
+
+ if (call->status == CSD_CALL_STATUS_IDLE)
+ continue;
+
+ if (call->status != CSD_CALL_STATUS_HOLD)
+ return call;
+ }
+
+ return NULL;
+}
+
+static struct csd_call *find_non_idle_call(void)
+{
+ GSList *l;
+
+ for (l = calls; l != NULL; l = l->next) {
+ struct csd_call *call = l->data;
+
+ if (call->status != CSD_CALL_STATUS_IDLE)
+ return call;
+ }
+
+ return NULL;
+}
+
+static struct csd_call *find_call_with_status(int status)
+{
+ GSList *l;
+
+ for (l = calls; l != NULL; l = l->next) {
+ struct csd_call *call = l->data;
+
+ if (call->status == status)
+ return call;
+ }
+
+ return NULL;
+}
+
+static int release_conference(void)
+{
+ DBusMessage *msg;
+
+ DBG("telephony-maemo6: releasing conference call");
+
+ msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
+ CSD_CALL_CONFERENCE_PATH,
+ CSD_CALL_INSTANCE,
+ "Release");
+ if (!msg) {
+ error("Unable to allocate new D-Bus message");
+ return -ENOMEM;
+ }
+
+ g_dbus_send_message(connection, msg);
+
+ return 0;
+}
+
+static int release_call(struct csd_call *call)
+{
+ DBusMessage *msg;
+
+ msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
+ call->object_path,
+ CSD_CALL_INSTANCE,
+ "Release");
+ if (!msg) {
+ error("Unable to allocate new D-Bus message");
+ return -ENOMEM;
+ }
+
+ g_dbus_send_message(connection, msg);
+
+ return 0;
+}
+
+static int answer_call(struct csd_call *call)
+{
+ DBusMessage *msg;
+
+ msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
+ call->object_path,
+ CSD_CALL_INSTANCE,
+ "Answer");
+ if (!msg) {
+ error("Unable to allocate new D-Bus message");
+ return -ENOMEM;
+ }
+
+ g_dbus_send_message(connection, msg);
+
+ return 0;
+}
+
+static int split_call(struct csd_call *call)
+{
+ DBusMessage *msg;
+
+ msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
+ call->object_path,
+ CSD_CALL_INSTANCE,
+ "Split");
+ if (!msg) {
+ error("Unable to allocate new D-Bus message");
+ return -ENOMEM;
+ }
+
+ g_dbus_send_message(connection, msg);
+
+ return 0;
+}
+
+static int unhold_call(struct csd_call *call)
+{
+ DBusMessage *msg;
+
+ msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+ CSD_CALL_INTERFACE,
+ "Unhold");
+ if (!msg) {
+ error("Unable to allocate new D-Bus message");
+ return -ENOMEM;
+ }
+
+ g_dbus_send_message(connection, msg);
+
+ return 0;
+}
+
+static int hold_call(struct csd_call *call)
+{
+ DBusMessage *msg;
+
+ msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+ CSD_CALL_INTERFACE,
+ "Hold");
+ if (!msg) {
+ error("Unable to allocate new D-Bus message");
+ return -ENOMEM;
+ }
+
+ g_dbus_send_message(connection, msg);
+
+ return 0;
+}
+
+static int swap_calls(void)
+{
+ DBusMessage *msg;
+
+ msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+ CSD_CALL_INTERFACE,
+ "Swap");
+ if (!msg) {
+ error("Unable to allocate new D-Bus message");
+ return -ENOMEM;
+ }
+
+ g_dbus_send_message(connection, msg);
+
+ return 0;
+}
+
+static int create_conference(void)
+{
+ DBusMessage *msg;
+
+ msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+ CSD_CALL_INTERFACE,
+ "Conference");
+ if (!msg) {
+ error("Unable to allocate new D-Bus message");
+ return -ENOMEM;
+ }
+
+ g_dbus_send_message(connection, msg);
+
+ return 0;
+}
+
+static int call_transfer(void)
+{
+ DBusMessage *msg;
+
+ msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+ CSD_CALL_INTERFACE,
+ "Transfer");
+ if (!msg) {
+ error("Unable to allocate new D-Bus message");
+ return -ENOMEM;
+ }
+
+ g_dbus_send_message(connection, msg);
+
+ return 0;
+}
+
+static int number_type(const char *number)
+{
+ if (number == NULL)
+ return NUMBER_TYPE_TELEPHONY;
+
+ if (number[0] == '+' || strncmp(number, "00", 2) == 0)
+ return NUMBER_TYPE_INTERNATIONAL;
+
+ return NUMBER_TYPE_TELEPHONY;
+}
+
+void telephony_device_connected(void *telephony_device)
+{
+ struct csd_call *coming;
+
+ DBG("telephony-maemo6: device %p connected", telephony_device);
+
+ coming = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING);
+ if (coming) {
+ if (find_call_with_status(CSD_CALL_STATUS_ACTIVE))
+ telephony_call_waiting_ind(coming->number,
+ number_type(coming->number));
+ else
+ telephony_incoming_call_ind(coming->number,
+ number_type(coming->number));
+ }
+}
+
+static void pending_req_finalize(struct pending_req *req)
+{
+ if (!dbus_pending_call_get_completed(req->call))
+ dbus_pending_call_cancel(req->call);
+
+ dbus_pending_call_unref(req->call);
+ g_free(req);
+}
+
+static void remove_pending_by_data(gpointer data, gpointer user_data)
+{
+ struct pending_req *req = data;
+
+ if (req->user_data == user_data) {
+ pending = g_slist_remove(pending, req);
+ pending_req_finalize(req);
+ }
+}
+
+void telephony_device_disconnected(void *telephony_device)
+{
+ DBG("telephony-maemo6: device %p disconnected", telephony_device);
+ events_enabled = FALSE;
+
+ g_slist_foreach(pending, remove_pending_by_data, telephony_device);
+}
+
+void telephony_event_reporting_req(void *telephony_device, int ind)
+{
+ events_enabled = ind == 1 ? TRUE : FALSE;
+
+ telephony_event_reporting_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_response_and_hold_req(void *telephony_device, int rh)
+{
+ telephony_response_and_hold_rsp(telephony_device,
+ CME_ERROR_NOT_SUPPORTED);
+}
+
+void telephony_terminate_call_req(void *telephony_device)
+{
+ struct csd_call *call;
+ struct csd_call *alerting;
+ int err;
+
+ call = find_call_with_status(CSD_CALL_STATUS_ACTIVE);
+ if (!call)
+ call = find_non_idle_call();
+
+ if (!call) {
+ error("No active call");
+ telephony_terminate_call_rsp(telephony_device,
+ CME_ERROR_NOT_ALLOWED);
+ return;
+ }
+
+ alerting = find_call_with_status(CSD_CALL_STATUS_MO_ALERTING);
+ if (call->on_hold && alerting)
+ err = release_call(alerting);
+ else if (call->conference)
+ err = release_conference();
+ else
+ err = release_call(call);
+
+ if (err < 0)
+ telephony_terminate_call_rsp(telephony_device,
+ CME_ERROR_AG_FAILURE);
+ else
+ telephony_terminate_call_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_answer_call_req(void *telephony_device)
+{
+ struct csd_call *call;
+
+ call = find_call_with_status(CSD_CALL_STATUS_COMING);
+ if (!call)
+ call = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING);
+
+ if (!call)
+ call = find_call_with_status(CSD_CALL_STATUS_PROCEEDING);
+
+ if (!call)
+ call = find_call_with_status(CSD_CALL_STATUS_WAITING);
+
+ if (!call) {
+ telephony_answer_call_rsp(telephony_device,
+ CME_ERROR_NOT_ALLOWED);
+ return;
+ }
+
+ if (answer_call(call) < 0)
+ telephony_answer_call_rsp(telephony_device,
+ CME_ERROR_AG_FAILURE);
+ else
+ telephony_answer_call_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+static int send_method_call(const char *dest, const char *path,
+ const char *interface, const char *method,
+ DBusPendingCallNotifyFunction cb,
+ void *user_data, int type, ...)
+{
+ DBusMessage *msg;
+ DBusPendingCall *call;
+ va_list args;
+ struct pending_req *req;
+
+ msg = dbus_message_new_method_call(dest, path, interface, method);
+ if (!msg) {
+ error("Unable to allocate new D-Bus %s message", method);
+ return -ENOMEM;
+ }
+
+ va_start(args, type);
+
+ if (!dbus_message_append_args_valist(msg, type, args)) {
+ dbus_message_unref(msg);
+ va_end(args);
+ return -EIO;
+ }
+
+ va_end(args);
+
+ if (!cb) {
+ g_dbus_send_message(connection, msg);
+ return 0;
+ }
+
+ if (!dbus_connection_send_with_reply(connection, msg, &call, -1)) {
+ error("Sending %s failed", method);
+ dbus_message_unref(msg);
+ return -EIO;
+ }
+
+ dbus_pending_call_set_notify(call, cb, user_data, NULL);
+
+ req = g_new0(struct pending_req, 1);
+ req->call = call;
+ req->user_data = user_data;
+
+ pending = g_slist_prepend(pending, req);
+ dbus_message_unref(msg);
+
+ return 0;
+}
+
+static struct pending_req *find_request(const DBusPendingCall *call)
+{
+ GSList *l;
+
+ for (l = pending; l; l = l->next) {
+ struct pending_req *req = l->data;
+
+ if (req->call == call)
+ return req;
+ }
+
+ return NULL;
+}
+
+static void remove_pending(DBusPendingCall *call)
+{
+ struct pending_req *req = find_request(call);
+
+ pending = g_slist_remove(pending, req);
+ pending_req_finalize(req);
+}
+
+static void last_number_call_reply(DBusPendingCall *call, void *user_data)
+{
+ DBusError err;
+ DBusMessage *reply;
+ void *telephony_device = user_data;
+
+ reply = dbus_pending_call_steal_reply(call);
+
+ dbus_error_init(&err);
+ if (dbus_set_error_from_message(&err, reply)) {
+ error("csd replied with an error: %s, %s",
+ err.name, err.message);
+ dbus_error_free(&err);
+ telephony_dial_number_rsp(telephony_device,
+ CME_ERROR_AG_FAILURE);
+ } else
+ telephony_dial_number_rsp(telephony_device, CME_ERROR_NONE);
+
+ dbus_message_unref(reply);
+ remove_pending(call);
+}
+
+void telephony_last_dialed_number_req(void *telephony_device)
+{
+ int ret;
+
+ DBG("telephony-maemo6: last dialed number request");
+
+ ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+ CSD_CALL_INTERFACE, "CreateFromLast",
+ last_number_call_reply, telephony_device,
+ DBUS_TYPE_INVALID);
+ if (ret < 0)
+ telephony_dial_number_rsp(telephony_device,
+ CME_ERROR_AG_FAILURE);
+}
+
+static const char *memory_dial_lookup(int location)
+{
+ if (location == 1)
+ return vmbx;
+ else
+ return NULL;
+}
+
+void telephony_dial_number_req(void *telephony_device, const char *number)
+{
+ int ret;
+
+ DBG("telephony-maemo6: dial request to %s", number);
+
+ if (strncmp(number, "*31#", 4) == 0)
+ number += 4;
+ else if (strncmp(number, "#31#", 4) == 0)
+ number += 4;
+ else if (number[0] == '>') {
+ const char *location = &number[1];
+
+ number = memory_dial_lookup(strtol(&number[1], NULL, 0));
+ if (!number) {
+ error("No number at memory location %s", location);
+ telephony_dial_number_rsp(telephony_device,
+ CME_ERROR_INVALID_INDEX);
+ return;
+ }
+ }
+
+ ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+ CSD_CALL_INTERFACE, "Create",
+ NULL, NULL,
+ DBUS_TYPE_STRING, &number,
+ DBUS_TYPE_INVALID);
+ if (ret < 0) {
+ telephony_dial_number_rsp(telephony_device,
+ CME_ERROR_AG_FAILURE);
+ return;
+ }
+
+ telephony_dial_number_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_transmit_dtmf_req(void *telephony_device, char tone)
+{
+ int ret;
+ char buf[2] = { tone, '\0' }, *buf_ptr = buf;
+
+ DBG("telephony-maemo6: transmit dtmf: %s", buf);
+
+ ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+ CSD_CALL_INTERFACE, "SendDTMF",
+ NULL, NULL,
+ DBUS_TYPE_STRING, &buf_ptr,
+ DBUS_TYPE_INVALID);
+ if (ret < 0) {
+ telephony_transmit_dtmf_rsp(telephony_device,
+ CME_ERROR_AG_FAILURE);
+ return;
+ }
+
+ telephony_transmit_dtmf_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_subscriber_number_req(void *telephony_device)
+{
+ DBG("telephony-maemo6: subscriber number request");
+ if (msisdn)
+ telephony_subscriber_number_ind(msisdn,
+ number_type(msisdn),
+ SUBSCRIBER_SERVICE_VOICE);
+ telephony_subscriber_number_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+static int csd_status_to_hfp(struct csd_call *call)
+{
+ switch (call->status) {
+ case CSD_CALL_STATUS_IDLE:
+ case CSD_CALL_STATUS_MO_RELEASE:
+ case CSD_CALL_STATUS_MT_RELEASE:
+ case CSD_CALL_STATUS_TERMINATED:
+ return -1;
+ case CSD_CALL_STATUS_CREATE:
+ return CALL_STATUS_DIALING;
+ case CSD_CALL_STATUS_WAITING:
+ return CALL_STATUS_WAITING;
+ case CSD_CALL_STATUS_PROCEEDING:
+ /* PROCEEDING can happen in outgoing/incoming */
+ if (call->originating)
+ return CALL_STATUS_DIALING;
+ else
+ return CALL_STATUS_INCOMING;
+ case CSD_CALL_STATUS_COMING:
+ return CALL_STATUS_INCOMING;
+ case CSD_CALL_STATUS_MO_ALERTING:
+ return CALL_STATUS_ALERTING;
+ case CSD_CALL_STATUS_MT_ALERTING:
+ return CALL_STATUS_INCOMING;
+ case CSD_CALL_STATUS_ANSWERED:
+ case CSD_CALL_STATUS_ACTIVE:
+ case CSD_CALL_STATUS_RECONNECT_PENDING:
+ case CSD_CALL_STATUS_SWAP_INITIATED:
+ case CSD_CALL_STATUS_HOLD_INITIATED:
+ return CALL_STATUS_ACTIVE;
+ case CSD_CALL_STATUS_RETRIEVE_INITIATED:
+ case CSD_CALL_STATUS_HOLD:
+ return CALL_STATUS_HELD;
+ default:
+ return -1;
+ }
+}
+
+void telephony_list_current_calls_req(void *telephony_device)
+{
+ GSList *l;
+ int i;
+
+ DBG("telephony-maemo6: list current calls request");
+
+ for (l = calls, i = 1; l != NULL; l = l->next, i++) {
+ struct csd_call *call = l->data;
+ int status, direction, multiparty;
+
+ status = csd_status_to_hfp(call);
+ if (status < 0)
+ continue;
+
+ direction = call->originating ?
+ CALL_DIR_OUTGOING : CALL_DIR_INCOMING;
+
+ multiparty = call->conference ?
+ CALL_MULTIPARTY_YES : CALL_MULTIPARTY_NO;
+
+ telephony_list_current_call_ind(i, direction, status,
+ CALL_MODE_VOICE, multiparty,
+ call->number,
+ number_type(call->number));
+ }
+
+ telephony_list_current_calls_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_operator_selection_req(void *telephony_device)
+{
+ telephony_operator_selection_ind(OPERATOR_MODE_AUTO,
+ net.operator_name ? net.operator_name : "");
+ telephony_operator_selection_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+static void foreach_call_with_status(int status,
+ int (*func)(struct csd_call *call))
+{
+ GSList *l;
+
+ for (l = calls; l != NULL; l = l->next) {
+ struct csd_call *call = l->data;
+
+ if (call->status == status)
+ func(call);
+ }
+}
+
+void telephony_call_hold_req(void *telephony_device, const char *cmd)
+{
+ const char *idx;
+ struct csd_call *call;
+ int err = 0;
+
+ DBG("telephony-maemo6: got call hold request %s", cmd);
+
+ if (strlen(cmd) > 1)
+ idx = &cmd[1];
+ else
+ idx = NULL;
+
+ if (idx)
+ call = g_slist_nth_data(calls, strtol(idx, NULL, 0) - 1);
+ else
+ call = NULL;
+
+ switch (cmd[0]) {
+ case '0':
+ if (find_call_with_status(CSD_CALL_STATUS_WAITING))
+ foreach_call_with_status(CSD_CALL_STATUS_WAITING,
+ release_call);
+ else
+ foreach_call_with_status(CSD_CALL_STATUS_HOLD,
+ release_call);
+ break;
+ case '1':
+ if (idx) {
+ if (call)
+ err = release_call(call);
+ break;
+ }
+ foreach_call_with_status(CSD_CALL_STATUS_ACTIVE, release_call);
+ call = find_call_with_status(CSD_CALL_STATUS_WAITING);
+ if (call)
+ err = answer_call(call);
+ break;
+ case '2':
+ if (idx) {
+ if (call)
+ err = split_call(call);
+ } else {
+ struct csd_call *held, *wait;
+
+ call = find_call_with_status(CSD_CALL_STATUS_ACTIVE);
+ held = find_call_with_status(CSD_CALL_STATUS_HOLD);
+ wait = find_call_with_status(CSD_CALL_STATUS_WAITING);
+
+ if (wait)
+ err = answer_call(wait);
+ else if (call && held)
+ err = swap_calls();
+ else {
+ if (call)
+ err = hold_call(call);
+ if (held)
+ err = unhold_call(held);
+ }
+ }
+ break;
+ case '3':
+ if (find_call_with_status(CSD_CALL_STATUS_HOLD) ||
+ find_call_with_status(CSD_CALL_STATUS_WAITING))
+ err = create_conference();
+ break;
+ case '4':
+ err = call_transfer();
+ break;
+ default:
+ DBG("Unknown call hold request");
+ break;
+ }
+
+ if (err)
+ telephony_call_hold_rsp(telephony_device,
+ CME_ERROR_AG_FAILURE);
+ else
+ telephony_call_hold_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_nr_and_ec_req(void *telephony_device, gboolean enable)
+{
+ DBG("telephony-maemo6: got %s NR and EC request",
+ enable ? "enable" : "disable");
+ telephony_nr_and_ec_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_key_press_req(void *telephony_device, const char *keys)
+{
+ struct csd_call *active, *waiting;
+ int err;
+
+ DBG("telephony-maemo6: got key press request for %s", keys);
+
+ waiting = find_call_with_status(CSD_CALL_STATUS_COMING);
+ if (!waiting)
+ waiting = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING);
+ if (!waiting)
+ waiting = find_call_with_status(CSD_CALL_STATUS_PROCEEDING);
+
+ active = find_call_with_status(CSD_CALL_STATUS_ACTIVE);
+
+ if (waiting)
+ err = answer_call(waiting);
+ else if (active)
+ err = release_call(active);
+ else
+ err = 0;
+
+ if (err < 0)
+ telephony_key_press_rsp(telephony_device,
+ CME_ERROR_AG_FAILURE);
+ else
+ telephony_key_press_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_voice_dial_req(void *telephony_device, gboolean enable)
+{
+ DBG("telephony-maemo6: got %s voice dial request",
+ enable ? "enable" : "disable");
+
+ telephony_voice_dial_rsp(telephony_device, CME_ERROR_NOT_SUPPORTED);
+}
+
+static void handle_incoming_call(DBusMessage *msg)
+{
+ const char *number, *call_path;
+ struct csd_call *call;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_OBJECT_PATH, &call_path,
+ DBUS_TYPE_STRING, &number,
+ DBUS_TYPE_INVALID)) {
+ error("Unexpected parameters in Call.Coming() signal");
+ return;
+ }
+
+ call = find_call(call_path);
+ if (!call) {
+ error("Didn't find any matching call object for %s",
+ call_path);
+ return;
+ }
+
+ DBG("Incoming call to %s from number %s", call_path, number);
+
+ g_free(call->number);
+ call->number = g_strdup(number);
+
+ if (find_call_with_status(CSD_CALL_STATUS_ACTIVE) ||
+ find_call_with_status(CSD_CALL_STATUS_HOLD))
+ telephony_call_waiting_ind(call->number,
+ number_type(call->number));
+ else
+ telephony_incoming_call_ind(call->number,
+ number_type(call->number));
+
+ telephony_update_indicator(maemo_indicators, "callsetup",
+ EV_CALLSETUP_INCOMING);
+}
+
+static void handle_outgoing_call(DBusMessage *msg)
+{
+ const char *number, *call_path;
+ struct csd_call *call;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_OBJECT_PATH, &call_path,
+ DBUS_TYPE_STRING, &number,
+ DBUS_TYPE_INVALID)) {
+ error("Unexpected parameters in Call.Created() signal");
+ return;
+ }
+
+ call = find_call(call_path);
+ if (!call) {
+ error("Didn't find any matching call object for %s",
+ call_path);
+ return;
+ }
+
+ DBG("Outgoing call from %s to number %s", call_path, number);
+
+ g_free(call->number);
+ call->number = g_strdup(number);
+
+ if (create_request_timer) {
+ g_source_remove(create_request_timer);
+ create_request_timer = 0;
+ }
+}
+
+static gboolean create_timeout(gpointer user_data)
+{
+ telephony_update_indicator(maemo_indicators, "callsetup",
+ EV_CALLSETUP_INACTIVE);
+ create_request_timer = 0;
+ return FALSE;
+}
+
+static void handle_create_requested(DBusMessage *msg)
+{
+ DBG("Call.CreateRequested()");
+
+ if (create_request_timer)
+ g_source_remove(create_request_timer);
+
+ create_request_timer = g_timeout_add_seconds(5, create_timeout, NULL);
+
+ telephony_update_indicator(maemo_indicators, "callsetup",
+ EV_CALLSETUP_OUTGOING);
+}
+
+static void call_set_status(struct csd_call *call, dbus_uint32_t status)
+{
+ dbus_uint32_t prev_status;
+ int callheld = telephony_get_indicator(maemo_indicators, "callheld");
+
+ prev_status = call->status;
+ DBG("Call %s changed from %s to %s", call->object_path,
+ call_status_str[prev_status], call_status_str[status]);
+
+ if (prev_status == status) {
+ DBG("Ignoring CSD Call state change to existing state");
+ return;
+ }
+
+ call->status = (int) status;
+
+ switch (status) {
+ case CSD_CALL_STATUS_IDLE:
+ if (call->setup) {
+ telephony_update_indicator(maemo_indicators,
+ "callsetup",
+ EV_CALLSETUP_INACTIVE);
+ if (!call->originating)
+ telephony_calling_stopped_ind();
+ }
+
+ g_free(call->number);
+ call->number = NULL;
+ call->originating = FALSE;
+ call->emergency = FALSE;
+ call->on_hold = FALSE;
+ call->conference = FALSE;
+ call->setup = FALSE;
+ break;
+ case CSD_CALL_STATUS_CREATE:
+ call->originating = TRUE;
+ call->setup = TRUE;
+ break;
+ case CSD_CALL_STATUS_COMING:
+ call->originating = FALSE;
+ call->setup = TRUE;
+ break;
+ case CSD_CALL_STATUS_PROCEEDING:
+ break;
+ case CSD_CALL_STATUS_MO_ALERTING:
+ telephony_update_indicator(maemo_indicators, "callsetup",
+ EV_CALLSETUP_ALERTING);
+ break;
+ case CSD_CALL_STATUS_MT_ALERTING:
+ /* Some headsets expect incoming call notification before they
+ * can send ATA command. When call changed status from waiting
+ * to alerting we need to send missing notification. Otherwise
+ * headsets like Nokia BH-108 or BackBeat 903 are unable to
+ * answer incoming call that was previously waiting. */
+ if (prev_status == CSD_CALL_STATUS_WAITING)
+ telephony_incoming_call_ind(call->number,
+ number_type(call->number));
+ break;
+ case CSD_CALL_STATUS_WAITING:
+ break;
+ case CSD_CALL_STATUS_ANSWERED:
+ break;
+ case CSD_CALL_STATUS_ACTIVE:
+ if (call->on_hold) {
+ call->on_hold = FALSE;
+ if (find_call_with_status(CSD_CALL_STATUS_HOLD))
+ telephony_update_indicator(maemo_indicators,
+ "callheld",
+ EV_CALLHELD_MULTIPLE);
+ else
+ telephony_update_indicator(maemo_indicators,
+ "callheld",
+ EV_CALLHELD_NONE);
+ } else {
+ if (!g_slist_find(active_calls, call))
+ active_calls = g_slist_prepend(active_calls, call);
+ if (g_slist_length(active_calls) == 1)
+ telephony_update_indicator(maemo_indicators,
+ "call",
+ EV_CALL_ACTIVE);
+ /* Upgrade callheld status if necessary */
+ if (callheld == EV_CALLHELD_ON_HOLD)
+ telephony_update_indicator(maemo_indicators,
+ "callheld",
+ EV_CALLHELD_MULTIPLE);
+ telephony_update_indicator(maemo_indicators,
+ "callsetup",
+ EV_CALLSETUP_INACTIVE);
+ if (!call->originating)
+ telephony_calling_stopped_ind();
+ call->setup = FALSE;
+ }
+ break;
+ case CSD_CALL_STATUS_MO_RELEASE:
+ case CSD_CALL_STATUS_MT_RELEASE:
+ active_calls = g_slist_remove(active_calls, call);
+ if (g_slist_length(active_calls) == 0)
+ telephony_update_indicator(maemo_indicators, "call",
+ EV_CALL_INACTIVE);
+ break;
+ case CSD_CALL_STATUS_HOLD_INITIATED:
+ break;
+ case CSD_CALL_STATUS_HOLD:
+ call->on_hold = TRUE;
+ if (find_non_held_call())
+ telephony_update_indicator(maemo_indicators,
+ "callheld",
+ EV_CALLHELD_MULTIPLE);
+ else
+ telephony_update_indicator(maemo_indicators,
+ "callheld",
+ EV_CALLHELD_ON_HOLD);
+ break;
+ case CSD_CALL_STATUS_RETRIEVE_INITIATED:
+ break;
+ case CSD_CALL_STATUS_RECONNECT_PENDING:
+ break;
+ case CSD_CALL_STATUS_TERMINATED:
+ if (call->on_hold &&
+ !find_call_with_status(CSD_CALL_STATUS_HOLD))
+ telephony_update_indicator(maemo_indicators,
+ "callheld",
+ EV_CALLHELD_NONE);
+ else if (callheld == EV_CALLHELD_MULTIPLE &&
+ find_call_with_status(CSD_CALL_STATUS_HOLD))
+ telephony_update_indicator(maemo_indicators,
+ "callheld",
+ EV_CALLHELD_ON_HOLD);
+ break;
+ case CSD_CALL_STATUS_SWAP_INITIATED:
+ break;
+ default:
+ error("Unknown call status %u", status);
+ break;
+ }
+}
+
+static void handle_call_status(DBusMessage *msg, const char *call_path)
+{
+ struct csd_call *call;
+ dbus_uint32_t status, cause_type, cause;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_UINT32, &status,
+ DBUS_TYPE_UINT32, &cause_type,
+ DBUS_TYPE_UINT32, &cause,
+ DBUS_TYPE_INVALID)) {
+ error("Unexpected paramters in Instance.CallStatus() signal");
+ return;
+ }
+
+ call = find_call(call_path);
+ if (!call) {
+ error("Didn't find any matching call object for %s",
+ call_path);
+ return;
+ }
+
+ if (status > 16) {
+ error("Invalid call status %u", status);
+ return;
+ }
+
+ call_set_status(call, status);
+}
+
+static void handle_conference(DBusMessage *msg, gboolean joined)
+{
+ const char *path;
+ struct csd_call *call;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID)) {
+ error("Unexpected parameters in Conference.%s",
+ dbus_message_get_member(msg));
+ return;
+ }
+
+ call = find_call(path);
+ if (!call) {
+ error("Conference signal for unknown call %s", path);
+ return;
+ }
+
+ DBG("Call %s %s the conference", path, joined ? "joined" : "left");
+
+ call->conference = joined;
+}
+
+static uint8_t str2status(const char *state)
+{
+ if (g_strcmp0(state, "Home") == 0)
+ return NETWORK_REG_STATUS_HOME;
+ else if (g_strcmp0(state, "Roaming") == 0)
+ return NETWORK_REG_STATUS_ROAMING;
+ else if (g_strcmp0(state, "Offline") == 0)
+ return NETWORK_REG_STATUS_OFFLINE;
+ else if (g_strcmp0(state, "Searching") == 0)
+ return NETWORK_REG_STATUS_SEARCHING;
+ else if (g_strcmp0(state, "NoSim") == 0)
+ return NETWORK_REG_STATUS_NO_SIM;
+ else if (g_strcmp0(state, "Poweroff") == 0)
+ return NETWORK_REG_STATUS_POWEROFF;
+ else if (g_strcmp0(state, "Powersafe") == 0)
+ return NETWORK_REG_STATUS_POWERSAFE;
+ else if (g_strcmp0(state, "NoCoverage") == 0)
+ return NETWORK_REG_STATUS_NO_COVERAGE;
+ else if (g_strcmp0(state, "Reject") == 0)
+ return NETWORK_REG_STATUS_REJECTED;
+ else
+ return NETWORK_REG_STATUS_UNKOWN;
+}
+
+static void update_registration_status(const char *status)
+{
+ uint8_t new_status;
+
+ new_status = str2status(status);
+
+ if (net.status == new_status)
+ return;
+
+ switch (new_status) {
+ case NETWORK_REG_STATUS_HOME:
+ telephony_update_indicator(maemo_indicators, "roam",
+ EV_ROAM_INACTIVE);
+ if (net.status > NETWORK_REG_STATUS_ROAMING)
+ telephony_update_indicator(maemo_indicators,
+ "service",
+ EV_SERVICE_PRESENT);
+ break;
+ case NETWORK_REG_STATUS_ROAMING:
+ telephony_update_indicator(maemo_indicators, "roam",
+ EV_ROAM_ACTIVE);
+ if (net.status > NETWORK_REG_STATUS_ROAMING)
+ telephony_update_indicator(maemo_indicators,
+ "service",
+ EV_SERVICE_PRESENT);
+ break;
+ case NETWORK_REG_STATUS_OFFLINE:
+ case NETWORK_REG_STATUS_SEARCHING:
+ case NETWORK_REG_STATUS_NO_SIM:
+ case NETWORK_REG_STATUS_POWEROFF:
+ case NETWORK_REG_STATUS_POWERSAFE:
+ case NETWORK_REG_STATUS_NO_COVERAGE:
+ case NETWORK_REG_STATUS_REJECTED:
+ case NETWORK_REG_STATUS_UNKOWN:
+ if (net.status < NETWORK_REG_STATUS_OFFLINE)
+ telephony_update_indicator(maemo_indicators,
+ "service",
+ EV_SERVICE_NONE);
+ break;
+ }
+
+ net.status = new_status;
+
+ DBG("telephony-maemo6: registration status changed: %s", status);
+}
+
+static void handle_registration_changed(DBusMessage *msg)
+{
+ const char *status;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &status,
+ DBUS_TYPE_INVALID)) {
+ error("Unexpected parameters in RegistrationChanged");
+ return;
+ }
+
+ update_registration_status(status);
+}
+
+static void update_signal_strength(int32_t signal_bars)
+{
+ if (signal_bars < 0) {
+ DBG("signal strength smaller than expected: %d < 0",
+ signal_bars);
+ signal_bars = 0;
+ } else if (signal_bars > 5) {
+ DBG("signal strength greater than expected: %d > 5",
+ signal_bars);
+ signal_bars = 5;
+ }
+
+ if (net.signal_bars == signal_bars)
+ return;
+
+ telephony_update_indicator(maemo_indicators, "signal", signal_bars);
+
+ net.signal_bars = signal_bars;
+ DBG("telephony-maemo6: signal strength updated: %d/5", signal_bars);
+}
+
+static void handle_signal_bars_changed(DBusMessage *msg)
+{
+ int32_t signal_bars;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_INT32, &signal_bars,
+ DBUS_TYPE_INVALID)) {
+ error("Unexpected parameters in SignalBarsChanged");
+ return;
+ }
+
+ update_signal_strength(signal_bars);
+}
+
+static gboolean iter_get_basic_args(DBusMessageIter *iter,
+ int first_arg_type, ...)
+{
+ int type;
+ va_list ap;
+
+ va_start(ap, first_arg_type);
+
+ for (type = first_arg_type; type != DBUS_TYPE_INVALID;
+ type = va_arg(ap, int)) {
+ void *value = va_arg(ap, void *);
+ int real_type = dbus_message_iter_get_arg_type(iter);
+
+ if (real_type != type) {
+ error("iter_get_basic_args: expected %c but got %c",
+ (char) type, (char) real_type);
+ break;
+ }
+
+ dbus_message_iter_get_basic(iter, value);
+ dbus_message_iter_next(iter);
+ }
+
+ va_end(ap);
+
+ return type == DBUS_TYPE_INVALID ? TRUE : FALSE;
+}
+
+static void hal_battery_level_reply(DBusPendingCall *call, void *user_data)
+{
+ DBusError err;
+ DBusMessage *reply;
+ dbus_int32_t level;
+ int *value = user_data;
+
+ reply = dbus_pending_call_steal_reply(call);
+
+ dbus_error_init(&err);
+ if (dbus_set_error_from_message(&err, reply)) {
+ error("hald replied with an error: %s, %s",
+ err.name, err.message);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ if (!dbus_message_get_args(reply, NULL,
+ DBUS_TYPE_INT32, &level,
+ DBUS_TYPE_INVALID)) {
+ error("Unexpected args in hald reply");
+ goto done;
+ }
+
+ *value = (int) level;
+
+ if (value == &battchg_last)
+ DBG("telephony-maemo6: battery.charge_level.last_full is %d",
+ *value);
+ else if (value == &battchg_design)
+ DBG("telephony-maemo6: battery.charge_level.design is %d",
+ *value);
+ else
+ DBG("telephony-maemo6: battery.charge_level.current is %d",
+ *value);
+
+ if ((battchg_design > 0 || battchg_last > 0) && battchg_cur >= 0) {
+ int new, max;
+
+ if (battchg_last > 0)
+ max = battchg_last;
+ else
+ max = battchg_design;
+
+ new = battchg_cur * 5 / max;
+
+ telephony_update_indicator(maemo_indicators, "battchg", new);
+ }
+
+done:
+ dbus_message_unref(reply);
+ remove_pending(call);
+}
+
+static void hal_get_integer(const char *path, const char *key, void *user_data)
+{
+ send_method_call("org.freedesktop.Hal", path,
+ "org.freedesktop.Hal.Device",
+ "GetPropertyInteger",
+ hal_battery_level_reply, user_data,
+ DBUS_TYPE_STRING, &key,
+ DBUS_TYPE_INVALID);
+}
+
+static void handle_hal_property_modified(DBusMessage *msg)
+{
+ DBusMessageIter iter, array;
+ dbus_int32_t num_changes;
+ const char *path;
+
+ path = dbus_message_get_path(msg);
+
+ dbus_message_iter_init(msg, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INT32) {
+ error("Unexpected signature in hal PropertyModified signal");
+ return;
+ }
+
+ dbus_message_iter_get_basic(&iter, &num_changes);
+ dbus_message_iter_next(&iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+ error("Unexpected signature in hal PropertyModified signal");
+ return;
+ }
+
+ dbus_message_iter_recurse(&iter, &array);
+
+ while (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_INVALID) {
+ DBusMessageIter prop;
+ const char *name;
+ dbus_bool_t added, removed;
+
+ dbus_message_iter_recurse(&array, &prop);
+
+ if (!iter_get_basic_args(&prop,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_BOOLEAN, &added,
+ DBUS_TYPE_BOOLEAN, &removed,
+ DBUS_TYPE_INVALID)) {
+ error("Invalid hal PropertyModified parameters");
+ break;
+ }
+
+ if (g_str_equal(name, "battery.charge_level.last_full"))
+ hal_get_integer(path, name, &battchg_last);
+ else if (g_str_equal(name, "battery.charge_level.current"))
+ hal_get_integer(path, name, &battchg_cur);
+ else if (g_str_equal(name, "battery.charge_level.design"))
+ hal_get_integer(path, name, &battchg_design);
+
+ dbus_message_iter_next(&array);
+ }
+}
+
+static void csd_call_free(struct csd_call *call)
+{
+ if (!call)
+ return;
+
+ g_free(call->object_path);
+ g_free(call->number);
+
+ g_free(call);
+}
+
+static void parse_call_list(DBusMessageIter *iter)
+{
+ do {
+ DBusMessageIter call_iter;
+ struct csd_call *call;
+ const char *object_path, *number;
+ dbus_uint32_t status;
+ dbus_bool_t originating, terminating, emerg, on_hold, conf;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRUCT) {
+ error("Unexpected signature in GetCallInfoAll reply");
+ break;
+ }
+
+ dbus_message_iter_recurse(iter, &call_iter);
+
+ if (!iter_get_basic_args(&call_iter,
+ DBUS_TYPE_OBJECT_PATH, &object_path,
+ DBUS_TYPE_UINT32, &status,
+ DBUS_TYPE_BOOLEAN, &originating,
+ DBUS_TYPE_BOOLEAN, &terminating,
+ DBUS_TYPE_BOOLEAN, &emerg,
+ DBUS_TYPE_BOOLEAN, &on_hold,
+ DBUS_TYPE_BOOLEAN, &conf,
+ DBUS_TYPE_STRING, &number,
+ DBUS_TYPE_INVALID)) {
+ error("Parsing call D-Bus parameters failed");
+ break;
+ }
+
+ call = find_call(object_path);
+ if (!call) {
+ call = g_new0(struct csd_call, 1);
+ call->object_path = g_strdup(object_path);
+ calls = g_slist_append(calls, call);
+ DBG("telephony-maemo6: new csd call instance at %s",
+ object_path);
+ }
+
+ if (status == CSD_CALL_STATUS_IDLE)
+ continue;
+
+ /* CSD gives incorrect call_hold property sometimes */
+ if ((call->status != CSD_CALL_STATUS_HOLD && on_hold) ||
+ (call->status == CSD_CALL_STATUS_HOLD &&
+ !on_hold)) {
+ error("Conflicting call status and on_hold property!");
+ on_hold = call->status == CSD_CALL_STATUS_HOLD;
+ }
+
+ call->originating = originating;
+ call->on_hold = on_hold;
+ call->conference = conf;
+ g_free(call->number);
+ call->number = g_strdup(number);
+
+ /* Update indicators */
+ call_set_status(call, status);
+
+ } while (dbus_message_iter_next(iter));
+}
+
+static void update_operator_name(const char *name)
+{
+ if (name == NULL)
+ return;
+
+ g_free(net.operator_name);
+ net.operator_name = g_strndup(name, 16);
+ DBG("telephony-maemo6: operator name updated: %s", name);
+}
+
+static void get_property_reply(DBusPendingCall *call, void *user_data)
+{
+ char *prop = user_data;
+ DBusError err;
+ DBusMessage *reply;
+ DBusMessageIter iter, sub;
+
+ reply = dbus_pending_call_steal_reply(call);
+
+ dbus_error_init(&err);
+ if (dbus_set_error_from_message(&err, reply)) {
+ error("csd replied with an error: %s, %s",
+ err.name, err.message);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ dbus_message_iter_init(reply, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
+ error("Unexpected signature in Get return");
+ goto done;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ if (g_strcmp0(prop, "RegistrationStatus") == 0) {
+ const char *status;
+
+ dbus_message_iter_get_basic(&sub, &status);
+ update_registration_status(status);
+
+ get_property(CSD_CSNET_OPERATOR, "OperatorName");
+ get_property(CSD_CSNET_SIGNAL, "SignalBars");
+ } else if (g_strcmp0(prop, "OperatorName") == 0) {
+ const char *name;
+
+ dbus_message_iter_get_basic(&sub, &name);
+ update_operator_name(name);
+ } else if (g_strcmp0(prop, "SignalBars") == 0) {
+ int32_t signal_bars;
+
+ dbus_message_iter_get_basic(&sub, &signal_bars);
+ update_signal_strength(signal_bars);
+ }
+
+done:
+ g_free(prop);
+ dbus_message_unref(reply);
+ remove_pending(call);
+}
+
+static int get_property(const char *iface, const char *prop)
+{
+ return send_method_call(CSD_CSNET_BUS_NAME, CSD_CSNET_PATH,
+ DBUS_INTERFACE_PROPERTIES, "Get",
+ get_property_reply, g_strdup(prop),
+ DBUS_TYPE_STRING, &iface,
+ DBUS_TYPE_STRING, &prop,
+ DBUS_TYPE_INVALID);
+}
+
+static void handle_operator_name_changed(DBusMessage *msg)
+{
+ const char *name;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID)) {
+ error("Unexpected parameters in OperatorNameChanged");
+ return;
+ }
+
+ update_operator_name(name);
+}
+
+static void call_info_reply(DBusPendingCall *call, void *user_data)
+{
+ DBusError err;
+ DBusMessage *reply;
+ DBusMessageIter iter, sub;;
+
+ get_calls_active = FALSE;
+
+ reply = dbus_pending_call_steal_reply(call);
+
+ dbus_error_init(&err);
+ if (dbus_set_error_from_message(&err, reply)) {
+ error("csd replied with an error: %s, %s",
+ err.name, err.message);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ dbus_message_iter_init(reply, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+ error("Unexpected signature in GetCallInfoAll return");
+ goto done;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ parse_call_list(&sub);
+
+ get_property(CSD_CSNET_REGISTRATION, "RegistrationStatus");
+
+done:
+ dbus_message_unref(reply);
+ remove_pending(call);
+}
+
+
+static void phonebook_read_reply(DBusPendingCall *call, void *user_data)
+{
+ DBusError derr;
+ DBusMessage *reply;
+ const char *name, *number, *secondname, *additionalnumber, *email;
+ int index;
+ char **number_type = user_data;
+
+ reply = dbus_pending_call_steal_reply(call);
+
+ dbus_error_init(&derr);
+ if (dbus_set_error_from_message(&derr, reply)) {
+ error("%s.ReadFirst replied with an error: %s, %s",
+ CSD_SIMPB_INTERFACE, derr.name, derr.message);
+ dbus_error_free(&derr);
+ if (number_type == &vmbx)
+ vmbx = g_strdup(getenv("VMBX_NUMBER"));
+ goto done;
+ }
+
+ dbus_error_init(&derr);
+ if (dbus_message_get_args(reply, NULL,
+ DBUS_TYPE_INT32, &index,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_STRING, &number,
+ DBUS_TYPE_STRING, &secondname,
+ DBUS_TYPE_STRING, &additionalnumber,
+ DBUS_TYPE_STRING, &email,
+ DBUS_TYPE_INVALID) == FALSE) {
+ error("Unable to parse %s.ReadFirst arguments: %s, %s",
+ CSD_SIMPB_INTERFACE, derr.name, derr.message);
+ dbus_error_free(&derr);
+ goto done;
+ }
+
+ if (number_type == &msisdn) {
+ g_free(msisdn);
+ msisdn = g_strdup(number);
+ DBG("Got MSISDN %s (%s)", number, name);
+ } else {
+ g_free(vmbx);
+ vmbx = g_strdup(number);
+ DBG("Got voice mailbox number %s (%s)", number, name);
+ }
+
+done:
+ dbus_message_unref(reply);
+ remove_pending(call);
+}
+
+static void csd_init(void)
+{
+ const char *pb_type;
+ int ret;
+
+ ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+ CSD_CALL_INTERFACE, "GetCallInfoAll",
+ call_info_reply, NULL, DBUS_TYPE_INVALID);
+ if (ret < 0) {
+ error("Unable to sent GetCallInfoAll method call");
+ return;
+ }
+
+ get_calls_active = TRUE;
+
+ pb_type = CSD_SIMPB_TYPE_MSISDN;
+
+ ret = send_method_call(CSD_SIMPB_BUS_NAME, CSD_SIMPB_PATH,
+ CSD_SIMPB_INTERFACE, "ReadFirst",
+ phonebook_read_reply, &msisdn,
+ DBUS_TYPE_STRING, &pb_type,
+ DBUS_TYPE_INVALID);
+ if (ret < 0) {
+ error("Unable to send " CSD_SIMPB_INTERFACE ".read()");
+ return;
+ }
+
+ /* Voicemail should be in MBDN index 0 */
+ pb_type = CSD_SIMPB_TYPE_MBDN;
+
+ ret = send_method_call(CSD_SIMPB_BUS_NAME, CSD_SIMPB_PATH,
+ CSD_SIMPB_INTERFACE, "ReadFirst",
+ phonebook_read_reply, &vmbx,
+ DBUS_TYPE_STRING, &pb_type,
+ DBUS_TYPE_INVALID);
+ if (ret < 0) {
+ error("Unable to send " CSD_SIMPB_INTERFACE ".read()");
+ return;
+ }
+}
+
+static void handle_modem_state(DBusMessage *msg)
+{
+ const char *state;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &state,
+ DBUS_TYPE_INVALID)) {
+ error("Unexpected modem state parameters");
+ return;
+ }
+
+ DBG("SSC modem state: %s", state);
+
+ if (calls != NULL || get_calls_active)
+ return;
+
+ if (g_str_equal(state, "cmt_ready") || g_str_equal(state, "online"))
+ csd_init();
+}
+
+static void modem_state_reply(DBusPendingCall *call, void *user_data)
+{
+ DBusMessage *reply = dbus_pending_call_steal_reply(call);
+ DBusError err;
+
+ dbus_error_init(&err);
+ if (dbus_set_error_from_message(&err, reply)) {
+ error("get_modem_state: %s, %s", err.name, err.message);
+ dbus_error_free(&err);
+ } else
+ handle_modem_state(reply);
+
+ dbus_message_unref(reply);
+ remove_pending(call);
+}
+
+static gboolean signal_filter(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ const char *path = dbus_message_get_path(msg);
+
+ if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE, "Coming"))
+ handle_incoming_call(msg);
+ else if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE, "Created"))
+ handle_outgoing_call(msg);
+ else if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE,
+ "CreateRequested"))
+ handle_create_requested(msg);
+ else if (dbus_message_is_signal(msg, CSD_CALL_INSTANCE, "CallStatus"))
+ handle_call_status(msg, path);
+ else if (dbus_message_is_signal(msg, CSD_CALL_CONFERENCE, "Joined"))
+ handle_conference(msg, TRUE);
+ else if (dbus_message_is_signal(msg, CSD_CALL_CONFERENCE, "Left"))
+ handle_conference(msg, FALSE);
+ else if (dbus_message_is_signal(msg, CSD_CSNET_REGISTRATION,
+ "RegistrationChanged"))
+ handle_registration_changed(msg);
+ else if (dbus_message_is_signal(msg, CSD_CSNET_OPERATOR,
+ "OperatorNameChanged"))
+ handle_operator_name_changed(msg);
+ else if (dbus_message_is_signal(msg, CSD_CSNET_SIGNAL,
+ "SignalBarsChanged"))
+ handle_signal_bars_changed(msg);
+ else if (dbus_message_is_signal(msg, "org.freedesktop.Hal.Device",
+ "PropertyModified"))
+ handle_hal_property_modified(msg);
+ else if (dbus_message_is_signal(msg, SSC_DBUS_IFACE,
+ "modem_state_changed_ind"))
+ handle_modem_state(msg);
+
+ return TRUE;
+}
+
+static void add_watch(const char *sender, const char *path,
+ const char *interface, const char *member)
+{
+ guint watch;
+
+ watch = g_dbus_add_signal_watch(connection, sender, path, interface,
+ member, signal_filter, NULL, NULL);
+
+ watches = g_slist_prepend(watches, GUINT_TO_POINTER(watch));
+}
+
+static void hal_find_device_reply(DBusPendingCall *call, void *user_data)
+{
+ DBusError err;
+ DBusMessage *reply;
+ DBusMessageIter iter, sub;
+ const char *path;
+ int type;
+
+ reply = dbus_pending_call_steal_reply(call);
+
+ dbus_error_init(&err);
+ if (dbus_set_error_from_message(&err, reply)) {
+ error("hald replied with an error: %s, %s",
+ err.name, err.message);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ dbus_message_iter_init(reply, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+ error("Unexpected signature in FindDeviceByCapability return");
+ goto done;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ type = dbus_message_iter_get_arg_type(&sub);
+
+ if (type != DBUS_TYPE_OBJECT_PATH && type != DBUS_TYPE_STRING) {
+ error("No hal device with battery capability found");
+ goto done;
+ }
+
+ dbus_message_iter_get_basic(&sub, &path);
+
+ DBG("telephony-maemo6: found battery device at %s", path);
+
+ add_watch(NULL, path, "org.freedesktop.Hal.Device",
+ "PropertyModified");
+
+ hal_get_integer(path, "battery.charge_level.last_full", &battchg_last);
+ hal_get_integer(path, "battery.charge_level.current", &battchg_cur);
+ hal_get_integer(path, "battery.charge_level.design", &battchg_design);
+
+done:
+ dbus_message_unref(reply);
+ remove_pending(call);
+}
+
+int telephony_init(void)
+{
+ const char *battery_cap = "battery";
+ uint32_t features = AG_FEATURE_EC_ANDOR_NR |
+ AG_FEATURE_INBAND_RINGTONE |
+ AG_FEATURE_REJECT_A_CALL |
+ AG_FEATURE_ENHANCED_CALL_STATUS |
+ AG_FEATURE_ENHANCED_CALL_CONTROL |
+ AG_FEATURE_EXTENDED_ERROR_RESULT_CODES |
+ AG_FEATURE_THREE_WAY_CALLING;
+ int i;
+
+ DBG("");
+
+ connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+
+ add_watch(NULL, NULL, CSD_CALL_INTERFACE, NULL);
+ add_watch(NULL, NULL, CSD_CALL_INSTANCE, NULL);
+ add_watch(NULL, NULL, CSD_CALL_CONFERENCE, NULL);
+ add_watch(NULL, NULL, CSD_CSNET_REGISTRATION, "RegistrationChanged");
+ add_watch(NULL, NULL, CSD_CSNET_OPERATOR, "OperatorNameChanged");
+ add_watch(NULL, NULL, CSD_CSNET_SIGNAL, "SignalBarsChanged");
+ add_watch(NULL, NULL, SSC_DBUS_IFACE, "modem_state_changed_ind");
+
+ if (send_method_call(SSC_DBUS_NAME, SSC_DBUS_PATH, SSC_DBUS_IFACE,
+ "get_modem_state", modem_state_reply,
+ NULL, DBUS_TYPE_INVALID) < 0)
+ error("Unable to send " SSC_DBUS_IFACE ".get_modem_state()");
+
+ /* Reset indicators */
+ for (i = 0; maemo_indicators[i].desc != NULL; i++) {
+ if (g_str_equal(maemo_indicators[i].desc, "battchg"))
+ maemo_indicators[i].val = 5;
+ else
+ maemo_indicators[i].val = 0;
+ }
+
+ telephony_ready_ind(features, maemo_indicators, BTRH_NOT_SUPPORTED,
+ chld_str);
+ if (send_method_call("org.freedesktop.Hal",
+ "/org/freedesktop/Hal/Manager",
+ "org.freedesktop.Hal.Manager",
+ "FindDeviceByCapability",
+ hal_find_device_reply, NULL,
+ DBUS_TYPE_STRING, &battery_cap,
+ DBUS_TYPE_INVALID) < 0)
+ error("Unable to send HAL method call");
+
+ return 0;
+}
+
+static void remove_watch(gpointer data)
+{
+ g_dbus_remove_watch(connection, GPOINTER_TO_UINT(data));
+}
+
+void telephony_exit(void)
+{
+ DBG("");
+
+ g_free(net.operator_name);
+ net.operator_name = NULL;
+
+ net.status = NETWORK_REG_STATUS_UNKOWN;
+ net.signal_bars = 0;
+
+ g_slist_free(active_calls);
+ active_calls = NULL;
+
+ g_slist_foreach(calls, (GFunc) csd_call_free, NULL);
+ g_slist_free(calls);
+ calls = NULL;
+
+ g_slist_foreach(pending, (GFunc) pending_req_finalize, NULL);
+ g_slist_free(pending);
+ pending = NULL;
+
+ g_slist_foreach(watches, (GFunc) remove_watch, NULL);
+ g_slist_free(watches);
+ watches = NULL;
+
+ dbus_connection_unref(connection);
+ connection = NULL;
+
+ telephony_deinit();
+}
diff --git a/audio/telephony-ofono.c b/audio/telephony-ofono.c
new file mode 100644
index 0000000..6f5685b
--- /dev/null
+++ b/audio/telephony-ofono.c
@@ -0,0 +1,1627 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2009-2010 Intel Corporation
+ * Copyright (C) 2006-2009 Nokia Corporation
+ * Copyright (C) 2004-2010 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+#include "telephony.h"
+
+enum net_registration_status {
+ NETWORK_REG_STATUS_HOME = 0x00,
+ NETWORK_REG_STATUS_ROAM,
+ NETWORK_REG_STATUS_NOSERV
+};
+
+struct voice_call {
+ char *obj_path;
+ int status;
+ gboolean originating;
+ gboolean conference;
+ char *number;
+ guint watch;
+};
+
+static DBusConnection *connection = NULL;
+static char *modem_obj_path = NULL;
+static char *last_dialed_number = NULL;
+static GSList *calls = NULL;
+static GSList *watches = NULL;
+static GSList *pending = NULL;
+
+#define OFONO_BUS_NAME "org.ofono"
+#define OFONO_PATH "/"
+#define OFONO_MODEM_INTERFACE "org.ofono.Modem"
+#define OFONO_MANAGER_INTERFACE "org.ofono.Manager"
+#define OFONO_NETWORKREG_INTERFACE "org.ofono.NetworkRegistration"
+#define OFONO_VCMANAGER_INTERFACE "org.ofono.VoiceCallManager"
+#define OFONO_VC_INTERFACE "org.ofono.VoiceCall"
+
+/* HAL battery namespace key values */
+static int battchg_cur = -1; /* "battery.charge_level.current" */
+static int battchg_last = -1; /* "battery.charge_level.last_full" */
+static int battchg_design = -1; /* "battery.charge_level.design" */
+
+static struct {
+ uint8_t status;
+ uint32_t signals_bar;
+ char *operator_name;
+} net = {
+ .status = NETWORK_REG_STATUS_NOSERV,
+ .signals_bar = 0,
+ .operator_name = NULL,
+};
+
+static const char *chld_str = "0,1,1x,2,2x,3,4";
+static char *subscriber_number = NULL;
+
+static gboolean events_enabled = FALSE;
+
+static struct indicator ofono_indicators[] =
+{
+ { "battchg", "0-5", 5, TRUE },
+ { "signal", "0-5", 5, TRUE },
+ { "service", "0,1", 1, TRUE },
+ { "call", "0,1", 0, TRUE },
+ { "callsetup", "0-3", 0, TRUE },
+ { "callheld", "0-2", 0, FALSE },
+ { "roam", "0,1", 0, TRUE },
+ { NULL }
+};
+
+static struct voice_call *find_vc(const char *path)
+{
+ GSList *l;
+
+ for (l = calls; l != NULL; l = l->next) {
+ struct voice_call *vc = l->data;
+
+ if (g_str_equal(vc->obj_path, path))
+ return vc;
+ }
+
+ return NULL;
+}
+
+static struct voice_call *find_vc_with_status(int status)
+{
+ GSList *l;
+
+ for (l = calls; l != NULL; l = l->next) {
+ struct voice_call *vc = l->data;
+
+ if (vc->status == status)
+ return vc;
+ }
+
+ return NULL;
+}
+
+static struct voice_call *find_vc_without_status(int status)
+{
+ GSList *l;
+
+ for (l = calls; l != NULL; l = l->next) {
+ struct voice_call *call = l->data;
+
+ if (call->status != status)
+ return call;
+ }
+
+ return NULL;
+}
+
+static int number_type(const char *number)
+{
+ if (number == NULL)
+ return NUMBER_TYPE_TELEPHONY;
+
+ if (number[0] == '+' || strncmp(number, "00", 2) == 0)
+ return NUMBER_TYPE_INTERNATIONAL;
+
+ return NUMBER_TYPE_TELEPHONY;
+}
+
+void telephony_device_connected(void *telephony_device)
+{
+ struct voice_call *coming;
+
+ DBG("telephony-ofono: device %p connected", telephony_device);
+
+ coming = find_vc_with_status(CALL_STATUS_ALERTING);
+ if (coming) {
+ if (find_vc_with_status(CALL_STATUS_ACTIVE))
+ telephony_call_waiting_ind(coming->number,
+ number_type(coming->number));
+ else
+ telephony_incoming_call_ind(coming->number,
+ number_type(coming->number));
+ }
+}
+
+void telephony_device_disconnected(void *telephony_device)
+{
+ DBG("telephony-ofono: device %p disconnected", telephony_device);
+ events_enabled = FALSE;
+}
+
+void telephony_event_reporting_req(void *telephony_device, int ind)
+{
+ events_enabled = ind == 1 ? TRUE : FALSE;
+
+ telephony_event_reporting_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_response_and_hold_req(void *telephony_device, int rh)
+{
+ telephony_response_and_hold_rsp(telephony_device,
+ CME_ERROR_NOT_SUPPORTED);
+}
+
+void telephony_last_dialed_number_req(void *telephony_device)
+{
+ DBG("telephony-ofono: last dialed number request");
+
+ if (last_dialed_number)
+ telephony_dial_number_req(telephony_device, last_dialed_number);
+ else
+ telephony_last_dialed_number_rsp(telephony_device,
+ CME_ERROR_NOT_ALLOWED);
+}
+
+static int send_method_call(const char *dest, const char *path,
+ const char *interface, const char *method,
+ DBusPendingCallNotifyFunction cb,
+ void *user_data, int type, ...)
+{
+ DBusMessage *msg;
+ DBusPendingCall *call;
+ va_list args;
+
+ msg = dbus_message_new_method_call(dest, path, interface, method);
+ if (!msg) {
+ error("Unable to allocate new D-Bus %s message", method);
+ return -ENOMEM;
+ }
+
+ va_start(args, type);
+
+ if (!dbus_message_append_args_valist(msg, type, args)) {
+ dbus_message_unref(msg);
+ va_end(args);
+ return -EIO;
+ }
+
+ va_end(args);
+
+ if (!cb) {
+ g_dbus_send_message(connection, msg);
+ return 0;
+ }
+
+ if (!dbus_connection_send_with_reply(connection, msg, &call, -1)) {
+ error("Sending %s failed", method);
+ dbus_message_unref(msg);
+ return -EIO;
+ }
+
+ dbus_pending_call_set_notify(call, cb, user_data, NULL);
+ pending = g_slist_prepend(pending, call);
+ dbus_message_unref(msg);
+
+ return 0;
+}
+
+static int answer_call(struct voice_call *vc)
+{
+ DBG("%s", vc->number);
+ return send_method_call(OFONO_BUS_NAME, vc->obj_path,
+ OFONO_VC_INTERFACE, "Answer",
+ NULL, NULL, DBUS_TYPE_INVALID);
+}
+
+static int release_call(struct voice_call *vc)
+{
+ DBG("%s", vc->number);
+ return send_method_call(OFONO_BUS_NAME, vc->obj_path,
+ OFONO_VC_INTERFACE, "Hangup",
+ NULL, NULL, DBUS_TYPE_INVALID);
+}
+
+static int release_answer_calls()
+{
+ DBG("");
+ return send_method_call(OFONO_BUS_NAME, modem_obj_path,
+ OFONO_VCMANAGER_INTERFACE,
+ "ReleaseAndAnswer",
+ NULL, NULL, DBUS_TYPE_INVALID);
+}
+
+static int split_call(struct voice_call *call)
+{
+ DBG("%s", call->number);
+ return send_method_call(OFONO_BUS_NAME, modem_obj_path,
+ OFONO_VCMANAGER_INTERFACE,
+ "PrivateChat",
+ NULL, NULL,
+ DBUS_TYPE_OBJECT_PATH,
+ call->obj_path,
+ DBUS_TYPE_INVALID);
+ return -1;
+}
+
+static int swap_calls(void)
+{
+ DBG("");
+ return send_method_call(OFONO_BUS_NAME, modem_obj_path,
+ OFONO_VCMANAGER_INTERFACE,
+ "SwapCalls",
+ NULL, NULL, DBUS_TYPE_INVALID);
+}
+
+static int create_conference(void)
+{
+ DBG("");
+ return send_method_call(OFONO_BUS_NAME, modem_obj_path,
+ OFONO_VCMANAGER_INTERFACE,
+ "CreateMultiparty",
+ NULL, NULL, DBUS_TYPE_INVALID);
+}
+
+static int release_conference(void)
+{
+ DBG("");
+ return send_method_call(OFONO_BUS_NAME, modem_obj_path,
+ OFONO_VCMANAGER_INTERFACE,
+ "HangupMultiparty",
+ NULL, NULL, DBUS_TYPE_INVALID);
+}
+
+static int call_transfer(void)
+{
+ DBG("");
+ return send_method_call(OFONO_BUS_NAME, modem_obj_path,
+ OFONO_VCMANAGER_INTERFACE,
+ "Transfer",
+ NULL, NULL, DBUS_TYPE_INVALID);
+}
+
+void telephony_terminate_call_req(void *telephony_device)
+{
+ struct voice_call *call;
+ struct voice_call *alerting;
+ int err;
+
+ call = find_vc_with_status(CALL_STATUS_ACTIVE);
+ if (!call)
+ call = calls->data;
+
+ if (!call) {
+ error("No active call");
+ telephony_terminate_call_rsp(telephony_device,
+ CME_ERROR_NOT_ALLOWED);
+ return;
+ }
+
+ alerting = find_vc_with_status(CALL_STATUS_ALERTING);
+ if (call->status == CALL_STATUS_HELD && alerting)
+ err = release_call(alerting);
+ else if (call->conference)
+ err = release_conference();
+ else
+ err = release_call(call);
+
+ if (err < 0)
+ telephony_terminate_call_rsp(telephony_device,
+ CME_ERROR_AG_FAILURE);
+ else
+ telephony_terminate_call_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_answer_call_req(void *telephony_device)
+{
+ struct voice_call *vc;
+ int ret;
+
+ vc = find_vc_with_status(CALL_STATUS_INCOMING);
+ if (!vc)
+ vc = find_vc_with_status(CALL_STATUS_ALERTING);
+
+ if (!vc)
+ vc = find_vc_with_status(CALL_STATUS_WAITING);
+
+ if (!vc) {
+ telephony_answer_call_rsp(telephony_device,
+ CME_ERROR_NOT_ALLOWED);
+ return;
+ }
+
+ ret = answer_call(vc);
+ if (ret < 0) {
+ telephony_answer_call_rsp(telephony_device,
+ CME_ERROR_AG_FAILURE);
+ return;
+ }
+
+ telephony_answer_call_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_dial_number_req(void *telephony_device, const char *number)
+{
+ const char *clir;
+ int ret;
+
+ DBG("telephony-ofono: dial request to %s", number);
+
+ if (!modem_obj_path) {
+ telephony_dial_number_rsp(telephony_device,
+ CME_ERROR_AG_FAILURE);
+ return;
+ }
+
+ if (!strncmp(number, "*31#", 4)) {
+ number += 4;
+ clir = "enabled";
+ } else if (!strncmp(number, "#31#", 4)) {
+ number += 4;
+ clir = "disabled";
+ } else
+ clir = "default";
+
+ ret = send_method_call(OFONO_BUS_NAME, modem_obj_path,
+ OFONO_VCMANAGER_INTERFACE,
+ "Dial", NULL, NULL,
+ DBUS_TYPE_STRING, &number,
+ DBUS_TYPE_STRING, &clir,
+ DBUS_TYPE_INVALID);
+
+ if (ret < 0)
+ telephony_dial_number_rsp(telephony_device,
+ CME_ERROR_AG_FAILURE);
+ else
+ telephony_dial_number_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_transmit_dtmf_req(void *telephony_device, char tone)
+{
+ char *tone_string;
+ int ret;
+
+ DBG("telephony-ofono: transmit dtmf: %c", tone);
+
+ if (!modem_obj_path) {
+ telephony_transmit_dtmf_rsp(telephony_device,
+ CME_ERROR_AG_FAILURE);
+ return;
+ }
+
+ tone_string = g_strdup_printf("%c", tone);
+ ret = send_method_call(OFONO_BUS_NAME, modem_obj_path,
+ OFONO_VCMANAGER_INTERFACE,
+ "SendTones", NULL, NULL,
+ DBUS_TYPE_STRING, &tone_string,
+ DBUS_TYPE_INVALID);
+ g_free(tone_string);
+
+ if (ret < 0)
+ telephony_transmit_dtmf_rsp(telephony_device,
+ CME_ERROR_AG_FAILURE);
+ else
+ telephony_transmit_dtmf_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_subscriber_number_req(void *telephony_device)
+{
+ DBG("telephony-ofono: subscriber number request");
+
+ if (subscriber_number)
+ telephony_subscriber_number_ind(subscriber_number,
+ NUMBER_TYPE_TELEPHONY,
+ SUBSCRIBER_SERVICE_VOICE);
+ telephony_subscriber_number_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_list_current_calls_req(void *telephony_device)
+{
+ GSList *l;
+ int i;
+
+ DBG("telephony-ofono: list current calls request");
+
+ for (l = calls, i = 1; l != NULL; l = l->next, i++) {
+ struct voice_call *vc = l->data;
+ int direction, multiparty;
+
+ direction = vc->originating ?
+ CALL_DIR_OUTGOING : CALL_DIR_INCOMING;
+
+ multiparty = vc->conference ?
+ CALL_MULTIPARTY_YES : CALL_MULTIPARTY_NO;
+
+ DBG("call %s direction %d multiparty %d", vc->number,
+ direction, multiparty);
+
+ telephony_list_current_call_ind(i, direction, vc->status,
+ CALL_MODE_VOICE, multiparty,
+ vc->number, number_type(vc->number));
+ }
+
+ telephony_list_current_calls_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_operator_selection_req(void *telephony_device)
+{
+ DBG("telephony-ofono: operator selection request");
+
+ telephony_operator_selection_ind(OPERATOR_MODE_AUTO,
+ net.operator_name ? net.operator_name : "");
+ telephony_operator_selection_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+static void foreach_vc_with_status(int status,
+ int (*func)(struct voice_call *vc))
+{
+ GSList *l;
+
+ for (l = calls; l != NULL; l = l->next) {
+ struct voice_call *call = l->data;
+
+ if (call->status == status)
+ func(call);
+ }
+}
+
+void telephony_call_hold_req(void *telephony_device, const char *cmd)
+{
+ const char *idx;
+ struct voice_call *call;
+ int err = 0;
+
+ DBG("telephony-ofono: got call hold request %s", cmd);
+
+ if (strlen(cmd) > 1)
+ idx = &cmd[1];
+ else
+ idx = NULL;
+
+ if (idx)
+ call = g_slist_nth_data(calls, strtol(idx, NULL, 0) - 1);
+ else
+ call = NULL;
+
+ switch (cmd[0]) {
+ case '0':
+ if (find_vc_with_status(CALL_STATUS_WAITING))
+ foreach_vc_with_status(CALL_STATUS_WAITING,
+ release_call);
+ else
+ foreach_vc_with_status(CALL_STATUS_HELD, release_call);
+ break;
+ case '1':
+ if (idx) {
+ if (call)
+ err = release_call(call);
+ break;
+ }
+ err = release_answer_calls();
+ break;
+ case '2':
+ if (idx) {
+ if (call)
+ err = split_call(call);
+ } else {
+ call = find_vc_with_status(CALL_STATUS_WAITING);
+
+ if (call)
+ err = answer_call(call);
+ else
+ err = swap_calls();
+ }
+ break;
+ case '3':
+ if (find_vc_with_status(CALL_STATUS_HELD) ||
+ find_vc_with_status(CALL_STATUS_WAITING))
+ err = create_conference();
+ break;
+ case '4':
+ err = call_transfer();
+ break;
+ default:
+ DBG("Unknown call hold request");
+ break;
+ }
+
+ if (err)
+ telephony_call_hold_rsp(telephony_device,
+ CME_ERROR_AG_FAILURE);
+ else
+ telephony_call_hold_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_nr_and_ec_req(void *telephony_device, gboolean enable)
+{
+ DBG("telephony-ofono: got %s NR and EC request",
+ enable ? "enable" : "disable");
+
+ telephony_nr_and_ec_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_key_press_req(void *telephony_device, const char *keys)
+{
+ struct voice_call *active, *incoming;
+ int err;
+
+ DBG("telephony-ofono: got key press request for %s", keys);
+
+ incoming = find_vc_with_status(CALL_STATUS_INCOMING);
+
+ active = find_vc_with_status(CALL_STATUS_ACTIVE);
+
+ if (incoming)
+ err = answer_call(incoming);
+ else if (active)
+ err = release_call(active);
+ else
+ err = 0;
+
+ if (err < 0)
+ telephony_key_press_rsp(telephony_device,
+ CME_ERROR_AG_FAILURE);
+ else
+ telephony_key_press_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_voice_dial_req(void *telephony_device, gboolean enable)
+{
+ DBG("telephony-ofono: got %s voice dial request",
+ enable ? "enable" : "disable");
+
+ telephony_voice_dial_rsp(telephony_device, CME_ERROR_NOT_SUPPORTED);
+}
+
+static gboolean iter_get_basic_args(DBusMessageIter *iter,
+ int first_arg_type, ...)
+{
+ int type;
+ va_list ap;
+
+ va_start(ap, first_arg_type);
+
+ for (type = first_arg_type; type != DBUS_TYPE_INVALID;
+ type = va_arg(ap, int)) {
+ void *value = va_arg(ap, void *);
+ int real_type = dbus_message_iter_get_arg_type(iter);
+
+ if (real_type != type) {
+ error("iter_get_basic_args: expected %c but got %c",
+ (char) type, (char) real_type);
+ break;
+ }
+
+ dbus_message_iter_get_basic(iter, value);
+ dbus_message_iter_next(iter);
+ }
+
+ va_end(ap);
+
+ return type == DBUS_TYPE_INVALID ? TRUE : FALSE;
+}
+
+static void call_free(struct voice_call *vc)
+{
+ DBG("%s", vc->obj_path);
+
+ if (vc->status == CALL_STATUS_ACTIVE)
+ telephony_update_indicator(ofono_indicators, "call",
+ EV_CALL_INACTIVE);
+ else
+ telephony_update_indicator(ofono_indicators, "callsetup",
+ EV_CALLSETUP_INACTIVE);
+
+ if (vc->status == CALL_STATUS_INCOMING)
+ telephony_calling_stopped_ind();
+
+ g_dbus_remove_watch(connection, vc->watch);
+ g_free(vc->obj_path);
+ g_free(vc->number);
+ g_free(vc);
+}
+
+static gboolean handle_vc_property_changed(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct voice_call *vc = data;
+ const char *obj_path = dbus_message_get_path(msg);
+ DBusMessageIter iter, sub;
+ const char *property, *state;
+
+ DBG("path %s", obj_path);
+
+ dbus_message_iter_init(msg, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
+ error("Unexpected signature in vc PropertyChanged signal");
+ return TRUE;
+ }
+
+ dbus_message_iter_get_basic(&iter, &property);
+ DBG("property %s", property);
+
+ dbus_message_iter_next(&iter);
+ dbus_message_iter_recurse(&iter, &sub);
+ if (g_str_equal(property, "State")) {
+ dbus_message_iter_get_basic(&sub, &state);
+ DBG("State %s", state);
+ if (g_str_equal(state, "disconnected")) {
+ calls = g_slist_remove(calls, vc);
+ call_free(vc);
+ } else if (g_str_equal(state, "active")) {
+ telephony_update_indicator(ofono_indicators,
+ "call", EV_CALL_ACTIVE);
+ telephony_update_indicator(ofono_indicators,
+ "callsetup",
+ EV_CALLSETUP_INACTIVE);
+ if (vc->status == CALL_STATUS_INCOMING)
+ telephony_calling_stopped_ind();
+ vc->status = CALL_STATUS_ACTIVE;
+ } else if (g_str_equal(state, "alerting")) {
+ telephony_update_indicator(ofono_indicators,
+ "callsetup", EV_CALLSETUP_ALERTING);
+ vc->status = CALL_STATUS_ALERTING;
+ vc->originating = TRUE;
+ } else if (g_str_equal(state, "incoming")) {
+ /* state change from waiting to incoming */
+ telephony_update_indicator(ofono_indicators,
+ "callsetup", EV_CALLSETUP_INCOMING);
+ telephony_incoming_call_ind(vc->number,
+ NUMBER_TYPE_TELEPHONY);
+ vc->status = CALL_STATUS_INCOMING;
+ vc->originating = FALSE;
+ } else if (g_str_equal(state, "held")) {
+ vc->status = CALL_STATUS_HELD;
+ if (find_vc_without_status(CALL_STATUS_HELD))
+ telephony_update_indicator(ofono_indicators,
+ "callheld",
+ EV_CALLHELD_MULTIPLE);
+ else
+ telephony_update_indicator(ofono_indicators,
+ "callheld",
+ EV_CALLHELD_ON_HOLD);
+ }
+ } else if (g_str_equal(property, "Multiparty")) {
+ dbus_bool_t multiparty;
+
+ dbus_message_iter_get_basic(&sub, &multiparty);
+ DBG("Multiparty %s", multiparty ? "True" : "False");
+ vc->conference = multiparty;
+ }
+
+ return TRUE;
+}
+
+static struct voice_call *call_new(const char *path, DBusMessageIter *properties)
+{
+ struct voice_call *vc;
+
+ DBG("%s", path);
+
+ vc = g_new0(struct voice_call, 1);
+ vc->obj_path = g_strdup(path);
+ vc->watch = g_dbus_add_signal_watch(connection, NULL, path,
+ OFONO_VC_INTERFACE, "PropertyChanged",
+ handle_vc_property_changed, vc, NULL);
+
+ while (dbus_message_iter_get_arg_type(properties)
+ == DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter entry, value;
+ const char *property, *cli, *state;
+ dbus_bool_t multiparty;
+
+ dbus_message_iter_recurse(properties, &entry);
+ dbus_message_iter_get_basic(&entry, &property);
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_recurse(&entry, &value);
+
+ if (g_str_equal(property, "LineIdentification")) {
+ dbus_message_iter_get_basic(&value, &cli);
+ DBG("cli %s", cli);
+ vc->number = g_strdup(cli);
+ } else if (g_str_equal(property, "State")) {
+ dbus_message_iter_get_basic(&value, &state);
+ DBG("state %s", state);
+ if (g_str_equal(state, "incoming"))
+ vc->status = CALL_STATUS_INCOMING;
+ else if (g_str_equal(state, "dialing"))
+ vc->status = CALL_STATUS_DIALING;
+ else if (g_str_equal(state, "alerting"))
+ vc->status = CALL_STATUS_ALERTING;
+ else if (g_str_equal(state, "waiting"))
+ vc->status = CALL_STATUS_WAITING;
+ else if (g_str_equal(state, "held"))
+ vc->status = CALL_STATUS_HELD;
+ } else if (g_str_equal(property, "Multiparty")) {
+ dbus_message_iter_get_basic(&value, &multiparty);
+ DBG("Multipary %s", multiparty ? "True" : "False");
+ vc->conference = multiparty;
+ }
+
+ dbus_message_iter_next(properties);
+ }
+
+ switch (vc->status) {
+ case CALL_STATUS_INCOMING:
+ DBG("CALL_STATUS_INCOMING");
+ vc->originating = FALSE;
+ telephony_update_indicator(ofono_indicators, "callsetup",
+ EV_CALLSETUP_INCOMING);
+ telephony_incoming_call_ind(vc->number, NUMBER_TYPE_TELEPHONY);
+ break;
+ case CALL_STATUS_DIALING:
+ DBG("CALL_STATUS_DIALING");
+ vc->originating = TRUE;
+ g_free(last_dialed_number);
+ last_dialed_number = g_strdup(vc->number);
+ telephony_update_indicator(ofono_indicators, "callsetup",
+ EV_CALLSETUP_OUTGOING);
+ break;
+ case CALL_STATUS_ALERTING:
+ DBG("CALL_STATUS_ALERTING");
+ vc->originating = TRUE;
+ g_free(last_dialed_number);
+ last_dialed_number = g_strdup(vc->number);
+ telephony_update_indicator(ofono_indicators, "callsetup",
+ EV_CALLSETUP_ALERTING);
+ break;
+ case CALL_STATUS_WAITING:
+ DBG("CALL_STATUS_WAITING");
+ vc->originating = FALSE;
+ telephony_update_indicator(ofono_indicators, "callsetup",
+ EV_CALLSETUP_INCOMING);
+ telephony_call_waiting_ind(vc->number, NUMBER_TYPE_TELEPHONY);
+ break;
+ }
+
+ return vc;
+}
+
+static void remove_pending(DBusPendingCall *call)
+{
+ pending = g_slist_remove(pending, call);
+ dbus_pending_call_unref(call);
+}
+
+static void call_added(const char *path, DBusMessageIter *properties)
+{
+ struct voice_call *vc;
+
+ DBG("%s", path);
+
+ vc = find_vc(path);
+ if (vc)
+ return;
+
+ vc = call_new(path, properties);
+ calls = g_slist_prepend(calls, vc);
+}
+
+static void get_calls_reply(DBusPendingCall *call, void *user_data)
+{
+ DBusError err;
+ DBusMessage *reply;
+ DBusMessageIter iter, entry;
+
+ DBG("");
+ reply = dbus_pending_call_steal_reply(call);
+
+ dbus_error_init(&err);
+ if (dbus_set_error_from_message(&err, reply)) {
+ error("ofono replied with an error: %s, %s",
+ err.name, err.message);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ dbus_message_iter_init(reply, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+ error("Unexpected signature");
+ goto done;
+ }
+
+ dbus_message_iter_recurse(&iter, &entry);
+
+ while (dbus_message_iter_get_arg_type(&entry)
+ == DBUS_TYPE_STRUCT) {
+ const char *path;
+ DBusMessageIter value, properties;
+
+ dbus_message_iter_recurse(&entry, &value);
+ dbus_message_iter_get_basic(&value, &path);
+
+ dbus_message_iter_next(&value);
+ dbus_message_iter_recurse(&value, &properties);
+
+ call_added(path, &properties);
+
+ dbus_message_iter_next(&entry);
+ }
+
+done:
+ dbus_message_unref(reply);
+ remove_pending(call);
+}
+
+static void handle_network_property(const char *property, DBusMessageIter *variant)
+{
+ const char *status, *operator;
+ unsigned int signals_bar;
+
+ if (g_str_equal(property, "Status")) {
+ dbus_message_iter_get_basic(variant, &status);
+ DBG("Status is %s", status);
+ if (g_str_equal(status, "registered")) {
+ net.status = NETWORK_REG_STATUS_HOME;
+ telephony_update_indicator(ofono_indicators,
+ "roam", EV_ROAM_INACTIVE);
+ telephony_update_indicator(ofono_indicators,
+ "service", EV_SERVICE_PRESENT);
+ } else if (g_str_equal(status, "roaming")) {
+ net.status = NETWORK_REG_STATUS_ROAM;
+ telephony_update_indicator(ofono_indicators,
+ "roam", EV_ROAM_ACTIVE);
+ telephony_update_indicator(ofono_indicators,
+ "service", EV_SERVICE_PRESENT);
+ } else {
+ net.status = NETWORK_REG_STATUS_NOSERV;
+ telephony_update_indicator(ofono_indicators,
+ "roam", EV_ROAM_INACTIVE);
+ telephony_update_indicator(ofono_indicators,
+ "service", EV_SERVICE_NONE);
+ }
+ } else if (g_str_equal(property, "Name")) {
+ dbus_message_iter_get_basic(variant, &operator);
+ DBG("Operator is %s", operator);
+ g_free(net.operator_name);
+ net.operator_name = g_strdup(operator);
+ } else if (g_str_equal(property, "SignalStrength")) {
+ dbus_message_iter_get_basic(variant, &signals_bar);
+ DBG("SignalStrength is %d", signals_bar);
+ net.signals_bar = signals_bar;
+ telephony_update_indicator(ofono_indicators, "signal",
+ (signals_bar + 20) / 21);
+ }
+}
+
+static int parse_network_properties(DBusMessageIter *properties)
+{
+ uint32_t features = AG_FEATURE_EC_ANDOR_NR |
+ AG_FEATURE_INBAND_RINGTONE |
+ AG_FEATURE_REJECT_A_CALL |
+ AG_FEATURE_ENHANCED_CALL_STATUS |
+ AG_FEATURE_ENHANCED_CALL_CONTROL |
+ AG_FEATURE_EXTENDED_ERROR_RESULT_CODES |
+ AG_FEATURE_THREE_WAY_CALLING;
+ int i;
+
+ /* Reset indicators */
+ for (i = 0; ofono_indicators[i].desc != NULL; i++) {
+ if (g_str_equal(ofono_indicators[i].desc, "battchg"))
+ ofono_indicators[i].val = 5;
+ else
+ ofono_indicators[i].val = 0;
+ }
+
+ while (dbus_message_iter_get_arg_type(properties)
+ == DBUS_TYPE_DICT_ENTRY) {
+ const char *key;
+ DBusMessageIter value, entry;
+
+ dbus_message_iter_recurse(properties, &entry);
+ dbus_message_iter_get_basic(&entry, &key);
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_recurse(&entry, &value);
+
+ handle_network_property(key, &value);
+
+ dbus_message_iter_next(properties);
+ }
+
+ telephony_ready_ind(features, ofono_indicators, BTRH_NOT_SUPPORTED,
+ chld_str);
+
+ return 0;
+}
+
+static void get_properties_reply(DBusPendingCall *call, void *user_data)
+{
+ DBusError err;
+ DBusMessage *reply;
+ DBusMessageIter iter, properties;
+ int ret = 0;
+
+ DBG("");
+ reply = dbus_pending_call_steal_reply(call);
+
+ dbus_error_init(&err);
+ if (dbus_set_error_from_message(&err, reply)) {
+ error("ofono replied with an error: %s, %s",
+ err.name, err.message);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ dbus_message_iter_init(reply, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+ error("Unexpected signature");
+ goto done;
+ }
+
+ dbus_message_iter_recurse(&iter, &properties);
+
+ ret = parse_network_properties(&properties);
+ if (ret < 0) {
+ error("Unable to parse %s.GetProperty reply",
+ OFONO_NETWORKREG_INTERFACE);
+ goto done;
+ }
+
+ ret = send_method_call(OFONO_BUS_NAME, modem_obj_path,
+ OFONO_VCMANAGER_INTERFACE, "GetCalls",
+ get_calls_reply, NULL, DBUS_TYPE_INVALID);
+ if (ret < 0)
+ error("Unable to send %s.GetCalls",
+ OFONO_VCMANAGER_INTERFACE);
+
+done:
+ dbus_message_unref(reply);
+ remove_pending(call);
+}
+
+static void network_found(const char *path)
+{
+ int ret;
+
+ DBG("%s", path);
+
+ modem_obj_path = g_strdup(path);
+
+ ret = send_method_call(OFONO_BUS_NAME, path,
+ OFONO_NETWORKREG_INTERFACE, "GetProperties",
+ get_properties_reply, NULL, DBUS_TYPE_INVALID);
+ if (ret < 0)
+ error("Unable to send %s.GetProperties",
+ OFONO_NETWORKREG_INTERFACE);
+}
+
+static void modem_removed(const char *path)
+{
+ if (g_strcmp0(modem_obj_path, path) != 0)
+ return;
+
+ DBG("%s", path);
+
+ g_slist_foreach(calls, (GFunc) call_free, NULL);
+ g_slist_free(calls);
+ calls = NULL;
+
+ g_free(net.operator_name);
+ net.operator_name = NULL;
+ net.status = NETWORK_REG_STATUS_NOSERV;
+ net.signals_bar = 0;
+
+ g_free(modem_obj_path);
+ modem_obj_path = NULL;
+}
+
+static void parse_modem_interfaces(const char *path, DBusMessageIter *ifaces)
+{
+ DBG("%s", path);
+
+ while (dbus_message_iter_get_arg_type(ifaces) == DBUS_TYPE_STRING) {
+ const char *iface;
+
+ dbus_message_iter_get_basic(ifaces, &iface);
+
+ if (g_str_equal(iface, OFONO_NETWORKREG_INTERFACE)) {
+ network_found(path);
+ return;
+ }
+
+ dbus_message_iter_next(ifaces);
+ }
+
+ modem_removed(path);
+}
+
+static void modem_added(const char *path, DBusMessageIter *properties)
+{
+ if (modem_obj_path != NULL) {
+ DBG("Ignoring, modem already exist");
+ return;
+ }
+
+ DBG("%s", path);
+
+ while (dbus_message_iter_get_arg_type(properties)
+ == DBUS_TYPE_DICT_ENTRY) {
+ const char *key;
+ DBusMessageIter interfaces, value, entry;
+
+ dbus_message_iter_recurse(properties, &entry);
+ dbus_message_iter_get_basic(&entry, &key);
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_recurse(&entry, &value);
+
+ if (strcasecmp(key, "Interfaces") != 0)
+ goto next;
+
+ if (dbus_message_iter_get_arg_type(&value)
+ != DBUS_TYPE_ARRAY) {
+ error("Invalid Signature");
+ return;
+ }
+
+ dbus_message_iter_recurse(&value, &interfaces);
+
+ parse_modem_interfaces(path, &interfaces);
+
+ if (modem_obj_path != NULL)
+ return;
+
+ next:
+ dbus_message_iter_next(properties);
+ }
+}
+
+static void get_modems_reply(DBusPendingCall *call, void *user_data)
+{
+ DBusError err;
+ DBusMessage *reply;
+ DBusMessageIter iter, entry;
+
+ DBG("");
+ reply = dbus_pending_call_steal_reply(call);
+
+ dbus_error_init(&err);
+ if (dbus_set_error_from_message(&err, reply)) {
+ error("ofono replied with an error: %s, %s",
+ err.name, err.message);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ /* Skip modem selection if a modem already exist */
+ if (modem_obj_path != NULL)
+ goto done;
+
+ dbus_message_iter_init(reply, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+ error("Unexpected signature");
+ goto done;
+ }
+
+ dbus_message_iter_recurse(&iter, &entry);
+
+ while (dbus_message_iter_get_arg_type(&entry)
+ == DBUS_TYPE_STRUCT) {
+ const char *path;
+ DBusMessageIter item, properties;
+
+ dbus_message_iter_recurse(&entry, &item);
+ dbus_message_iter_get_basic(&item, &path);
+
+ dbus_message_iter_next(&item);
+ dbus_message_iter_recurse(&item, &properties);
+
+ modem_added(path, &properties);
+ if (modem_obj_path != NULL)
+ break;
+
+ dbus_message_iter_next(&entry);
+ }
+
+done:
+ dbus_message_unref(reply);
+ remove_pending(call);
+}
+
+static gboolean handle_network_property_changed(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessageIter iter, variant;
+ const char *property;
+
+ dbus_message_iter_init(msg, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
+ error("Unexpected signature in networkregistration"
+ " PropertyChanged signal");
+ return TRUE;
+ }
+ dbus_message_iter_get_basic(&iter, &property);
+ DBG("in handle_registration_property_changed(),"
+ " the property is %s", property);
+
+ dbus_message_iter_next(&iter);
+ dbus_message_iter_recurse(&iter, &variant);
+
+ handle_network_property(property, &variant);
+
+ return TRUE;
+}
+
+static void handle_modem_property(const char *path, const char *property,
+ DBusMessageIter *variant)
+{
+ DBG("%s", property);
+
+ if (g_str_equal(property, "Interfaces")) {
+ DBusMessageIter interfaces;
+
+ if (dbus_message_iter_get_arg_type(variant)
+ != DBUS_TYPE_ARRAY) {
+ error("Invalid signature");
+ return;
+ }
+
+ dbus_message_iter_recurse(variant, &interfaces);
+ parse_modem_interfaces(path, &interfaces);
+ }
+}
+
+static gboolean handle_modem_property_changed(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessageIter iter, variant;
+ const char *property, *path;
+
+ path = dbus_message_get_path(msg);
+
+ /* Ignore if modem already exist and paths doesn't match */
+ if (modem_obj_path != NULL &&
+ g_str_equal(path, modem_obj_path) == FALSE)
+ return TRUE;
+
+ dbus_message_iter_init(msg, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
+ error("Unexpected signature in %s.%s PropertyChanged signal",
+ dbus_message_get_interface(msg),
+ dbus_message_get_member(msg));
+ return TRUE;
+ }
+
+ dbus_message_iter_get_basic(&iter, &property);
+
+ dbus_message_iter_next(&iter);
+ dbus_message_iter_recurse(&iter, &variant);
+
+ handle_modem_property(path, property, &variant);
+
+ return TRUE;
+}
+
+static gboolean handle_vcmanager_call_added(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessageIter iter, properties;
+ const char *path = dbus_message_get_path(msg);
+
+ /* Ignore call if modem path doesn't math */
+ if (g_strcmp0(modem_obj_path, path) != 0)
+ return TRUE;
+
+ dbus_message_iter_init(msg, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter)
+ != DBUS_TYPE_OBJECT_PATH) {
+ error("Unexpected signature in %s.%s signal",
+ dbus_message_get_interface(msg),
+ dbus_message_get_member(msg));
+ return TRUE;
+ }
+
+ dbus_message_iter_get_basic(&iter, &path);
+ dbus_message_iter_next(&iter);
+ dbus_message_iter_recurse(&iter, &properties);
+
+ call_added(path, &properties);
+
+ return TRUE;
+}
+
+static void call_removed(const char *path)
+{
+ struct voice_call *vc;
+
+ DBG("%s", path);
+
+ vc = find_vc(path);
+ if (vc == NULL)
+ return;
+
+ calls = g_slist_remove(calls, vc);
+ call_free(vc);
+}
+
+static gboolean handle_vcmanager_call_removed(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ const char *path = dbus_message_get_path(msg);
+
+ /* Ignore call if modem path doesn't math */
+ if (g_strcmp0(modem_obj_path, path) != 0)
+ return TRUE;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID)) {
+ error("Unexpected signature in %s.%s signal",
+ dbus_message_get_interface(msg),
+ dbus_message_get_member(msg));
+ return TRUE;
+ }
+
+ call_removed(path);
+
+ return TRUE;
+}
+
+static gboolean handle_manager_modem_added(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessageIter iter, properties;
+ const char *path;
+
+ if (modem_obj_path != NULL)
+ return TRUE;
+
+ dbus_message_iter_init(msg, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter)
+ != DBUS_TYPE_OBJECT_PATH) {
+ error("Unexpected signature in %s.%s signal",
+ dbus_message_get_interface(msg),
+ dbus_message_get_member(msg));
+ return TRUE;
+ }
+
+ dbus_message_iter_get_basic(&iter, &path);
+ dbus_message_iter_next(&iter);
+ dbus_message_iter_recurse(&iter, &properties);
+
+ modem_added(path, &properties);
+
+ return TRUE;
+}
+
+static gboolean handle_manager_modem_removed(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ const char *path;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID)) {
+ error("Unexpected signature in %s.%s signal",
+ dbus_message_get_interface(msg),
+ dbus_message_get_member(msg));
+ return TRUE;
+ }
+
+ modem_removed(path);
+
+ return TRUE;
+}
+
+static void hal_battery_level_reply(DBusPendingCall *call, void *user_data)
+{
+ DBusMessage *reply;
+ DBusError err;
+ dbus_int32_t level;
+ int *value = user_data;
+
+ reply = dbus_pending_call_steal_reply(call);
+
+ dbus_error_init(&err);
+ if (dbus_set_error_from_message(&err, reply)) {
+ error("hald replied with an error: %s, %s",
+ err.name, err.message);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ dbus_error_init(&err);
+ if (dbus_message_get_args(reply, &err,
+ DBUS_TYPE_INT32, &level,
+ DBUS_TYPE_INVALID) == FALSE) {
+ error("Unable to parse GetPropertyInteger reply: %s, %s",
+ err.name, err.message);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ *value = (int) level;
+
+ if (value == &battchg_last)
+ DBG("telephony-ofono: battery.charge_level.last_full"
+ " is %d", *value);
+ else if (value == &battchg_design)
+ DBG("telephony-ofono: battery.charge_level.design"
+ " is %d", *value);
+ else
+ DBG("telephony-ofono: battery.charge_level.current"
+ " is %d", *value);
+
+ if ((battchg_design > 0 || battchg_last > 0) && battchg_cur >= 0) {
+ int new, max;
+
+ if (battchg_last > 0)
+ max = battchg_last;
+ else
+ max = battchg_design;
+
+ new = battchg_cur * 5 / max;
+
+ telephony_update_indicator(ofono_indicators, "battchg", new);
+ }
+done:
+ dbus_message_unref(reply);
+ remove_pending(call);
+}
+
+static void hal_get_integer(const char *path, const char *key, void *user_data)
+{
+ send_method_call("org.freedesktop.Hal", path,
+ "org.freedesktop.Hal.Device",
+ "GetPropertyInteger",
+ hal_battery_level_reply, user_data,
+ DBUS_TYPE_STRING, &key,
+ DBUS_TYPE_INVALID);
+}
+
+static gboolean handle_hal_property_modified(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ const char *path;
+ DBusMessageIter iter, array;
+ dbus_int32_t num_changes;
+
+ path = dbus_message_get_path(msg);
+
+ dbus_message_iter_init(msg, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INT32) {
+ error("Unexpected signature in hal PropertyModified signal");
+ return TRUE;
+ }
+
+ dbus_message_iter_get_basic(&iter, &num_changes);
+ dbus_message_iter_next(&iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+ error("Unexpected signature in hal PropertyModified signal");
+ return TRUE;
+ }
+
+ dbus_message_iter_recurse(&iter, &array);
+
+ while (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_INVALID) {
+ DBusMessageIter prop;
+ const char *name;
+ dbus_bool_t added, removed;
+
+ dbus_message_iter_recurse(&array, &prop);
+
+ if (!iter_get_basic_args(&prop,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_BOOLEAN, &added,
+ DBUS_TYPE_BOOLEAN, &removed,
+ DBUS_TYPE_INVALID)) {
+ error("Invalid hal PropertyModified parameters");
+ break;
+ }
+
+ if (g_str_equal(name, "battery.charge_level.last_full"))
+ hal_get_integer(path, name, &battchg_last);
+ else if (g_str_equal(name, "battery.charge_level.current"))
+ hal_get_integer(path, name, &battchg_cur);
+ else if (g_str_equal(name, "battery.charge_level.design"))
+ hal_get_integer(path, name, &battchg_design);
+
+ dbus_message_iter_next(&array);
+ }
+
+ return TRUE;
+}
+
+static void add_watch(const char *sender, const char *path,
+ const char *interface, const char *member,
+ GDBusSignalFunction function)
+{
+ guint watch;
+
+ watch = g_dbus_add_signal_watch(connection, sender, path, interface,
+ member, function, NULL, NULL);
+
+ watches = g_slist_prepend(watches, GUINT_TO_POINTER(watch));
+}
+
+static void hal_find_device_reply(DBusPendingCall *call, void *user_data)
+{
+ DBusMessage *reply;
+ DBusError err;
+ DBusMessageIter iter, sub;
+ int type;
+ const char *path;
+
+ DBG("begin of hal_find_device_reply()");
+ reply = dbus_pending_call_steal_reply(call);
+
+ dbus_error_init(&err);
+
+ if (dbus_set_error_from_message(&err, reply)) {
+ error("hald replied with an error: %s, %s",
+ err.name, err.message);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ dbus_message_iter_init(reply, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+ error("Unexpected signature in hal_find_device_reply()");
+ goto done;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ type = dbus_message_iter_get_arg_type(&sub);
+
+ if (type != DBUS_TYPE_OBJECT_PATH && type != DBUS_TYPE_STRING) {
+ error("No hal device with battery capability found");
+ goto done;
+ }
+
+ dbus_message_iter_get_basic(&sub, &path);
+
+ DBG("telephony-ofono: found battery device at %s", path);
+
+ add_watch(NULL, path, "org.freedesktop.Hal.Device",
+ "PropertyModified", handle_hal_property_modified);
+
+ hal_get_integer(path, "battery.charge_level.last_full", &battchg_last);
+ hal_get_integer(path, "battery.charge_level.current", &battchg_cur);
+ hal_get_integer(path, "battery.charge_level.design", &battchg_design);
+done:
+ dbus_message_unref(reply);
+ remove_pending(call);
+}
+
+static void handle_service_connect(DBusConnection *conn, void *user_data)
+{
+ DBG("telephony-ofono: %s found", OFONO_BUS_NAME);
+
+ send_method_call(OFONO_BUS_NAME, OFONO_PATH,
+ OFONO_MANAGER_INTERFACE, "GetModems",
+ get_modems_reply, NULL, DBUS_TYPE_INVALID);
+}
+
+static void handle_service_disconnect(DBusConnection *conn, void *user_data)
+{
+ DBG("telephony-ofono: %s exitted", OFONO_BUS_NAME);
+
+ if (modem_obj_path)
+ modem_removed(modem_obj_path);
+}
+
+int telephony_init(void)
+{
+ const char *battery_cap = "battery";
+ int ret;
+ guint watch;
+
+ connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+
+ add_watch(OFONO_BUS_NAME, NULL, OFONO_MODEM_INTERFACE,
+ "PropertyChanged", handle_modem_property_changed);
+ add_watch(OFONO_BUS_NAME, NULL, OFONO_NETWORKREG_INTERFACE,
+ "PropertyChanged", handle_network_property_changed);
+ add_watch(OFONO_BUS_NAME, NULL, OFONO_MANAGER_INTERFACE,
+ "ModemAdded", handle_manager_modem_added);
+ add_watch(OFONO_BUS_NAME, NULL, OFONO_MANAGER_INTERFACE,
+ "ModemRemoved", handle_manager_modem_removed);
+ add_watch(OFONO_BUS_NAME, NULL, OFONO_VCMANAGER_INTERFACE,
+ "CallAdded", handle_vcmanager_call_added);
+ add_watch(OFONO_BUS_NAME, NULL, OFONO_VCMANAGER_INTERFACE,
+ "CallRemoved", handle_vcmanager_call_removed);
+
+ watch = g_dbus_add_service_watch(connection, OFONO_BUS_NAME,
+ handle_service_connect,
+ handle_service_disconnect,
+ NULL, NULL);
+ if (watch == 0)
+ return -ENOMEM;
+
+ watches = g_slist_prepend(watches, GUINT_TO_POINTER(watch));
+
+ ret = send_method_call("org.freedesktop.Hal",
+ "/org/freedesktop/Hal/Manager",
+ "org.freedesktop.Hal.Manager",
+ "FindDeviceByCapability",
+ hal_find_device_reply, NULL,
+ DBUS_TYPE_STRING, &battery_cap,
+ DBUS_TYPE_INVALID);
+ if (ret < 0)
+ return ret;
+
+ DBG("telephony_init() successfully");
+
+ return ret;
+}
+
+static void remove_watch(gpointer data)
+{
+ g_dbus_remove_watch(connection, GPOINTER_TO_UINT(data));
+}
+
+void telephony_exit(void)
+{
+ DBG("");
+
+ g_free(last_dialed_number);
+ last_dialed_number = NULL;
+
+ if (modem_obj_path)
+ modem_removed(modem_obj_path);
+
+ g_slist_foreach(watches, (GFunc) remove_watch, NULL);
+ g_slist_free(watches);
+ watches = NULL;
+
+ g_slist_foreach(pending, (GFunc) dbus_pending_call_cancel, NULL);
+ g_slist_foreach(pending, (GFunc) dbus_pending_call_unref, NULL);
+ g_slist_free(pending);
+ pending = NULL;
+
+ dbus_connection_unref(connection);
+ connection = NULL;
+
+ telephony_deinit();
+}
diff --git a/audio/telephony.h b/audio/telephony.h
new file mode 100644
index 0000000..73b390c
--- /dev/null
+++ b/audio/telephony.h
@@ -0,0 +1,244 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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
+ *
+ */
+
+#include <stdint.h>
+#include <errno.h>
+#include <glib.h>
+
+/* Response and hold values */
+#define BTRH_NOT_SUPPORTED -2
+#define BTRH_NONE -1
+#define BTRH_HOLD 0
+#define BTRH_ACCEPT 1
+#define BTRH_REJECT 2
+
+/* HFP feature bits */
+#define AG_FEATURE_THREE_WAY_CALLING 0x0001
+#define AG_FEATURE_EC_ANDOR_NR 0x0002
+#define AG_FEATURE_VOICE_RECOGNITION 0x0004
+#define AG_FEATURE_INBAND_RINGTONE 0x0008
+#define AG_FEATURE_ATTACH_NUMBER_TO_VOICETAG 0x0010
+#define AG_FEATURE_REJECT_A_CALL 0x0020
+#define AG_FEATURE_ENHANCED_CALL_STATUS 0x0040
+#define AG_FEATURE_ENHANCED_CALL_CONTROL 0x0080
+#define AG_FEATURE_EXTENDED_ERROR_RESULT_CODES 0x0100
+
+#define HF_FEATURE_EC_ANDOR_NR 0x0001
+#define HF_FEATURE_CALL_WAITING_AND_3WAY 0x0002
+#define HF_FEATURE_CLI_PRESENTATION 0x0004
+#define HF_FEATURE_VOICE_RECOGNITION 0x0008
+#define HF_FEATURE_REMOTE_VOLUME_CONTROL 0x0010
+#define HF_FEATURE_ENHANCED_CALL_STATUS 0x0020
+#define HF_FEATURE_ENHANCED_CALL_CONTROL 0x0040
+
+/* Indicator event values */
+#define EV_SERVICE_NONE 0
+#define EV_SERVICE_PRESENT 1
+
+#define EV_CALL_INACTIVE 0
+#define EV_CALL_ACTIVE 1
+
+#define EV_CALLSETUP_INACTIVE 0
+#define EV_CALLSETUP_INCOMING 1
+#define EV_CALLSETUP_OUTGOING 2
+#define EV_CALLSETUP_ALERTING 3
+
+#define EV_CALLHELD_NONE 0
+#define EV_CALLHELD_MULTIPLE 1
+#define EV_CALLHELD_ON_HOLD 2
+
+#define EV_ROAM_INACTIVE 0
+#define EV_ROAM_ACTIVE 1
+
+/* Call parameters */
+#define CALL_DIR_OUTGOING 0
+#define CALL_DIR_INCOMING 1
+
+#define CALL_STATUS_ACTIVE 0
+#define CALL_STATUS_HELD 1
+#define CALL_STATUS_DIALING 2
+#define CALL_STATUS_ALERTING 3
+#define CALL_STATUS_INCOMING 4
+#define CALL_STATUS_WAITING 5
+
+#define CALL_MODE_VOICE 0
+#define CALL_MODE_DATA 1
+#define CALL_MODE_FAX 2
+
+#define CALL_MULTIPARTY_NO 0
+#define CALL_MULTIPARTY_YES 1
+
+/* Subscriber number parameters */
+#define SUBSCRIBER_SERVICE_VOICE 4
+#define SUBSCRIBER_SERVICE_FAX 5
+
+/* Operator selection mode values */
+#define OPERATOR_MODE_AUTO 0
+#define OPERATOR_MODE_MANUAL 1
+#define OPERATOR_MODE_DEREGISTER 2
+#define OPERATOR_MODE_MANUAL_AUTO 4
+
+/* Some common number types */
+#define NUMBER_TYPE_UNKNOWN 128
+#define NUMBER_TYPE_TELEPHONY 129
+#define NUMBER_TYPE_INTERNATIONAL 145
+#define NUMBER_TYPE_NATIONAL 161
+#define NUMBER_TYPE_VOIP 255
+
+/* Extended Audio Gateway Error Result Codes */
+typedef enum {
+ CME_ERROR_NONE = -1,
+ CME_ERROR_AG_FAILURE = 0,
+ CME_ERROR_NO_PHONE_CONNECTION = 1,
+ CME_ERROR_NOT_ALLOWED = 3,
+ CME_ERROR_NOT_SUPPORTED = 4,
+ CME_ERROR_PH_SIM_PIN_REQUIRED = 5,
+ CME_ERROR_SIM_NOT_INSERTED = 10,
+ CME_ERROR_SIM_PIN_REQUIRED = 11,
+ CME_ERROR_SIM_PUK_REQUIRED = 12,
+ CME_ERROR_SIM_FAILURE = 13,
+ CME_ERROR_SIM_BUSY = 14,
+ CME_ERROR_INCORRECT_PASSWORD = 16,
+ CME_ERROR_SIM_PIN2_REQUIRED = 17,
+ CME_ERROR_SIM_PUK2_REQUIRED = 18,
+ CME_ERROR_MEMORY_FULL = 20,
+ CME_ERROR_INVALID_INDEX = 21,
+ CME_ERROR_MEMORY_FAILURE = 23,
+ CME_ERROR_TEXT_STRING_TOO_LONG = 24,
+ CME_ERROR_INVALID_TEXT_STRING = 25,
+ CME_ERROR_DIAL_STRING_TOO_LONG = 26,
+ CME_ERROR_INVALID_DIAL_STRING = 27,
+ CME_ERROR_NO_NETWORK_SERVICE = 30,
+ CME_ERROR_NETWORK_TIMEOUT = 31,
+ CME_ERROR_NETWORK_NOT_ALLOWED = 32,
+} cme_error_t;
+
+struct indicator {
+ const char *desc;
+ const char *range;
+ int val;
+ gboolean ignore_redundant;
+};
+
+/* Notify telephony-*.c of connected/disconnected devices. Implemented by
+ * telephony-*.c
+ */
+void telephony_device_connected(void *telephony_device);
+void telephony_device_disconnected(void *telephony_device);
+
+/* HF requests (sent by the handsfree device). These are implemented by
+ * telephony-*.c
+ */
+void telephony_event_reporting_req(void *telephony_device, int ind);
+void telephony_response_and_hold_req(void *telephony_device, int rh);
+void telephony_last_dialed_number_req(void *telephony_device);
+void telephony_terminate_call_req(void *telephony_device);
+void telephony_answer_call_req(void *telephony_device);
+void telephony_dial_number_req(void *telephony_device, const char *number);
+void telephony_transmit_dtmf_req(void *telephony_device, char tone);
+void telephony_subscriber_number_req(void *telephony_device);
+void telephony_list_current_calls_req(void *telephony_device);
+void telephony_operator_selection_req(void *telephony_device);
+void telephony_call_hold_req(void *telephony_device, const char *cmd);
+void telephony_nr_and_ec_req(void *telephony_device, gboolean enable);
+void telephony_voice_dial_req(void *telephony_device, gboolean enable);
+void telephony_key_press_req(void *telephony_device, const char *keys);
+
+/* AG responses to HF requests. These are implemented by headset.c */
+int telephony_event_reporting_rsp(void *telephony_device, cme_error_t err);
+int telephony_response_and_hold_rsp(void *telephony_device, cme_error_t err);
+int telephony_last_dialed_number_rsp(void *telephony_device, cme_error_t err);
+int telephony_terminate_call_rsp(void *telephony_device, cme_error_t err);
+int telephony_answer_call_rsp(void *telephony_device, cme_error_t err);
+int telephony_dial_number_rsp(void *telephony_device, cme_error_t err);
+int telephony_transmit_dtmf_rsp(void *telephony_device, cme_error_t err);
+int telephony_subscriber_number_rsp(void *telephony_device, cme_error_t err);
+int telephony_list_current_calls_rsp(void *telephony_device, cme_error_t err);
+int telephony_operator_selection_rsp(void *telephony_device, cme_error_t err);
+int telephony_call_hold_rsp(void *telephony_device, cme_error_t err);
+int telephony_nr_and_ec_rsp(void *telephony_device, cme_error_t err);
+int telephony_voice_dial_rsp(void *telephony_device, cme_error_t err);
+int telephony_key_press_rsp(void *telephony_device, cme_error_t err);
+
+/* Event indications by AG. These are implemented by headset.c */
+int telephony_event_ind(int index);
+int telephony_response_and_hold_ind(int rh);
+int telephony_incoming_call_ind(const char *number, int type);
+int telephony_calling_stopped_ind(void);
+int telephony_ready_ind(uint32_t features, const struct indicator *indicators,
+ int rh, const char *chld);
+int telephony_deinit(void);
+int telephony_list_current_call_ind(int idx, int dir, int status, int mode,
+ int mprty, const char *number,
+ int type);
+int telephony_subscriber_number_ind(const char *number, int type,
+ int service);
+int telephony_call_waiting_ind(const char *number, int type);
+int telephony_operator_selection_ind(int mode, const char *oper);
+
+/* Helper function for quick indicator updates */
+static inline int telephony_update_indicator(struct indicator *indicators,
+ const char *desc,
+ int new_val)
+{
+ int i;
+ struct indicator *ind = NULL;
+
+ for (i = 0; indicators[i].desc != NULL; i++) {
+ if (g_str_equal(indicators[i].desc, desc)) {
+ ind = &indicators[i];
+ break;
+ }
+ }
+
+ if (!ind)
+ return -ENOENT;
+
+ DBG("Telephony indicator \"%s\" %d->%d", desc, ind->val, new_val);
+
+ if (ind->ignore_redundant && ind->val == new_val) {
+ DBG("Ignoring no-change indication");
+ return 0;
+ }
+
+ ind->val = new_val;
+
+ return telephony_event_ind(i);
+}
+
+static inline int telephony_get_indicator(const struct indicator *indicators,
+ const char *desc)
+{
+ int i;
+
+ for (i = 0; indicators[i].desc != NULL; i++) {
+ if (g_str_equal(indicators[i].desc, desc))
+ return indicators[i].val;
+ }
+
+ return -ENOENT;
+}
+
+int telephony_init(void);
+void telephony_exit(void);
diff --git a/audio/transport.c b/audio/transport.c
new file mode 100644
index 0000000..8ff6c85
--- /dev/null
+++ b/audio/transport.c
@@ -0,0 +1,927 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2007 Nokia Corporation
+ * Copyright (C) 2004-2009 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 <errno.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "../src/adapter.h"
+#include "../src/dbus-common.h"
+
+#include "log.h"
+#include "error.h"
+#include "device.h"
+#include "avdtp.h"
+#include "media.h"
+#include "transport.h"
+#include "a2dp.h"
+#include "headset.h"
+
+#ifndef DBUS_TYPE_UNIX_FD
+#define DBUS_TYPE_UNIX_FD -1
+#endif
+
+#define MEDIA_TRANSPORT_INTERFACE "org.bluez.MediaTransport"
+
+struct media_request {
+ DBusMessage *msg;
+ guint id;
+};
+
+struct media_owner {
+ struct media_transport *transport;
+ struct media_request *pending;
+ char *name;
+ char *accesstype;
+ guint watch;
+};
+
+struct media_transport {
+ DBusConnection *conn;
+ char *path; /* Transport object path */
+ struct audio_device *device; /* Transport device */
+ struct avdtp *session; /* Signalling session (a2dp only) */
+ struct media_endpoint *endpoint; /* Transport endpoint */
+ GSList *owners; /* Transport owners */
+ uint8_t *configuration; /* Transport configuration */
+ int size; /* Transport configuration size */
+ int fd; /* Transport file descriptor */
+ uint16_t imtu; /* Transport input mtu */
+ uint16_t omtu; /* Transport output mtu */
+ uint16_t delay; /* Transport delay (a2dp only) */
+ unsigned int nrec_id; /* Transport nrec watch (headset only) */
+ gboolean read_lock;
+ gboolean write_lock;
+ gboolean in_use;
+ guint (*resume) (struct media_transport *transport,
+ struct media_owner *owner);
+ guint (*suspend) (struct media_transport *transport,
+ struct media_owner *owner);
+ void (*cancel) (struct media_transport *transport,
+ guint id);
+ void (*get_properties) (
+ struct media_transport *transport,
+ DBusMessageIter *dict);
+ int (*set_property) (
+ struct media_transport *transport,
+ const char *property,
+ DBusMessageIter *value);
+};
+
+void media_transport_destroy(struct media_transport *transport)
+{
+ char *path;
+
+ path = g_strdup(transport->path);
+
+ g_dbus_unregister_interface(transport->conn, path,
+ MEDIA_TRANSPORT_INTERFACE);
+
+ g_free(path);
+}
+
+static struct media_request *media_request_create(DBusMessage *msg, guint id)
+{
+ struct media_request *req;
+
+ req = g_new0(struct media_request, 1);
+ req->msg = dbus_message_ref(msg);
+ req->id = id;
+
+ DBG("Request created: method=%s id=%u", dbus_message_get_member(msg),
+ id);
+
+ return req;
+}
+
+static void media_request_reply(struct media_request *req,
+ DBusConnection *conn, int err)
+{
+ DBusMessage *reply;
+
+ DBG("Request %s Reply %s", dbus_message_get_member(req->msg),
+ strerror(err));
+
+ if (!err)
+ reply = g_dbus_create_reply(req->msg, DBUS_TYPE_INVALID);
+ else
+ reply = g_dbus_create_error(req->msg,
+ ERROR_INTERFACE ".Failed",
+ "%s", strerror(err));
+
+ g_dbus_send_message(conn, reply);
+}
+
+static gboolean media_transport_release(struct media_transport *transport,
+ const char *accesstype)
+{
+ if (g_strstr_len(accesstype, -1, "r") != NULL) {
+ transport->read_lock = FALSE;
+ DBG("Transport %s: read lock released", transport->path);
+ }
+
+ if (g_strstr_len(accesstype, -1, "w") != NULL) {
+ transport->write_lock = FALSE;
+ DBG("Transport %s: write lock released", transport->path);
+ }
+
+ return TRUE;
+}
+
+static void media_owner_remove(struct media_owner *owner)
+{
+ struct media_transport *transport = owner->transport;
+ struct media_request *req = owner->pending;
+
+ if (!req)
+ return;
+
+ DBG("Owner %s Request %s", owner->name,
+ dbus_message_get_member(req->msg));
+
+ if (req->id)
+ transport->cancel(transport, req->id);
+
+ owner->pending = NULL;
+ if (req->msg)
+ dbus_message_unref(req->msg);
+
+ g_free(req);
+}
+
+static void media_owner_free(struct media_owner *owner)
+{
+ DBG("Owner %s", owner->name);
+
+ media_owner_remove(owner);
+
+ g_free(owner->name);
+ g_free(owner->accesstype);
+ g_free(owner);
+}
+
+static void media_transport_remove(struct media_transport *transport,
+ struct media_owner *owner)
+{
+ DBG("Transport %s Owner %s", transport->path, owner->name);
+
+ media_transport_release(transport, owner->accesstype);
+
+ /* Reply if owner has a pending request */
+ if (owner->pending)
+ media_request_reply(owner->pending, transport->conn, EIO);
+
+ transport->owners = g_slist_remove(transport->owners, owner);
+
+ if (owner->watch)
+ g_dbus_remove_watch(transport->conn, owner->watch);
+
+ media_owner_free(owner);
+
+ /* Suspend if there is no longer any owner */
+ if (transport->owners == NULL && transport->in_use)
+ transport->suspend(transport, NULL);
+}
+
+static gboolean media_transport_set_fd(struct media_transport *transport,
+ int fd, uint16_t imtu, uint16_t omtu)
+{
+ if (transport->fd == fd)
+ return TRUE;
+
+ transport->fd = fd;
+ transport->imtu = imtu;
+ transport->omtu = omtu;
+
+ info("%s: fd(%d) ready", transport->path, fd);
+
+ return TRUE;
+}
+
+static gboolean remove_owner(gpointer data)
+{
+ struct media_owner *owner = data;
+
+ media_transport_remove(owner->transport, owner);
+
+ return FALSE;
+}
+
+static void a2dp_resume_complete(struct avdtp *session,
+ struct avdtp_error *err, void *user_data)
+{
+ struct media_owner *owner = user_data;
+ struct media_request *req = owner->pending;
+ struct media_transport *transport = owner->transport;
+ struct a2dp_sep *sep = media_endpoint_get_sep(transport->endpoint);
+ struct avdtp_stream *stream;
+ int fd;
+ uint16_t imtu, omtu;
+ gboolean ret;
+
+ req->id = 0;
+
+ if (err)
+ goto fail;
+
+ stream = a2dp_sep_get_stream(sep);
+ if (stream == NULL)
+ goto fail;
+
+ ret = avdtp_stream_get_transport(stream, &fd, &imtu, &omtu, NULL);
+ if (ret == FALSE)
+ goto fail;
+
+ media_transport_set_fd(transport, fd, imtu, omtu);
+
+ if (g_strstr_len(owner->accesstype, -1, "r") == NULL)
+ imtu = 0;
+
+ if (g_strstr_len(owner->accesstype, -1, "w") == NULL)
+ omtu = 0;
+
+ ret = g_dbus_send_reply(transport->conn, req->msg,
+ DBUS_TYPE_UNIX_FD, &fd,
+ DBUS_TYPE_UINT16, &imtu,
+ DBUS_TYPE_UINT16, &omtu,
+ DBUS_TYPE_INVALID);
+ if (ret == FALSE)
+ goto fail;
+
+ media_owner_remove(owner);
+
+ return;
+
+fail:
+ /* Let the stream state change before removing the owner */
+ g_idle_add(remove_owner, owner);
+}
+
+static guint resume_a2dp(struct media_transport *transport,
+ struct media_owner *owner)
+{
+ struct media_endpoint *endpoint = transport->endpoint;
+ struct audio_device *device = transport->device;
+ struct a2dp_sep *sep = media_endpoint_get_sep(endpoint);
+
+ if (transport->session == NULL) {
+ transport->session = avdtp_get(&device->src, &device->dst);
+ if (transport->session == NULL)
+ return 0;
+ }
+
+ if (transport->in_use == TRUE)
+ goto done;
+
+ transport->in_use = a2dp_sep_lock(sep, transport->session);
+ if (transport->in_use == FALSE)
+ return 0;
+
+done:
+ return a2dp_resume(transport->session, sep, a2dp_resume_complete,
+ owner);
+}
+
+static void a2dp_suspend_complete(struct avdtp *session,
+ struct avdtp_error *err, void *user_data)
+{
+ struct media_owner *owner = user_data;
+ struct media_transport *transport = owner->transport;
+ struct a2dp_sep *sep = media_endpoint_get_sep(transport->endpoint);
+
+ /* Release always succeeds */
+ if (owner->pending) {
+ owner->pending->id = 0;
+ media_request_reply(owner->pending, transport->conn, 0);
+ media_owner_remove(owner);
+ }
+
+ a2dp_sep_unlock(sep, transport->session);
+ transport->in_use = FALSE;
+ media_transport_remove(transport, owner);
+}
+
+static guint suspend_a2dp(struct media_transport *transport,
+ struct media_owner *owner)
+{
+ struct media_endpoint *endpoint = transport->endpoint;
+ struct a2dp_sep *sep = media_endpoint_get_sep(endpoint);
+
+ if (!owner) {
+ a2dp_sep_unlock(sep, transport->session);
+ transport->in_use = FALSE;
+ return 0;
+ }
+
+ return a2dp_suspend(transport->session, sep, a2dp_suspend_complete,
+ owner);
+}
+
+static void cancel_a2dp(struct media_transport *transport, guint id)
+{
+ a2dp_cancel(transport->device, id);
+}
+
+static void headset_resume_complete(struct audio_device *dev, void *user_data)
+{
+ struct media_owner *owner = user_data;
+ struct media_request *req = owner->pending;
+ struct media_transport *transport = owner->transport;
+ int fd;
+ uint16_t imtu, omtu;
+ gboolean ret;
+
+ req->id = 0;
+
+ if (dev == NULL)
+ goto fail;
+
+ fd = headset_get_sco_fd(dev);
+ if (fd < 0)
+ goto fail;
+
+ imtu = 48;
+ omtu = 48;
+
+ media_transport_set_fd(transport, fd, imtu, omtu);
+
+ if (g_strstr_len(owner->accesstype, -1, "r") == NULL)
+ imtu = 0;
+
+ if (g_strstr_len(owner->accesstype, -1, "w") == NULL)
+ omtu = 0;
+
+ ret = g_dbus_send_reply(transport->conn, req->msg,
+ DBUS_TYPE_UNIX_FD, &fd,
+ DBUS_TYPE_UINT16, &imtu,
+ DBUS_TYPE_UINT16, &omtu,
+ DBUS_TYPE_INVALID);
+ if (ret == FALSE)
+ goto fail;
+
+ media_owner_remove(owner);
+
+ return;
+
+fail:
+ media_transport_remove(transport, owner);
+}
+
+static guint resume_headset(struct media_transport *transport,
+ struct media_owner *owner)
+{
+ struct audio_device *device = transport->device;
+
+ if (transport->in_use == TRUE)
+ goto done;
+
+ transport->in_use = headset_lock(device, HEADSET_LOCK_READ |
+ HEADSET_LOCK_WRITE);
+ if (transport->in_use == FALSE)
+ return 0;
+
+done:
+ return headset_request_stream(device, headset_resume_complete,
+ owner);
+}
+
+static void headset_suspend_complete(struct audio_device *dev, void *user_data)
+{
+ struct media_owner *owner = user_data;
+ struct media_transport *transport = owner->transport;
+
+ /* Release always succeeds */
+ if (owner->pending) {
+ owner->pending->id = 0;
+ media_request_reply(owner->pending, transport->conn, 0);
+ media_owner_remove(owner);
+ }
+
+ headset_unlock(dev, HEADSET_LOCK_READ | HEADSET_LOCK_WRITE);
+ transport->in_use = FALSE;
+ media_transport_remove(transport, owner);
+}
+
+static guint suspend_headset(struct media_transport *transport,
+ struct media_owner *owner)
+{
+ struct audio_device *device = transport->device;
+
+ if (!owner) {
+ headset_unlock(device, HEADSET_LOCK_READ | HEADSET_LOCK_WRITE);
+ transport->in_use = FALSE;
+ return 0;
+ }
+
+ return headset_suspend_stream(device, headset_suspend_complete, owner);
+}
+
+static void cancel_headset(struct media_transport *transport, guint id)
+{
+ headset_cancel_stream(transport->device, id);
+}
+
+static void media_owner_exit(DBusConnection *connection, void *user_data)
+{
+ struct media_owner *owner = user_data;
+
+ owner->watch = 0;
+
+ media_owner_remove(owner);
+
+ media_transport_remove(owner->transport, owner);
+}
+
+static gboolean media_transport_acquire(struct media_transport *transport,
+ const char *accesstype)
+{
+ gboolean read_lock = FALSE, write_lock = FALSE;
+
+ if (g_strstr_len(accesstype, -1, "r") != NULL) {
+ if (transport->read_lock == TRUE)
+ return FALSE;
+ read_lock = TRUE;
+ }
+
+ if (g_strstr_len(accesstype, -1, "w") != NULL) {
+ if (transport->write_lock == TRUE)
+ return FALSE;
+ write_lock = TRUE;
+ }
+
+ /* Check invalid accesstype */
+ if (read_lock == FALSE && write_lock == FALSE)
+ return FALSE;
+
+ if (read_lock) {
+ transport->read_lock = read_lock;
+ DBG("Transport %s: read lock acquired", transport->path);
+ }
+
+ if (write_lock) {
+ transport->write_lock = write_lock;
+ DBG("Transport %s: write lock acquired", transport->path);
+ }
+
+
+ return TRUE;
+}
+
+static void media_transport_add(struct media_transport *transport,
+ struct media_owner *owner)
+{
+ DBG("Transport %s Owner %s", transport->path, owner->name);
+ transport->owners = g_slist_append(transport->owners, owner);
+ owner->transport = transport;
+}
+
+static struct media_owner *media_owner_create(DBusConnection *conn,
+ DBusMessage *msg,
+ const char *accesstype)
+{
+ struct media_owner *owner;
+
+ owner = g_new0(struct media_owner, 1);
+ owner->name = g_strdup(dbus_message_get_sender(msg));
+ owner->accesstype = g_strdup(accesstype);
+ owner->watch = g_dbus_add_disconnect_watch(conn, owner->name,
+ media_owner_exit,
+ owner, NULL);
+
+ DBG("Owner created: sender=%s accesstype=%s", owner->name,
+ accesstype);
+
+ return owner;
+}
+
+static void media_owner_add(struct media_owner *owner,
+ struct media_request *req)
+{
+ DBG("Owner %s Request %s", owner->name,
+ dbus_message_get_member(req->msg));
+
+ owner->pending = req;
+}
+
+static struct media_owner *media_transport_find_owner(
+ struct media_transport *transport,
+ const char *name)
+{
+ GSList *l;
+
+ for (l = transport->owners; l; l = l->next) {
+ struct media_owner *owner = l->data;
+
+ if (g_strcmp0(owner->name, name) == 0)
+ return owner;
+ }
+
+ return NULL;
+}
+
+static DBusMessage *acquire(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct media_transport *transport = data;
+ struct media_owner *owner;
+ struct media_request *req;
+ const char *accesstype, *sender;
+ guint id;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &accesstype,
+ DBUS_TYPE_INVALID))
+ return NULL;
+
+ sender = dbus_message_get_sender(msg);
+
+ owner = media_transport_find_owner(transport, sender);
+ if (owner != NULL)
+ return btd_error_not_authorized(msg);
+
+ if (media_transport_acquire(transport, accesstype) == FALSE)
+ return btd_error_not_authorized(msg);
+
+ owner = media_owner_create(conn, msg, accesstype);
+ id = transport->resume(transport, owner);
+ if (id == 0) {
+ media_owner_free(owner);
+ return btd_error_not_authorized(msg);
+ }
+
+ req = media_request_create(msg, id);
+ media_owner_add(owner, req);
+ media_transport_add(transport, owner);
+
+ return NULL;
+}
+
+static DBusMessage *release(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct media_transport *transport = data;
+ struct media_owner *owner;
+ const char *accesstype, *sender;
+ struct media_request *req;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &accesstype,
+ DBUS_TYPE_INVALID))
+ return NULL;
+
+ sender = dbus_message_get_sender(msg);
+
+ owner = media_transport_find_owner(transport, sender);
+ if (owner == NULL)
+ return btd_error_not_authorized(msg);
+
+ if (g_strcmp0(owner->accesstype, accesstype) == 0) {
+ guint id;
+
+ /* Not the last owner, no need to suspend */
+ if (g_slist_length(transport->owners) != 1) {
+ media_transport_remove(transport, owner);
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+ }
+
+ if (owner->pending) {
+ const char *member;
+
+ member = dbus_message_get_member(owner->pending->msg);
+ /* Cancel Acquire request if that exist */
+ if (g_str_equal(member, "Acquire"))
+ media_owner_remove(owner);
+ else
+ return btd_error_in_progress(msg);
+ }
+
+ id = transport->suspend(transport, owner);
+ if (id == 0) {
+ media_transport_remove(transport, owner);
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+ }
+
+ req = media_request_create(msg, id);
+ media_owner_add(owner, req);
+
+ return NULL;
+ } else if (g_strstr_len(owner->accesstype, -1, accesstype) != NULL) {
+ media_transport_release(transport, accesstype);
+ g_strdelimit(owner->accesstype, accesstype, ' ');
+ } else
+ return btd_error_not_authorized(msg);
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static int set_property_a2dp(struct media_transport *transport,
+ const char *property,
+ DBusMessageIter *value)
+{
+ if (g_strcmp0(property, "Delay") == 0) {
+ if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_UINT16)
+ return -EINVAL;
+ dbus_message_iter_get_basic(value, &transport->delay);
+
+ /* FIXME: send new delay */
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int set_property_headset(struct media_transport *transport,
+ const char *property,
+ DBusMessageIter *value)
+{
+ if (g_strcmp0(property, "NREC") == 0) {
+ gboolean nrec;
+
+ if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_BOOLEAN)
+ return -EINVAL;
+ dbus_message_iter_get_basic(value, &nrec);
+
+ /* FIXME: set new nrec */
+ return 0;
+ } else if (g_strcmp0(property, "InbandRingtone") == 0) {
+ gboolean inband;
+
+ if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_BOOLEAN)
+ return -EINVAL;
+ dbus_message_iter_get_basic(value, &inband);
+
+ /* FIXME: set new inband */
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static DBusMessage *set_property(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct media_transport *transport = data;
+ DBusMessageIter iter;
+ DBusMessageIter value;
+ const char *property, *sender;
+ GSList *l;
+ int err;
+
+ if (!dbus_message_iter_init(msg, &iter))
+ return btd_error_invalid_args(msg);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&iter, &property);
+ dbus_message_iter_next(&iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+ return btd_error_invalid_args(msg);
+ dbus_message_iter_recurse(&iter, &value);
+
+ sender = dbus_message_get_sender(msg);
+ err = -EINVAL;
+
+ /* Check if sender has acquired the transport */
+ for (l = transport->owners; l; l = l->next) {
+ struct media_owner *owner = l->data;
+
+ if (g_strcmp0(owner->name, sender) == 0) {
+ err = transport->set_property(transport, property,
+ &value);
+ break;
+ }
+ }
+
+ if (err < 0) {
+ if (err == -EINVAL)
+ return btd_error_invalid_args(msg);
+ return btd_error_failed(msg, strerror(-err));
+ }
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static void get_properties_a2dp(struct media_transport *transport,
+ DBusMessageIter *dict)
+{
+ dict_append_entry(dict, "Delay", DBUS_TYPE_UINT16, &transport->delay);
+}
+
+static void get_properties_headset(struct media_transport *transport,
+ DBusMessageIter *dict)
+{
+ gboolean nrec, inband;
+ const char *routing;
+
+ nrec = headset_get_nrec(transport->device);
+ dict_append_entry(dict, "NREC", DBUS_TYPE_BOOLEAN, &nrec);
+
+ inband = headset_get_inband(transport->device);
+ dict_append_entry(dict, "InbandRingtone", DBUS_TYPE_BOOLEAN, &inband);
+
+ routing = headset_get_sco_hci(transport->device) ? "HCI" : "PCM";
+ dict_append_entry(dict, "Routing", DBUS_TYPE_STRING, &routing);
+}
+
+void transport_get_properties(struct media_transport *transport,
+ DBusMessageIter *iter)
+{
+ DBusMessageIter dict;
+ const char *uuid;
+ uint8_t codec;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ /* Device */
+ dict_append_entry(&dict, "Device", DBUS_TYPE_OBJECT_PATH,
+ &transport->device->path);
+
+ uuid = media_endpoint_get_uuid(transport->endpoint);
+ dict_append_entry(&dict, "UUID", DBUS_TYPE_STRING, &uuid);
+
+ codec = media_endpoint_get_codec(transport->endpoint);
+ dict_append_entry(&dict, "Codec", DBUS_TYPE_BYTE, &codec);
+
+ dict_append_array(&dict, "Configuration", DBUS_TYPE_BYTE,
+ &transport->configuration, transport->size);
+
+ if (transport->get_properties)
+ transport->get_properties(transport, &dict);
+
+ dbus_message_iter_close_container(iter, &dict);
+}
+
+static DBusMessage *get_properties(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct media_transport *transport = data;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ transport_get_properties(transport, &iter);
+
+ return reply;
+}
+
+static GDBusMethodTable transport_methods[] = {
+ { "GetProperties", "", "a{sv}", get_properties },
+ { "Acquire", "s", "h", acquire,
+ G_DBUS_METHOD_FLAG_ASYNC},
+ { "Release", "s", "", release,
+ G_DBUS_METHOD_FLAG_ASYNC},
+ { "SetProperty", "sv", "", set_property },
+ { },
+};
+
+static GDBusSignalTable transport_signals[] = {
+ { "PropertyChanged", "sv" },
+ { }
+};
+
+static void media_transport_free(void *data)
+{
+ struct media_transport *transport = data;
+ GSList *l;
+
+ for (l = transport->owners; l; l = l->next)
+ media_transport_remove(transport, l->data);
+
+ g_slist_free(transport->owners);
+
+ if (transport->session)
+ avdtp_unref(transport->session);
+
+ if (transport->nrec_id)
+ headset_remove_nrec_cb(transport->device, transport->nrec_id);
+
+ if (transport->conn)
+ dbus_connection_unref(transport->conn);
+
+ g_free(transport->configuration);
+ g_free(transport->path);
+ g_free(transport);
+}
+
+static void headset_nrec_changed(struct audio_device *dev, gboolean nrec,
+ void *user_data)
+{
+ struct media_transport *transport = user_data;
+
+ DBG("");
+
+ emit_property_changed(transport->conn, transport->path,
+ MEDIA_TRANSPORT_INTERFACE, "NREC",
+ DBUS_TYPE_BOOLEAN, &nrec);
+}
+
+struct media_transport *media_transport_create(DBusConnection *conn,
+ struct media_endpoint *endpoint,
+ struct audio_device *device,
+ uint8_t *configuration,
+ size_t size)
+{
+ struct media_transport *transport;
+ const char *uuid;
+ static int fd = 0;
+
+ transport = g_new0(struct media_transport, 1);
+ transport->conn = dbus_connection_ref(conn);
+ transport->device = device;
+ transport->endpoint = endpoint;
+ transport->configuration = g_new(uint8_t, size);
+ memcpy(transport->configuration, configuration, size);
+ transport->size = size;
+ transport->path = g_strdup_printf("%s/fd%d", device->path, fd++);
+ transport->fd = -1;
+
+ uuid = media_endpoint_get_uuid(endpoint);
+ if (strcasecmp(uuid, A2DP_SOURCE_UUID) == 0 ||
+ strcasecmp(uuid, A2DP_SINK_UUID) == 0) {
+ transport->resume = resume_a2dp;
+ transport->suspend = suspend_a2dp;
+ transport->cancel = cancel_a2dp;
+ transport->get_properties = get_properties_a2dp;
+ transport->set_property = set_property_a2dp;
+ } else if (strcasecmp(uuid, HFP_AG_UUID) == 0 ||
+ strcasecmp(uuid, HSP_AG_UUID) == 0) {
+ transport->resume = resume_headset;
+ transport->suspend = suspend_headset;
+ transport->cancel = cancel_headset;
+ transport->get_properties = get_properties_headset;
+ transport->set_property = set_property_headset;
+ transport->nrec_id = headset_add_nrec_cb(device,
+ headset_nrec_changed,
+ transport);
+ } else
+ goto fail;
+
+ if (g_dbus_register_interface(transport->conn, transport->path,
+ MEDIA_TRANSPORT_INTERFACE,
+ transport_methods, transport_signals, NULL,
+ transport, media_transport_free) == FALSE) {
+ error("Could not register transport %s", transport->path);
+ goto fail;
+ }
+
+ return transport;
+
+fail:
+ media_transport_free(transport);
+ return NULL;
+}
+
+const char *media_transport_get_path(struct media_transport *transport)
+{
+ return transport->path;
+}
+
+void media_transport_update_delay(struct media_transport *transport,
+ uint16_t delay)
+{
+ /* Check if delay really changed */
+ if (transport->delay == delay)
+ return;
+
+ transport->delay = delay;
+
+ emit_property_changed(transport->conn, transport->path,
+ MEDIA_TRANSPORT_INTERFACE, "Delay",
+ DBUS_TYPE_UINT16, &transport->delay);
+}
diff --git a/audio/transport.h b/audio/transport.h
new file mode 100644
index 0000000..be4d666
--- /dev/null
+++ b/audio/transport.h
@@ -0,0 +1,38 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2007 Nokia Corporation
+ * Copyright (C) 2004-2009 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
+ *
+ */
+
+struct media_transport;
+
+struct media_transport *media_transport_create(DBusConnection *conn,
+ struct media_endpoint *endpoint,
+ struct audio_device *device,
+ uint8_t *configuration,
+ size_t size);
+
+void media_transport_destroy(struct media_transport *transport);
+const char *media_transport_get_path(struct media_transport *transport);
+void media_transport_update_delay(struct media_transport *transport,
+ uint16_t delay);
+void transport_get_properties(struct media_transport *transport,
+ DBusMessageIter *iter);
diff --git a/audio/unix.c b/audio/unix.c
new file mode 100644
index 0000000..37c772d
--- /dev/null
+++ b/audio/unix.c
@@ -0,0 +1,1917 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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 <fcntl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdint.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <dbus/dbus.h>
+#include <glib.h>
+
+#include "log.h"
+#include "ipc.h"
+#include "device.h"
+#include "manager.h"
+#include "avdtp.h"
+#include "media.h"
+#include "a2dp.h"
+#include "headset.h"
+#include "sink.h"
+#include "gateway.h"
+#include "unix.h"
+#include "glib-helper.h"
+
+#define check_nul(str) (str[sizeof(str) - 1] == '\0')
+
+typedef enum {
+ TYPE_NONE,
+ TYPE_HEADSET,
+ TYPE_GATEWAY,
+ TYPE_SINK,
+ TYPE_SOURCE
+} service_type_t;
+
+typedef void (*notify_cb_t) (struct audio_device *dev, void *data);
+
+struct a2dp_data {
+ struct avdtp *session;
+ struct avdtp_stream *stream;
+ struct a2dp_sep *sep;
+};
+
+struct headset_data {
+ gboolean locked;
+};
+
+struct unix_client {
+ struct audio_device *dev;
+ GSList *caps;
+ service_type_t type;
+ char *interface;
+ uint8_t seid;
+ union {
+ struct a2dp_data a2dp;
+ struct headset_data hs;
+ } d;
+ int sock;
+ int lock;
+ int data_fd; /* To be deleted once two phase configuration is fully implemented */
+ unsigned int req_id;
+ unsigned int cb_id;
+ gboolean (*cancel) (struct audio_device *dev, unsigned int id);
+};
+
+static GSList *clients = NULL;
+
+static int unix_sock = -1;
+
+static void client_free(struct unix_client *client)
+{
+ DBG("client_free(%p)", client);
+
+ if (client->cancel && client->dev && client->req_id > 0)
+ client->cancel(client->dev, client->req_id);
+
+ if (client->sock >= 0)
+ close(client->sock);
+
+ if (client->caps) {
+ g_slist_foreach(client->caps, (GFunc) g_free, NULL);
+ g_slist_free(client->caps);
+ }
+
+ g_free(client->interface);
+ g_free(client);
+}
+
+static int set_nonblocking(int fd)
+{
+ long arg;
+
+ arg = fcntl(fd, F_GETFL);
+ if (arg < 0)
+ return -errno;
+
+ /* Return if already nonblocking */
+ if (arg & O_NONBLOCK)
+ return 0;
+
+ arg |= O_NONBLOCK;
+ if (fcntl(fd, F_SETFL, arg) < 0)
+ return -errno;
+
+ return 0;
+}
+
+/* Pass file descriptor through local domain sockets (AF_LOCAL, formerly
+ * AF_UNIX) and the sendmsg() system call with the cmsg_type field of a "struct
+ * cmsghdr" set to SCM_RIGHTS and the data being an integer value equal to the
+ * handle of the file descriptor to be passed. */
+static int unix_sendmsg_fd(int sock, int fd)
+{
+ char cmsg_b[CMSG_SPACE(sizeof(int))], m = 'm';
+ struct cmsghdr *cmsg;
+ struct iovec iov = { &m, sizeof(m) };
+ struct msghdr msgh;
+
+ memset(&msgh, 0, sizeof(msgh));
+ msgh.msg_iov = &iov;
+ msgh.msg_iovlen = 1;
+ msgh.msg_control = &cmsg_b;
+ msgh.msg_controllen = CMSG_LEN(sizeof(int));
+
+ cmsg = CMSG_FIRSTHDR(&msgh);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ /* Initialize the payload */
+ memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
+
+ return sendmsg(sock, &msgh, MSG_NOSIGNAL);
+}
+
+static void unix_ipc_sendmsg(struct unix_client *client,
+ const bt_audio_msg_header_t *msg)
+{
+ const char *type = bt_audio_strtype(msg->type);
+ const char *name = bt_audio_strname(msg->name);
+
+ DBG("Audio API: %s -> %s", type, name);
+
+ if (send(client->sock, msg, msg->length, 0) < 0)
+ error("Error %s(%d)", strerror(errno), errno);
+}
+
+static void unix_ipc_error(struct unix_client *client, uint8_t name, int err)
+{
+ char buf[BT_SUGGESTED_BUFFER_SIZE];
+ bt_audio_error_t *rsp = (void *) buf;
+
+ if (!g_slist_find(clients, client))
+ return;
+
+ memset(buf, 0, sizeof(buf));
+ rsp->h.type = BT_ERROR;
+ rsp->h.name = name;
+ rsp->h.length = sizeof(*rsp);
+
+ rsp->posix_errno = err;
+
+ DBG("sending error %s(%d)", strerror(err), err);
+ unix_ipc_sendmsg(client, &rsp->h);
+}
+
+static service_type_t select_service(struct audio_device *dev, const char *interface)
+{
+ if (!interface) {
+ if (dev->sink && avdtp_is_connected(&dev->src, &dev->dst))
+ return TYPE_SINK;
+ else if (dev->source && avdtp_is_connected(&dev->src,
+ &dev->dst))
+ return TYPE_SOURCE;
+ else if (dev->headset && headset_is_active(dev))
+ return TYPE_HEADSET;
+ else if (dev->sink)
+ return TYPE_SINK;
+ else if (dev->source)
+ return TYPE_SOURCE;
+ else if (dev->headset)
+ return TYPE_HEADSET;
+ } else if (!strcmp(interface, AUDIO_SOURCE_INTERFACE) && dev->source)
+ return TYPE_SOURCE;
+ else if (!strcmp(interface, AUDIO_SINK_INTERFACE) && dev->sink)
+ return TYPE_SINK;
+ else if (!strcmp(interface, AUDIO_HEADSET_INTERFACE) && dev->headset)
+ return TYPE_HEADSET;
+ else if (!strcmp(interface, AUDIO_GATEWAY_INTERFACE) && dev->gateway)
+ return TYPE_GATEWAY;
+
+ return TYPE_NONE;
+}
+
+static void stream_state_changed(struct avdtp_stream *stream,
+ avdtp_state_t old_state,
+ avdtp_state_t new_state,
+ struct avdtp_error *err,
+ void *user_data)
+{
+ struct unix_client *client = user_data;
+ struct a2dp_data *a2dp = &client->d.a2dp;
+
+ switch (new_state) {
+ case AVDTP_STATE_IDLE:
+ if (a2dp->sep) {
+ a2dp_sep_unlock(a2dp->sep, a2dp->session);
+ a2dp->sep = NULL;
+ }
+ if (a2dp->session) {
+ avdtp_unref(a2dp->session);
+ a2dp->session = NULL;
+ }
+ a2dp->stream = NULL;
+ client->cb_id = 0;
+ break;
+ default:
+ break;
+ }
+}
+
+static uint8_t headset_generate_capability(struct audio_device *dev,
+ codec_capabilities_t *codec)
+{
+ pcm_capabilities_t *pcm;
+
+ codec->seid = BT_A2DP_SEID_RANGE + 1;
+ codec->transport = BT_CAPABILITIES_TRANSPORT_SCO;
+ codec->type = BT_HFP_CODEC_PCM;
+ codec->length = sizeof(*pcm);
+
+ pcm = (void *) codec;
+ pcm->sampling_rate = 8000;
+ if (dev->headset) {
+ if (headset_get_nrec(dev))
+ pcm->flags |= BT_PCM_FLAG_NREC;
+ if (!headset_get_sco_hci(dev))
+ pcm->flags |= BT_PCM_FLAG_PCM_ROUTING;
+ codec->configured = headset_is_active(dev);
+ codec->lock = headset_get_lock(dev);
+ } else {
+ pcm->flags |= BT_PCM_FLAG_NREC;
+ codec->configured = TRUE;
+ codec->lock = 0;
+ }
+
+ return codec->length;
+}
+
+static void headset_discovery_complete(struct audio_device *dev, void *user_data)
+{
+ struct unix_client *client = user_data;
+ char buf[BT_SUGGESTED_BUFFER_SIZE];
+ struct bt_get_capabilities_rsp *rsp = (void *) buf;
+ uint8_t length;
+
+ client->req_id = 0;
+
+ if (!dev)
+ goto failed;
+
+ memset(buf, 0, sizeof(buf));
+
+ length = headset_generate_capability(dev, (void *) rsp->data);
+
+ rsp->h.type = BT_RESPONSE;
+ rsp->h.name = BT_GET_CAPABILITIES;
+ rsp->h.length = sizeof(*rsp) + length;
+
+ ba2str(&dev->src, rsp->source);
+ ba2str(&dev->dst, rsp->destination);
+ strncpy(rsp->object, dev->path, sizeof(rsp->object));
+
+ unix_ipc_sendmsg(client, &rsp->h);
+
+ return;
+
+failed:
+ error("discovery failed");
+ unix_ipc_error(client, BT_SET_CONFIGURATION, EIO);
+}
+
+static void headset_setup_complete(struct audio_device *dev, void *user_data)
+{
+ struct unix_client *client = user_data;
+ char buf[BT_SUGGESTED_BUFFER_SIZE];
+ struct bt_set_configuration_rsp *rsp = (void *) buf;
+
+ client->req_id = 0;
+
+ if (!dev)
+ goto failed;
+
+ memset(buf, 0, sizeof(buf));
+
+ rsp->h.type = BT_RESPONSE;
+ rsp->h.name = BT_SET_CONFIGURATION;
+ rsp->h.length = sizeof(*rsp);
+
+ rsp->link_mtu = 48;
+
+ client->data_fd = headset_get_sco_fd(dev);
+
+ unix_ipc_sendmsg(client, &rsp->h);
+
+ return;
+
+failed:
+ error("config failed");
+ unix_ipc_error(client, BT_SET_CONFIGURATION, EIO);
+}
+
+static void gateway_setup_complete(struct audio_device *dev, GError *err, void *user_data)
+{
+ struct unix_client *client = user_data;
+ char buf[BT_SUGGESTED_BUFFER_SIZE];
+ struct bt_set_configuration_rsp *rsp = (void *) buf;
+
+ if (err) {
+ unix_ipc_error(client, BT_SET_CONFIGURATION, err->code);
+ return;
+ }
+
+ client->req_id = 0;
+
+ memset(buf, 0, sizeof(buf));
+
+ rsp->h.type = BT_RESPONSE;
+ rsp->h.name = BT_SET_CONFIGURATION;
+ rsp->h.length = sizeof(*rsp);
+
+ rsp->link_mtu = 48;
+
+ client->data_fd = gateway_get_sco_fd(dev);
+
+ unix_ipc_sendmsg(client, &rsp->h);
+}
+
+static void headset_resume_complete(struct audio_device *dev, void *user_data)
+{
+ struct unix_client *client = user_data;
+ char buf[BT_SUGGESTED_BUFFER_SIZE];
+ struct bt_start_stream_rsp *rsp = (void *) buf;
+ struct bt_new_stream_ind *ind = (void *) buf;
+
+ client->req_id = 0;
+
+ if (!dev)
+ goto failed;
+
+ client->data_fd = headset_get_sco_fd(dev);
+ if (client->data_fd < 0) {
+ error("Unable to get a SCO fd");
+ goto failed;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ rsp->h.type = BT_RESPONSE;
+ rsp->h.name = BT_START_STREAM;
+ rsp->h.length = sizeof(*rsp);
+
+ unix_ipc_sendmsg(client, &rsp->h);
+
+ memset(buf, 0, sizeof(buf));
+ ind->h.type = BT_INDICATION;
+ ind->h.name = BT_NEW_STREAM;
+ ind->h.length = sizeof(*ind);
+
+ unix_ipc_sendmsg(client, &ind->h);
+
+ if (unix_sendmsg_fd(client->sock, client->data_fd) < 0) {
+ error("unix_sendmsg_fd: %s(%d)", strerror(errno), errno);
+ goto failed;
+ }
+
+ return;
+
+failed:
+ error("headset_resume_complete: resume failed");
+ unix_ipc_error(client, BT_START_STREAM, EIO);
+}
+
+static void gateway_resume_complete(struct audio_device *dev, GError *err, void *user_data)
+{
+ struct unix_client *client = user_data;
+ char buf[BT_SUGGESTED_BUFFER_SIZE];
+ struct bt_start_stream_rsp *rsp = (void *) buf;
+ struct bt_new_stream_ind *ind = (void *) buf;
+
+ if (err) {
+ unix_ipc_error(client, BT_START_STREAM, err->code);
+ return;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ rsp->h.type = BT_RESPONSE;
+ rsp->h.name = BT_START_STREAM;
+ rsp->h.length = sizeof(*rsp);
+
+ unix_ipc_sendmsg(client, &rsp->h);
+
+ memset(buf, 0, sizeof(buf));
+ ind->h.type = BT_INDICATION;
+ ind->h.name = BT_NEW_STREAM;
+ ind->h.length = sizeof(*ind);
+
+ unix_ipc_sendmsg(client, &ind->h);
+
+ client->data_fd = gateway_get_sco_fd(dev);
+ if (unix_sendmsg_fd(client->sock, client->data_fd) < 0) {
+ error("unix_sendmsg_fd: %s(%d)", strerror(errno), errno);
+ unix_ipc_error(client, BT_START_STREAM, EIO);
+ }
+
+ client->req_id = 0;
+}
+
+static void headset_suspend_complete(struct audio_device *dev, void *user_data)
+{
+ struct unix_client *client = user_data;
+ char buf[BT_SUGGESTED_BUFFER_SIZE];
+ struct bt_stop_stream_rsp *rsp = (void *) buf;
+
+ if (!dev)
+ goto failed;
+
+ memset(buf, 0, sizeof(buf));
+ rsp->h.type = BT_RESPONSE;
+ rsp->h.name = BT_STOP_STREAM;
+ rsp->h.length = sizeof(*rsp);
+
+ unix_ipc_sendmsg(client, &rsp->h);
+
+ return;
+
+failed:
+ error("suspend failed");
+ unix_ipc_error(client, BT_STOP_STREAM, EIO);
+}
+
+static void print_mpeg12(struct mpeg_codec_cap *mpeg)
+{
+ DBG("Media Codec: MPEG12"
+ " Channel Modes: %s%s%s%s"
+ " Frequencies: %s%s%s%s%s%s"
+ " Layers: %s%s%s"
+ " CRC: %s",
+ mpeg->channel_mode & MPEG_CHANNEL_MODE_MONO ? "Mono " : "",
+ mpeg->channel_mode & MPEG_CHANNEL_MODE_DUAL_CHANNEL ?
+ "DualChannel " : "",
+ mpeg->channel_mode & MPEG_CHANNEL_MODE_STEREO ? "Stereo " : "",
+ mpeg->channel_mode & MPEG_CHANNEL_MODE_JOINT_STEREO ?
+ "JointStereo " : "",
+ mpeg->frequency & MPEG_SAMPLING_FREQ_16000 ? "16Khz " : "",
+ mpeg->frequency & MPEG_SAMPLING_FREQ_22050 ? "22.05Khz " : "",
+ mpeg->frequency & MPEG_SAMPLING_FREQ_24000 ? "24Khz " : "",
+ mpeg->frequency & MPEG_SAMPLING_FREQ_32000 ? "32Khz " : "",
+ mpeg->frequency & MPEG_SAMPLING_FREQ_44100 ? "44.1Khz " : "",
+ mpeg->frequency & MPEG_SAMPLING_FREQ_48000 ? "48Khz " : "",
+ mpeg->layer & MPEG_LAYER_MP1 ? "1 " : "",
+ mpeg->layer & MPEG_LAYER_MP2 ? "2 " : "",
+ mpeg->layer & MPEG_LAYER_MP3 ? "3 " : "",
+ mpeg->crc ? "Yes" : "No");
+}
+
+static void print_sbc(struct sbc_codec_cap *sbc)
+{
+ DBG("Media Codec: SBC"
+ " Channel Modes: %s%s%s%s"
+ " Frequencies: %s%s%s%s"
+ " Subbands: %s%s"
+ " Blocks: %s%s%s%s"
+ " Bitpool: %d-%d",
+ sbc->channel_mode & SBC_CHANNEL_MODE_MONO ? "Mono " : "",
+ sbc->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL ?
+ "DualChannel " : "",
+ sbc->channel_mode & SBC_CHANNEL_MODE_STEREO ? "Stereo " : "",
+ sbc->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO ? "JointStereo" : "",
+ sbc->frequency & SBC_SAMPLING_FREQ_16000 ? "16Khz " : "",
+ sbc->frequency & SBC_SAMPLING_FREQ_32000 ? "32Khz " : "",
+ sbc->frequency & SBC_SAMPLING_FREQ_44100 ? "44.1Khz " : "",
+ sbc->frequency & SBC_SAMPLING_FREQ_48000 ? "48Khz " : "",
+ sbc->subbands & SBC_SUBBANDS_4 ? "4 " : "",
+ sbc->subbands & SBC_SUBBANDS_8 ? "8 " : "",
+ sbc->block_length & SBC_BLOCK_LENGTH_4 ? "4 " : "",
+ sbc->block_length & SBC_BLOCK_LENGTH_8 ? "8 " : "",
+ sbc->block_length & SBC_BLOCK_LENGTH_12 ? "12 " : "",
+ sbc->block_length & SBC_BLOCK_LENGTH_16 ? "16 " : "",
+ sbc->min_bitpool, sbc->max_bitpool);
+}
+
+static int a2dp_append_codec(struct bt_get_capabilities_rsp *rsp,
+ struct avdtp_service_capability *cap,
+ uint8_t seid,
+ uint8_t type,
+ uint8_t configured,
+ uint8_t lock)
+{
+ struct avdtp_media_codec_capability *codec_cap = (void *) cap->data;
+ codec_capabilities_t *codec = (void *) rsp + rsp->h.length;
+ size_t space_left;
+
+ if (rsp->h.length > BT_SUGGESTED_BUFFER_SIZE)
+ return -ENOMEM;
+
+ space_left = BT_SUGGESTED_BUFFER_SIZE - rsp->h.length;
+
+ /* endianess prevent direct cast */
+ if (codec_cap->media_codec_type == A2DP_CODEC_SBC) {
+ struct sbc_codec_cap *sbc_cap = (void *) codec_cap;
+ sbc_capabilities_t *sbc = (void *) codec;
+
+ if (space_left < sizeof(sbc_capabilities_t))
+ return -ENOMEM;
+
+ if (type == AVDTP_SEP_TYPE_SINK)
+ codec->type = BT_A2DP_SBC_SINK;
+ else if (type == AVDTP_SEP_TYPE_SOURCE)
+ codec->type = BT_A2DP_SBC_SOURCE;
+ else
+ return -EINVAL;
+
+ codec->length = sizeof(sbc_capabilities_t);
+
+ sbc->channel_mode = sbc_cap->channel_mode;
+ sbc->frequency = sbc_cap->frequency;
+ sbc->allocation_method = sbc_cap->allocation_method;
+ sbc->subbands = sbc_cap->subbands;
+ sbc->block_length = sbc_cap->block_length;
+ sbc->min_bitpool = sbc_cap->min_bitpool;
+ sbc->max_bitpool = sbc_cap->max_bitpool;
+
+ print_sbc(sbc_cap);
+ } else if (codec_cap->media_codec_type == A2DP_CODEC_MPEG12) {
+ struct mpeg_codec_cap *mpeg_cap = (void *) codec_cap;
+ mpeg_capabilities_t *mpeg = (void *) codec;
+
+ if (space_left < sizeof(mpeg_capabilities_t))
+ return -ENOMEM;
+
+ if (type == AVDTP_SEP_TYPE_SINK)
+ codec->type = BT_A2DP_MPEG12_SINK;
+ else if (type == AVDTP_SEP_TYPE_SOURCE)
+ codec->type = BT_A2DP_MPEG12_SOURCE;
+ else
+ return -EINVAL;
+
+ codec->length = sizeof(mpeg_capabilities_t);
+
+ mpeg->channel_mode = mpeg_cap->channel_mode;
+ mpeg->crc = mpeg_cap->crc;
+ mpeg->layer = mpeg_cap->layer;
+ mpeg->frequency = mpeg_cap->frequency;
+ mpeg->mpf = mpeg_cap->mpf;
+ mpeg->bitrate = mpeg_cap->bitrate;
+
+ print_mpeg12(mpeg_cap);
+ } else {
+ size_t codec_length, type_length, total_length;
+
+ codec_length = cap->length - (sizeof(struct avdtp_service_capability)
+ + sizeof(struct avdtp_media_codec_capability));
+ type_length = sizeof(codec_cap->media_codec_type);
+ total_length = type_length + codec_length +
+ sizeof(codec_capabilities_t);
+
+ if (space_left < total_length)
+ return -ENOMEM;
+
+ if (type == AVDTP_SEP_TYPE_SINK)
+ codec->type = BT_A2DP_UNKNOWN_SINK;
+ else if (type == AVDTP_SEP_TYPE_SOURCE)
+ codec->type = BT_A2DP_UNKNOWN_SOURCE;
+ else
+ return -EINVAL;
+
+ codec->length = total_length;
+ memcpy(codec->data, &codec_cap->media_codec_type, type_length);
+ memcpy(codec->data + type_length, codec_cap->data,
+ codec_length);
+ }
+
+ codec->seid = seid;
+ codec->configured = configured;
+ codec->lock = lock;
+ rsp->h.length += codec->length;
+
+ DBG("Append %s seid %d - length %d - total %d",
+ configured ? "configured" : "", seid, codec->length,
+ rsp->h.length);
+
+ return 0;
+}
+
+static void a2dp_discovery_complete(struct avdtp *session, GSList *seps,
+ struct avdtp_error *err,
+ void *user_data)
+{
+ struct unix_client *client = user_data;
+ char buf[BT_SUGGESTED_BUFFER_SIZE];
+ struct bt_get_capabilities_rsp *rsp = (void *) buf;
+ struct a2dp_data *a2dp = &client->d.a2dp;
+ GSList *l;
+
+ if (!g_slist_find(clients, client)) {
+ DBG("Client disconnected during discovery");
+ return;
+ }
+
+ if (err)
+ goto failed;
+
+ memset(buf, 0, sizeof(buf));
+ client->req_id = 0;
+
+ rsp->h.type = BT_RESPONSE;
+ rsp->h.name = BT_GET_CAPABILITIES;
+ rsp->h.length = sizeof(*rsp);
+ ba2str(&client->dev->src, rsp->source);
+ ba2str(&client->dev->dst, rsp->destination);
+ strncpy(rsp->object, client->dev->path, sizeof(rsp->object));
+
+ for (l = seps; l; l = g_slist_next(l)) {
+ struct avdtp_remote_sep *rsep = l->data;
+ struct a2dp_sep *sep;
+ struct avdtp_service_capability *cap;
+ struct avdtp_stream *stream;
+ uint8_t type, seid, configured = 0, lock = 0;
+ GSList *cl;
+
+ type = avdtp_get_type(rsep);
+
+ if (type != AVDTP_SEP_TYPE_SINK &&
+ type != AVDTP_SEP_TYPE_SOURCE)
+ continue;
+
+ cap = avdtp_get_codec(rsep);
+
+ if (cap->category != AVDTP_MEDIA_CODEC)
+ continue;
+
+ seid = avdtp_get_seid(rsep);
+
+ if (client->seid != 0 && client->seid != seid)
+ continue;
+
+ stream = avdtp_get_stream(rsep);
+ if (stream) {
+ configured = 1;
+ if (client->seid == seid)
+ cap = avdtp_stream_get_codec(stream);
+ }
+
+ for (cl = clients; cl; cl = cl->next) {
+ struct unix_client *c = cl->data;
+ struct a2dp_data *ca2dp = &c->d.a2dp;
+
+ if (ca2dp->session == session && c->seid == seid) {
+ lock = c->lock;
+ break;
+ }
+ }
+
+ sep = a2dp_get_sep(session, stream);
+ if (sep && a2dp_sep_get_lock(sep))
+ lock = BT_WRITE_LOCK;
+
+ a2dp_append_codec(rsp, cap, seid, type, configured, lock);
+ }
+
+ unix_ipc_sendmsg(client, &rsp->h);
+
+ return;
+
+failed:
+ error("discovery failed");
+ unix_ipc_error(client, BT_GET_CAPABILITIES, EIO);
+
+ if (a2dp->sep) {
+ a2dp_sep_unlock(a2dp->sep, a2dp->session);
+ a2dp->sep = NULL;
+ }
+
+ avdtp_unref(a2dp->session);
+ a2dp->session = NULL;
+ a2dp->stream = NULL;
+}
+
+static void a2dp_config_complete(struct avdtp *session, struct a2dp_sep *sep,
+ struct avdtp_stream *stream,
+ struct avdtp_error *err,
+ void *user_data)
+{
+ struct unix_client *client = user_data;
+ char buf[BT_SUGGESTED_BUFFER_SIZE];
+ struct bt_set_configuration_rsp *rsp = (void *) buf;
+ struct a2dp_data *a2dp = &client->d.a2dp;
+ uint16_t imtu, omtu;
+ GSList *caps;
+
+ client->req_id = 0;
+
+ if (err)
+ goto failed;
+
+ memset(buf, 0, sizeof(buf));
+
+ if (!stream)
+ goto failed;
+
+ if (client->cb_id > 0)
+ avdtp_stream_remove_cb(a2dp->session, a2dp->stream,
+ client->cb_id);
+
+ a2dp->sep = sep;
+ a2dp->stream = stream;
+
+ if (!avdtp_stream_get_transport(stream, &client->data_fd, &imtu, &omtu,
+ &caps)) {
+ error("Unable to get stream transport");
+ goto failed;
+ }
+
+ rsp->h.type = BT_RESPONSE;
+ rsp->h.name = BT_SET_CONFIGURATION;
+ rsp->h.length = sizeof(*rsp);
+
+ /* FIXME: Use imtu when fd_opt is CFG_FD_OPT_READ */
+ rsp->link_mtu = omtu;
+
+ unix_ipc_sendmsg(client, &rsp->h);
+
+ client->cb_id = avdtp_stream_add_cb(session, stream,
+ stream_state_changed, client);
+
+ return;
+
+failed:
+ error("config failed");
+
+ unix_ipc_error(client, BT_SET_CONFIGURATION, EIO);
+
+ avdtp_unref(a2dp->session);
+
+ a2dp->session = NULL;
+ a2dp->stream = NULL;
+ a2dp->sep = NULL;
+}
+
+static void a2dp_resume_complete(struct avdtp *session,
+ struct avdtp_error *err, void *user_data)
+{
+ struct unix_client *client = user_data;
+ char buf[BT_SUGGESTED_BUFFER_SIZE];
+ struct bt_start_stream_rsp *rsp = (void *) buf;
+ struct bt_new_stream_ind *ind = (void *) buf;
+ struct a2dp_data *a2dp = &client->d.a2dp;
+
+ if (err)
+ goto failed;
+
+ memset(buf, 0, sizeof(buf));
+ rsp->h.type = BT_RESPONSE;
+ rsp->h.name = BT_START_STREAM;
+ rsp->h.length = sizeof(*rsp);
+
+ unix_ipc_sendmsg(client, &rsp->h);
+
+ memset(buf, 0, sizeof(buf));
+ ind->h.type = BT_RESPONSE;
+ ind->h.name = BT_NEW_STREAM;
+ rsp->h.length = sizeof(*ind);
+
+ unix_ipc_sendmsg(client, &ind->h);
+
+ if (unix_sendmsg_fd(client->sock, client->data_fd) < 0) {
+ error("unix_sendmsg_fd: %s(%d)", strerror(errno), errno);
+ goto failed;
+ }
+
+ return;
+
+failed:
+ error("resume failed");
+
+ unix_ipc_error(client, BT_START_STREAM, EIO);
+
+ if (client->cb_id > 0) {
+ avdtp_stream_remove_cb(a2dp->session, a2dp->stream,
+ client->cb_id);
+ client->cb_id = 0;
+ }
+
+ if (a2dp->sep) {
+ a2dp_sep_unlock(a2dp->sep, a2dp->session);
+ a2dp->sep = NULL;
+ }
+
+ avdtp_unref(a2dp->session);
+ a2dp->session = NULL;
+ a2dp->stream = NULL;
+}
+
+static void a2dp_suspend_complete(struct avdtp *session,
+ struct avdtp_error *err, void *user_data)
+{
+ struct unix_client *client = user_data;
+ char buf[BT_SUGGESTED_BUFFER_SIZE];
+ struct bt_stop_stream_rsp *rsp = (void *) buf;
+
+ if (err)
+ goto failed;
+
+ memset(buf, 0, sizeof(buf));
+ rsp->h.type = BT_RESPONSE;
+ rsp->h.name = BT_STOP_STREAM;
+ rsp->h.length = sizeof(*rsp);
+
+ unix_ipc_sendmsg(client, &rsp->h);
+
+ return;
+
+failed:
+ error("suspend failed");
+
+ unix_ipc_error(client, BT_STOP_STREAM, EIO);
+}
+
+static void start_discovery(struct audio_device *dev, struct unix_client *client)
+{
+ struct a2dp_data *a2dp;
+ int err = 0;
+
+ switch (client->type) {
+ case TYPE_SINK:
+ case TYPE_SOURCE:
+ a2dp = &client->d.a2dp;
+
+ if (!a2dp->session)
+ a2dp->session = avdtp_get(&dev->src, &dev->dst);
+
+ if (!a2dp->session) {
+ error("Unable to get a session");
+ goto failed;
+ }
+
+ err = avdtp_discover(a2dp->session, a2dp_discovery_complete,
+ client);
+ if (err) {
+ if (a2dp->session) {
+ avdtp_unref(a2dp->session);
+ a2dp->session = NULL;
+ }
+ goto failed;
+ }
+ break;
+
+ case TYPE_HEADSET:
+ case TYPE_GATEWAY:
+ headset_discovery_complete(dev, client);
+ break;
+
+ default:
+ error("No known services for device");
+ goto failed;
+ }
+
+ client->dev = dev;
+
+ return;
+
+failed:
+ unix_ipc_error(client, BT_GET_CAPABILITIES, err ? : EIO);
+}
+
+static void open_complete(struct audio_device *dev, void *user_data)
+{
+ struct unix_client *client = user_data;
+ char buf[BT_SUGGESTED_BUFFER_SIZE];
+ struct bt_open_rsp *rsp = (void *) buf;
+
+ memset(buf, 0, sizeof(buf));
+
+ rsp->h.type = BT_RESPONSE;
+ rsp->h.name = BT_OPEN;
+ rsp->h.length = sizeof(*rsp);
+
+ ba2str(&dev->src, rsp->source);
+ ba2str(&dev->dst, rsp->destination);
+ strncpy(rsp->object, dev->path, sizeof(rsp->object));
+
+ unix_ipc_sendmsg(client, &rsp->h);
+}
+
+static void start_open(struct audio_device *dev, struct unix_client *client)
+{
+ struct a2dp_data *a2dp;
+ struct headset_data *hs;
+ struct avdtp_remote_sep *rsep;
+ gboolean unref_avdtp_on_fail = FALSE;
+
+ switch (client->type) {
+ case TYPE_SINK:
+ case TYPE_SOURCE:
+ a2dp = &client->d.a2dp;
+
+ if (!a2dp->session) {
+ a2dp->session = avdtp_get(&dev->src, &dev->dst);
+ unref_avdtp_on_fail = TRUE;
+ }
+
+ if (!a2dp->session) {
+ error("Unable to get a session");
+ goto failed;
+ }
+
+ if (a2dp->sep) {
+ error("Client already has an opened session");
+ goto failed;
+ }
+
+ rsep = avdtp_get_remote_sep(a2dp->session, client->seid);
+ if (!rsep) {
+ error("Invalid seid %d", client->seid);
+ goto failed;
+ }
+
+ a2dp->sep = a2dp_get(a2dp->session, rsep);
+ if (!a2dp->sep) {
+ error("seid %d not available or locked", client->seid);
+ goto failed;
+ }
+
+ if (!a2dp_sep_lock(a2dp->sep, a2dp->session)) {
+ error("Unable to open seid %d", client->seid);
+ a2dp->sep = NULL;
+ goto failed;
+ }
+
+ break;
+
+ case TYPE_HEADSET:
+ hs = &client->d.hs;
+
+ if (hs->locked) {
+ error("Client already has an opened session");
+ goto failed;
+ }
+
+ hs->locked = headset_lock(dev, client->lock);
+ if (!hs->locked) {
+ error("Unable to open seid %d", client->seid);
+ goto failed;
+ }
+ break;
+
+ case TYPE_GATEWAY:
+ break;
+ default:
+ error("No known services for device");
+ goto failed;
+ }
+
+ client->dev = dev;
+
+ open_complete(dev, client);
+
+ return;
+
+failed:
+ if (unref_avdtp_on_fail && a2dp->session) {
+ avdtp_unref(a2dp->session);
+ a2dp->session = NULL;
+ }
+ unix_ipc_error(client, BT_OPEN, EINVAL);
+}
+
+static void start_config(struct audio_device *dev, struct unix_client *client)
+{
+ struct a2dp_data *a2dp;
+ struct headset_data *hs;
+ unsigned int id;
+
+ switch (client->type) {
+ case TYPE_SINK:
+ case TYPE_SOURCE:
+ a2dp = &client->d.a2dp;
+
+ if (!a2dp->session)
+ a2dp->session = avdtp_get(&dev->src, &dev->dst);
+
+ if (!a2dp->session) {
+ error("Unable to get a session");
+ goto failed;
+ }
+
+ if (!a2dp->sep) {
+ error("seid %d not opened", client->seid);
+ goto failed;
+ }
+
+ id = a2dp_config(a2dp->session, a2dp->sep, a2dp_config_complete,
+ client->caps, client);
+ client->cancel = a2dp_cancel;
+ break;
+
+ case TYPE_HEADSET:
+ hs = &client->d.hs;
+
+ if (!hs->locked) {
+ error("seid %d not opened", client->seid);
+ goto failed;
+ }
+
+ id = headset_config_stream(dev, TRUE, headset_setup_complete,
+ client);
+ client->cancel = headset_cancel_stream;
+ break;
+ case TYPE_GATEWAY:
+ if (gateway_config_stream(dev, gateway_setup_complete, client) >= 0) {
+ client->cancel = gateway_cancel_stream;
+ id = 1;
+ } else
+ id = 0;
+ break;
+
+ default:
+ error("No known services for device");
+ goto failed;
+ }
+
+ if (id == 0) {
+ error("config failed");
+ goto failed;
+ }
+
+ client->req_id = id;
+ g_slist_free(client->caps);
+ client->caps = NULL;
+
+ return;
+
+failed:
+ unix_ipc_error(client, BT_SET_CONFIGURATION, EIO);
+}
+
+static void start_resume(struct audio_device *dev, struct unix_client *client)
+{
+ struct a2dp_data *a2dp = NULL;
+ struct headset_data *hs;
+ unsigned int id;
+ struct avdtp *session = NULL;
+
+ switch (client->type) {
+ case TYPE_SINK:
+ case TYPE_SOURCE:
+ a2dp = &client->d.a2dp;
+
+ if (!a2dp->sep) {
+ error("seid not opened");
+ goto failed;
+ }
+
+ if (!a2dp->session) {
+ session = avdtp_get(&dev->src, &dev->dst);
+ if (!session) {
+ error("Unable to get a session");
+ goto failed;
+ }
+ a2dp->session = session;
+ }
+
+ id = a2dp_resume(a2dp->session, a2dp->sep, a2dp_resume_complete,
+ client);
+ client->cancel = a2dp_cancel;
+
+ break;
+
+ case TYPE_HEADSET:
+ hs = &client->d.hs;
+
+ if (!hs->locked) {
+ error("seid not opened");
+ goto failed;
+ }
+
+ id = headset_request_stream(dev, headset_resume_complete,
+ client);
+ client->cancel = headset_cancel_stream;
+ break;
+
+ case TYPE_GATEWAY:
+ if (gateway_request_stream(dev, gateway_resume_complete, client))
+ id = 1;
+ else
+ id = 0;
+ client->cancel = gateway_cancel_stream;
+ break;
+
+ default:
+ error("No known services for device");
+ goto failed;
+ }
+
+ if (id == 0) {
+ error("start_resume: resume failed");
+ goto failed;
+ }
+
+ client->req_id = id;
+
+ return;
+
+failed:
+ if (session) {
+ avdtp_unref(session);
+ a2dp->session = NULL;
+ }
+
+ unix_ipc_error(client, BT_START_STREAM, EIO);
+}
+
+static void start_suspend(struct audio_device *dev, struct unix_client *client)
+{
+ struct a2dp_data *a2dp = NULL;
+ struct headset_data *hs;
+ unsigned int id;
+ struct avdtp *session = NULL;
+
+ switch (client->type) {
+ case TYPE_SINK:
+ case TYPE_SOURCE:
+ a2dp = &client->d.a2dp;
+
+ if (!a2dp->sep) {
+ error("seid not opened");
+ goto failed;
+ }
+
+ if (!a2dp->session) {
+ session = avdtp_get(&dev->src, &dev->dst);
+ if (!session) {
+ error("Unable to get a session");
+ goto failed;
+ }
+ a2dp->session = session;
+ }
+
+ if (!a2dp->sep) {
+ error("Unable to get a sep");
+ goto failed;
+ }
+
+ id = a2dp_suspend(a2dp->session, a2dp->sep,
+ a2dp_suspend_complete, client);
+ client->cancel = a2dp_cancel;
+ break;
+
+ case TYPE_HEADSET:
+ hs = &client->d.hs;
+
+ if (!hs->locked) {
+ error("seid not opened");
+ goto failed;
+ }
+
+ id = headset_suspend_stream(dev, headset_suspend_complete,
+ client);
+ client->cancel = headset_cancel_stream;
+ break;
+
+ case TYPE_GATEWAY:
+ gateway_suspend_stream(dev);
+ client->cancel = gateway_cancel_stream;
+ headset_suspend_complete(dev, client);
+ id = 1;
+ break;
+
+ default:
+ error("No known services for device");
+ goto failed;
+ }
+
+ if (id == 0) {
+ error("suspend failed");
+ goto failed;
+ }
+
+ return;
+
+failed:
+ if (session) {
+ avdtp_unref(session);
+ a2dp->session = NULL;
+ }
+
+ unix_ipc_error(client, BT_STOP_STREAM, EIO);
+}
+
+static void close_complete(struct audio_device *dev, void *user_data)
+{
+ struct unix_client *client = user_data;
+ char buf[BT_SUGGESTED_BUFFER_SIZE];
+ struct bt_close_rsp *rsp = (void *) buf;
+
+ memset(buf, 0, sizeof(buf));
+
+ rsp->h.type = BT_RESPONSE;
+ rsp->h.name = BT_CLOSE;
+ rsp->h.length = sizeof(*rsp);
+
+ unix_ipc_sendmsg(client, &rsp->h);
+
+ return;
+}
+
+static void start_close(struct audio_device *dev, struct unix_client *client,
+ gboolean reply)
+{
+ struct a2dp_data *a2dp;
+ struct headset_data *hs;
+
+ if (!client->dev)
+ goto failed;
+
+ switch (client->type) {
+ case TYPE_HEADSET:
+ hs = &client->d.hs;
+
+ if (client->dev && hs->locked) {
+ headset_unlock(client->dev, client->lock);
+ hs->locked = FALSE;
+ }
+ break;
+ case TYPE_GATEWAY:
+ break;
+ case TYPE_SOURCE:
+ case TYPE_SINK:
+ a2dp = &client->d.a2dp;
+
+ if (client->cb_id > 0) {
+ avdtp_stream_remove_cb(a2dp->session, a2dp->stream,
+ client->cb_id);
+ client->cb_id = 0;
+ }
+ if (a2dp->sep) {
+ a2dp_sep_unlock(a2dp->sep, a2dp->session);
+ a2dp->sep = NULL;
+ }
+ if (a2dp->session) {
+ avdtp_unref(a2dp->session);
+ a2dp->session = NULL;
+ }
+ a2dp->stream = NULL;
+ break;
+ default:
+ error("No known services for device");
+ goto failed;
+ }
+
+ if (!reply)
+ return;
+
+ close_complete(dev, client);
+ client->dev = NULL;
+
+ return;
+
+failed:
+ if (reply)
+ unix_ipc_error(client, BT_STOP_STREAM, EINVAL);
+}
+
+static void handle_getcapabilities_req(struct unix_client *client,
+ struct bt_get_capabilities_req *req)
+{
+ struct audio_device *dev;
+ bdaddr_t src, dst;
+ int err = EIO;
+ const char *interface;
+
+ if (!check_nul(req->source) || !check_nul(req->destination) ||
+ !check_nul(req->object)) {
+ err = EINVAL;
+ goto failed;
+ }
+
+ str2ba(req->source, &src);
+ str2ba(req->destination, &dst);
+
+ if (!manager_find_device(req->object, &src, &dst, NULL, FALSE))
+ goto failed;
+
+ if (req->transport == BT_CAPABILITIES_TRANSPORT_SCO)
+ interface = AUDIO_HEADSET_INTERFACE;
+ else if (req->transport == BT_CAPABILITIES_TRANSPORT_A2DP)
+ interface = AUDIO_SINK_INTERFACE;
+ else
+ interface = client->interface;
+
+ dev = manager_find_device(req->object, &src, &dst, interface, TRUE);
+ if (!dev && (req->flags & BT_FLAG_AUTOCONNECT))
+ dev = manager_find_device(req->object, &src, &dst,
+ interface, FALSE);
+
+ if (!dev) {
+ if (req->transport == BT_CAPABILITIES_TRANSPORT_SCO)
+ interface = AUDIO_GATEWAY_INTERFACE;
+ else if (req->transport == BT_CAPABILITIES_TRANSPORT_A2DP)
+ interface = AUDIO_SOURCE_INTERFACE;
+ else
+ interface = NULL;
+ dev = manager_find_device(req->object, &src, &dst,
+ interface, TRUE);
+ if (!dev && (req->flags & BT_FLAG_AUTOCONNECT))
+ dev = manager_find_device(req->object, &src, &dst,
+ interface, FALSE);
+ }
+
+ if (!dev) {
+ error("Unable to find a matching device");
+ goto failed;
+ }
+
+ client->type = select_service(dev, interface);
+ if (client->type == TYPE_NONE) {
+ error("No matching service found");
+ goto failed;
+ }
+
+ if (g_strcmp0(interface, client->interface) != 0) {
+ g_free(client->interface);
+ client->interface = g_strdup(interface);
+ }
+
+ client->seid = req->seid;
+
+ start_discovery(dev, client);
+
+ return;
+
+failed:
+ unix_ipc_error(client, BT_GET_CAPABILITIES, err);
+}
+
+static int handle_sco_open(struct unix_client *client, struct bt_open_req *req)
+{
+ if (!client->interface)
+ client->interface = g_strdup(AUDIO_HEADSET_INTERFACE);
+ else if (!g_str_equal(client->interface, AUDIO_HEADSET_INTERFACE) &&
+ !g_str_equal(client->interface, AUDIO_GATEWAY_INTERFACE))
+ return -EIO;
+
+ DBG("open sco - object=%s source=%s destination=%s lock=%s%s",
+ strcmp(req->object, "") ? req->object : "ANY",
+ strcmp(req->source, "") ? req->source : "ANY",
+ strcmp(req->destination, "") ? req->destination : "ANY",
+ req->lock & BT_READ_LOCK ? "read" : "",
+ req->lock & BT_WRITE_LOCK ? "write" : "");
+
+ return 0;
+}
+
+static int handle_a2dp_open(struct unix_client *client, struct bt_open_req *req)
+{
+ if (!client->interface)
+ /* FIXME: are we treating a sink or a source? */
+ client->interface = g_strdup(AUDIO_SINK_INTERFACE);
+ else if (!g_str_equal(client->interface, AUDIO_SINK_INTERFACE) &&
+ !g_str_equal(client->interface, AUDIO_SOURCE_INTERFACE))
+ return -EIO;
+
+ DBG("open a2dp - object=%s source=%s destination=%s lock=%s%s",
+ strcmp(req->object, "") ? req->object : "ANY",
+ strcmp(req->source, "") ? req->source : "ANY",
+ strcmp(req->destination, "") ? req->destination : "ANY",
+ req->lock & BT_READ_LOCK ? "read" : "",
+ req->lock & BT_WRITE_LOCK ? "write" : "");
+
+ return 0;
+}
+
+static void handle_open_req(struct unix_client *client, struct bt_open_req *req)
+{
+ struct audio_device *dev;
+ bdaddr_t src, dst;
+ int err = 0;
+
+ if (!check_nul(req->source) || !check_nul(req->destination) ||
+ !check_nul(req->object)) {
+ err = EINVAL;
+ goto failed;
+ }
+
+ str2ba(req->source, &src);
+ str2ba(req->destination, &dst);
+
+ if (req->seid > BT_A2DP_SEID_RANGE) {
+ err = handle_sco_open(client, req);
+ if (err < 0) {
+ err = -err;
+ goto failed;
+ }
+ } else {
+ err = handle_a2dp_open(client, req);
+ if (err < 0) {
+ err = -err;
+ goto failed;
+ }
+ }
+
+ if (!manager_find_device(req->object, &src, &dst, NULL, FALSE))
+ goto failed;
+
+ dev = manager_find_device(req->object, &src, &dst, client->interface,
+ TRUE);
+ if (!dev)
+ dev = manager_find_device(req->object, &src, &dst,
+ client->interface, FALSE);
+
+ if (!dev)
+ goto failed;
+
+ client->seid = req->seid;
+ client->lock = req->lock;
+
+ start_open(dev, client);
+
+ return;
+
+failed:
+ unix_ipc_error(client, BT_OPEN, err ? : EIO);
+}
+
+static int handle_sco_transport(struct unix_client *client,
+ struct bt_set_configuration_req *req)
+{
+ struct audio_device *dev = client->dev;
+
+ if (!client->interface) {
+ if (dev->headset)
+ client->interface = g_strdup(AUDIO_HEADSET_INTERFACE);
+ else if (dev->gateway)
+ client->interface = g_strdup(AUDIO_GATEWAY_INTERFACE);
+ else
+ return -EIO;
+ } else if (!g_str_equal(client->interface, AUDIO_HEADSET_INTERFACE) &&
+ !g_str_equal(client->interface, AUDIO_GATEWAY_INTERFACE))
+ return -EIO;
+
+ return 0;
+}
+
+static int handle_a2dp_transport(struct unix_client *client,
+ struct bt_set_configuration_req *req)
+{
+ struct avdtp_service_capability *media_transport, *media_codec;
+ struct sbc_codec_cap sbc_cap;
+ struct mpeg_codec_cap mpeg_cap;
+
+ if (!client->interface)
+ /* FIXME: are we treating a sink or a source? */
+ client->interface = g_strdup(AUDIO_SINK_INTERFACE);
+ else if (!g_str_equal(client->interface, AUDIO_SINK_INTERFACE) &&
+ !g_str_equal(client->interface, AUDIO_SOURCE_INTERFACE))
+ return -EIO;
+
+ if (client->caps) {
+ g_slist_foreach(client->caps, (GFunc) g_free, NULL);
+ g_slist_free(client->caps);
+ client->caps = NULL;
+ }
+
+ media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
+ NULL, 0);
+
+ client->caps = g_slist_append(client->caps, media_transport);
+
+ if (req->codec.type == BT_A2DP_MPEG12_SINK ||
+ req->codec.type == BT_A2DP_MPEG12_SOURCE) {
+ mpeg_capabilities_t *mpeg = (void *) &req->codec;
+
+ memset(&mpeg_cap, 0, sizeof(mpeg_cap));
+
+ mpeg_cap.cap.media_type = AVDTP_MEDIA_TYPE_AUDIO;
+ mpeg_cap.cap.media_codec_type = A2DP_CODEC_MPEG12;
+ mpeg_cap.channel_mode = mpeg->channel_mode;
+ mpeg_cap.crc = mpeg->crc;
+ mpeg_cap.layer = mpeg->layer;
+ mpeg_cap.frequency = mpeg->frequency;
+ mpeg_cap.mpf = mpeg->mpf;
+ mpeg_cap.bitrate = mpeg->bitrate;
+
+ media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &mpeg_cap,
+ sizeof(mpeg_cap));
+
+ print_mpeg12(&mpeg_cap);
+ } else if (req->codec.type == BT_A2DP_SBC_SINK ||
+ req->codec.type == BT_A2DP_SBC_SOURCE) {
+ sbc_capabilities_t *sbc = (void *) &req->codec;
+
+ memset(&sbc_cap, 0, sizeof(sbc_cap));
+
+ sbc_cap.cap.media_type = AVDTP_MEDIA_TYPE_AUDIO;
+ sbc_cap.cap.media_codec_type = A2DP_CODEC_SBC;
+ sbc_cap.channel_mode = sbc->channel_mode;
+ sbc_cap.frequency = sbc->frequency;
+ sbc_cap.allocation_method = sbc->allocation_method;
+ sbc_cap.subbands = sbc->subbands;
+ sbc_cap.block_length = sbc->block_length;
+ sbc_cap.min_bitpool = sbc->min_bitpool;
+ sbc_cap.max_bitpool = sbc->max_bitpool;
+
+ media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap,
+ sizeof(sbc_cap));
+
+ print_sbc(&sbc_cap);
+ } else
+ return -EINVAL;
+
+ client->caps = g_slist_append(client->caps, media_codec);
+
+ return 0;
+}
+
+static void handle_setconfiguration_req(struct unix_client *client,
+ struct bt_set_configuration_req *req)
+{
+ int err = 0;
+
+ if (req->codec.seid != client->seid) {
+ error("Unable to set configuration: seid %d not opened",
+ req->codec.seid);
+ goto failed;
+ }
+
+ if (!client->dev)
+ goto failed;
+
+ if (req->codec.transport == BT_CAPABILITIES_TRANSPORT_SCO) {
+ err = handle_sco_transport(client, req);
+ if (err < 0) {
+ err = -err;
+ goto failed;
+ }
+ } else if (req->codec.transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
+ err = handle_a2dp_transport(client, req);
+ if (err < 0) {
+ err = -err;
+ goto failed;
+ }
+ }
+
+ start_config(client->dev, client);
+
+ return;
+
+failed:
+ unix_ipc_error(client, BT_SET_CONFIGURATION, err ? : EIO);
+}
+
+static void handle_streamstart_req(struct unix_client *client,
+ struct bt_start_stream_req *req)
+{
+ if (!client->dev)
+ goto failed;
+
+ start_resume(client->dev, client);
+
+ return;
+
+failed:
+ unix_ipc_error(client, BT_START_STREAM, EIO);
+}
+
+static void handle_streamstop_req(struct unix_client *client,
+ struct bt_stop_stream_req *req)
+{
+ if (!client->dev)
+ goto failed;
+
+ start_suspend(client->dev, client);
+
+ return;
+
+failed:
+ unix_ipc_error(client, BT_STOP_STREAM, EIO);
+}
+
+static void handle_close_req(struct unix_client *client,
+ struct bt_close_req *req)
+{
+ if (!client->dev)
+ goto failed;
+
+ start_close(client->dev, client, TRUE);
+
+ return;
+
+failed:
+ unix_ipc_error(client, BT_CLOSE, EIO);
+}
+
+static void handle_control_req(struct unix_client *client,
+ struct bt_control_req *req)
+{
+ /* FIXME: really implement that */
+ char buf[BT_SUGGESTED_BUFFER_SIZE];
+ struct bt_set_configuration_rsp *rsp = (void *) buf;
+
+ memset(buf, 0, sizeof(buf));
+ rsp->h.type = BT_RESPONSE;
+ rsp->h.name = BT_CONTROL;
+ rsp->h.length = sizeof(*rsp);
+
+ unix_ipc_sendmsg(client, &rsp->h);
+}
+
+static void handle_delay_report_req(struct unix_client *client,
+ struct bt_delay_report_req *req)
+{
+ char buf[BT_SUGGESTED_BUFFER_SIZE];
+ struct bt_set_configuration_rsp *rsp = (void *) buf;
+ struct a2dp_data *a2dp;
+ int err;
+
+ if (!client->dev) {
+ err = -ENODEV;
+ goto failed;
+ }
+
+ switch (client->type) {
+ case TYPE_HEADSET:
+ case TYPE_GATEWAY:
+ err = -EINVAL;
+ goto failed;
+ case TYPE_SOURCE:
+ case TYPE_SINK:
+ a2dp = &client->d.a2dp;
+ if (a2dp->session && a2dp->stream) {
+ err = avdtp_delay_report(a2dp->session, a2dp->stream,
+ req->delay);
+ if (err < 0)
+ goto failed;
+ } else {
+ err = -EINVAL;
+ goto failed;
+ }
+ break;
+ default:
+ error("No known services for device");
+ err = -EINVAL;
+ goto failed;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ rsp->h.type = BT_RESPONSE;
+ rsp->h.name = BT_DELAY_REPORT;
+ rsp->h.length = sizeof(*rsp);
+
+ unix_ipc_sendmsg(client, &rsp->h);
+
+ return;
+
+failed:
+ unix_ipc_error(client, BT_DELAY_REPORT, -err);
+}
+
+static gboolean client_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+ char buf[BT_SUGGESTED_BUFFER_SIZE];
+ bt_audio_msg_header_t *msghdr = (void *) buf;
+ struct unix_client *client = data;
+ int len;
+ const char *type, *name;
+
+ if (cond & G_IO_NVAL)
+ return FALSE;
+
+ if (cond & (G_IO_HUP | G_IO_ERR)) {
+ DBG("Unix client disconnected (fd=%d)", client->sock);
+
+ goto failed;
+ }
+
+ memset(buf, 0, sizeof(buf));
+
+ len = recv(client->sock, buf, sizeof(buf), 0);
+ if (len < 0) {
+ error("recv: %s (%d)", strerror(errno), errno);
+ goto failed;
+ }
+
+ type = bt_audio_strtype(msghdr->type);
+ name = bt_audio_strname(msghdr->name);
+
+ DBG("Audio API: %s <- %s", type, name);
+
+ if (msghdr->length != len) {
+ error("Invalid message: length mismatch");
+ goto failed;
+ }
+
+ switch (msghdr->name) {
+ case BT_GET_CAPABILITIES:
+ handle_getcapabilities_req(client,
+ (struct bt_get_capabilities_req *) msghdr);
+ break;
+ case BT_OPEN:
+ handle_open_req(client,
+ (struct bt_open_req *) msghdr);
+ break;
+ case BT_SET_CONFIGURATION:
+ handle_setconfiguration_req(client,
+ (struct bt_set_configuration_req *) msghdr);
+ break;
+ case BT_START_STREAM:
+ handle_streamstart_req(client,
+ (struct bt_start_stream_req *) msghdr);
+ break;
+ case BT_STOP_STREAM:
+ handle_streamstop_req(client,
+ (struct bt_stop_stream_req *) msghdr);
+ break;
+ case BT_CLOSE:
+ handle_close_req(client,
+ (struct bt_close_req *) msghdr);
+ break;
+ case BT_CONTROL:
+ handle_control_req(client,
+ (struct bt_control_req *) msghdr);
+ break;
+ case BT_DELAY_REPORT:
+ handle_delay_report_req(client,
+ (struct bt_delay_report_req *) msghdr);
+ break;
+ default:
+ error("Audio API: received unexpected message name %d",
+ msghdr->name);
+ }
+
+ return TRUE;
+
+failed:
+ clients = g_slist_remove(clients, client);
+ start_close(client->dev, client, FALSE);
+ client_free(client);
+ return FALSE;
+}
+
+static gboolean server_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+ struct sockaddr_un addr;
+ socklen_t addrlen;
+ int sk, cli_sk;
+ struct unix_client *client;
+ GIOChannel *io;
+
+ if (cond & G_IO_NVAL)
+ return FALSE;
+
+ if (cond & (G_IO_HUP | G_IO_ERR)) {
+ g_io_channel_shutdown(chan, TRUE, NULL);
+ return FALSE;
+ }
+
+ sk = g_io_channel_unix_get_fd(chan);
+
+ memset(&addr, 0, sizeof(addr));
+ addrlen = sizeof(addr);
+
+ cli_sk = accept(sk, (struct sockaddr *) &addr, &addrlen);
+ if (cli_sk < 0) {
+ error("accept: %s (%d)", strerror(errno), errno);
+ return TRUE;
+ }
+
+ DBG("Accepted new client connection on unix socket (fd=%d)", cli_sk);
+ set_nonblocking(cli_sk);
+
+ client = g_new0(struct unix_client, 1);
+ client->sock = cli_sk;
+ clients = g_slist_append(clients, client);
+
+ io = g_io_channel_unix_new(cli_sk);
+ g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ client_cb, client);
+ g_io_channel_unref(io);
+
+ return TRUE;
+}
+
+void unix_device_removed(struct audio_device *dev)
+{
+ GSList *l;
+
+ DBG("unix_device_removed(%p)", dev);
+
+ l = clients;
+ while (l) {
+ struct unix_client *client = l->data;
+
+ l = l->next;
+
+ if (client->dev == dev) {
+ clients = g_slist_remove(clients, client);
+ start_close(client->dev, client, FALSE);
+ client_free(client);
+ }
+ }
+}
+
+void unix_delay_report(struct audio_device *dev, uint8_t seid, uint16_t delay)
+{
+ GSList *l;
+ struct bt_delay_report_ind ind;
+
+ DBG("unix_delay_report(%p): %u.%ums", dev, delay / 10, delay % 10);
+
+ memset(&ind, 0, sizeof(ind));
+ ind.h.type = BT_INDICATION;
+ ind.h.name = BT_DELAY_REPORT;
+ ind.h.length = sizeof(ind);
+ ind.delay = delay;
+
+ for (l = clients; l != NULL; l = g_slist_next(l)) {
+ struct unix_client *client = l->data;
+
+ if (client->dev != dev || client->seid != seid)
+ continue;
+
+ unix_ipc_sendmsg(client, (void *) &ind);
+ }
+}
+
+int unix_init(void)
+{
+ GIOChannel *io;
+ struct sockaddr_un addr = {
+ AF_UNIX, BT_IPC_SOCKET_NAME
+ };
+
+ int sk, err;
+
+ sk = socket(PF_LOCAL, SOCK_STREAM, 0);
+ if (sk < 0) {
+ err = errno;
+ error("Can't create unix socket: %s (%d)", strerror(err), err);
+ return -err;
+ }
+
+ if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ error("Can't bind unix socket: %s (%d)", strerror(errno),
+ errno);
+ close(sk);
+ return -1;
+ }
+
+ set_nonblocking(sk);
+
+ if (listen(sk, 1) < 0) {
+ error("Can't listen on unix socket: %s (%d)",
+ strerror(errno), errno);
+ close(sk);
+ return -1;
+ }
+
+ unix_sock = sk;
+
+ io = g_io_channel_unix_new(sk);
+ g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ server_cb, NULL);
+ g_io_channel_unref(io);
+
+ DBG("Unix socket created: %d", sk);
+
+ return 0;
+}
+
+void unix_exit(void)
+{
+ g_slist_foreach(clients, (GFunc) client_free, NULL);
+ g_slist_free(clients);
+ if (unix_sock >= 0) {
+ close(unix_sock);
+ unix_sock = -1;
+ }
+}
diff --git a/audio/unix.h b/audio/unix.h
new file mode 100644
index 0000000..74ca16d
--- /dev/null
+++ b/audio/unix.h
@@ -0,0 +1,30 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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
+ *
+ */
+
+void unix_device_removed(struct audio_device *dev);
+
+void unix_delay_report(struct audio_device *dev, uint8_t seid, uint16_t delay);
+
+int unix_init(void);
+void unix_exit(void);
diff --git a/bluez.pc.in b/bluez.pc.in
new file mode 100644
index 0000000..3d6e596
--- /dev/null
+++ b/bluez.pc.in
@@ -0,0 +1,10 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: BlueZ
+Description: Bluetooth protocol stack for Linux
+Version: @VERSION@
+Libs: -L${libdir} -lbluetooth
+Cflags: -I${includedir}
diff --git a/btio/btio.c b/btio/btio.c
new file mode 100644
index 0000000..3f5b69a
--- /dev/null
+++ b/btio/btio.c
@@ -0,0 +1,1319 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2009-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2009-2010 Nokia Corporation
+ *
+ *
+ * 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
+ *
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <poll.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/sco.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include <glib.h>
+
+#include "btio.h"
+
+#define ERROR_FAILED(gerr, str, err) \
+ g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_FAILED, \
+ str ": %s (%d)", strerror(err), err)
+
+#define DEFAULT_DEFER_TIMEOUT 30
+
+struct set_opts {
+ bdaddr_t src;
+ bdaddr_t dst;
+ int defer;
+ int sec_level;
+ uint8_t channel;
+ uint16_t psm;
+ uint16_t cid;
+ uint16_t mtu;
+ uint16_t imtu;
+ uint16_t omtu;
+ int master;
+ uint8_t mode;
+};
+
+struct connect {
+ BtIOConnect connect;
+ gpointer user_data;
+ GDestroyNotify destroy;
+};
+
+struct accept {
+ BtIOConnect connect;
+ gpointer user_data;
+ GDestroyNotify destroy;
+};
+
+struct server {
+ BtIOConnect connect;
+ BtIOConfirm confirm;
+ gpointer user_data;
+ GDestroyNotify destroy;
+};
+
+static void server_remove(struct server *server)
+{
+ if (server->destroy)
+ server->destroy(server->user_data);
+ g_free(server);
+}
+
+static void connect_remove(struct connect *conn)
+{
+ if (conn->destroy)
+ conn->destroy(conn->user_data);
+ g_free(conn);
+}
+
+static void accept_remove(struct accept *accept)
+{
+ if (accept->destroy)
+ accept->destroy(accept->user_data);
+ g_free(accept);
+}
+
+static gboolean check_nval(GIOChannel *io)
+{
+ struct pollfd fds;
+
+ memset(&fds, 0, sizeof(fds));
+ fds.fd = g_io_channel_unix_get_fd(io);
+ fds.events = POLLNVAL;
+
+ if (poll(&fds, 1, 0) > 0 && (fds.revents & POLLNVAL))
+ return TRUE;
+
+ return FALSE;
+}
+
+static gboolean accept_cb(GIOChannel *io, GIOCondition cond,
+ gpointer user_data)
+{
+ struct accept *accept = user_data;
+ GError *err = NULL;
+
+ /* If the user aborted this accept attempt */
+ if ((cond & G_IO_NVAL) || check_nval(io))
+ return FALSE;
+
+ if (cond & (G_IO_HUP | G_IO_ERR))
+ g_set_error(&err, BT_IO_ERROR, BT_IO_ERROR_DISCONNECTED,
+ "HUP or ERR on socket");
+
+ accept->connect(io, err, accept->user_data);
+
+ g_clear_error(&err);
+
+ return FALSE;
+}
+
+static gboolean connect_cb(GIOChannel *io, GIOCondition cond,
+ gpointer user_data)
+{
+ struct connect *conn = user_data;
+ GError *gerr = NULL;
+
+ /* If the user aborted this connect attempt */
+ if ((cond & G_IO_NVAL) || check_nval(io))
+ return FALSE;
+
+ if (cond & G_IO_OUT) {
+ int err = 0, sock = g_io_channel_unix_get_fd(io);
+ socklen_t len = sizeof(err);
+
+ if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len) < 0)
+ err = errno;
+
+ if (err)
+ g_set_error(&gerr, BT_IO_ERROR,
+ BT_IO_ERROR_CONNECT_FAILED, "%s (%d)",
+ strerror(err), err);
+ } else if (cond & (G_IO_HUP | G_IO_ERR))
+ g_set_error(&gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED,
+ "HUP or ERR on socket");
+
+ conn->connect(io, gerr, conn->user_data);
+
+ if (gerr)
+ g_error_free(gerr);
+
+ return FALSE;
+}
+
+static gboolean server_cb(GIOChannel *io, GIOCondition cond,
+ gpointer user_data)
+{
+ struct server *server = user_data;
+ int srv_sock, cli_sock;
+ GIOChannel *cli_io;
+
+ /* If the user closed the server */
+ if ((cond & G_IO_NVAL) || check_nval(io))
+ return FALSE;
+
+ srv_sock = g_io_channel_unix_get_fd(io);
+
+ cli_sock = accept(srv_sock, NULL, NULL);
+ if (cli_sock < 0)
+ return TRUE;
+
+ cli_io = g_io_channel_unix_new(cli_sock);
+
+ g_io_channel_set_close_on_unref(cli_io, TRUE);
+ g_io_channel_set_flags(cli_io, G_IO_FLAG_NONBLOCK, NULL);
+
+ if (server->confirm)
+ server->confirm(cli_io, server->user_data);
+ else
+ server->connect(cli_io, NULL, server->user_data);
+
+ g_io_channel_unref(cli_io);
+
+ return TRUE;
+}
+
+static void server_add(GIOChannel *io, BtIOConnect connect,
+ BtIOConfirm confirm, gpointer user_data,
+ GDestroyNotify destroy)
+{
+ struct server *server;
+ GIOCondition cond;
+
+ server = g_new0(struct server, 1);
+ server->connect = connect;
+ server->confirm = confirm;
+ server->user_data = user_data;
+ server->destroy = destroy;
+
+ cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+ g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, server_cb, server,
+ (GDestroyNotify) server_remove);
+}
+
+static void connect_add(GIOChannel *io, BtIOConnect connect,
+ gpointer user_data, GDestroyNotify destroy)
+{
+ struct connect *conn;
+ GIOCondition cond;
+
+ conn = g_new0(struct connect, 1);
+ conn->connect = connect;
+ conn->user_data = user_data;
+ conn->destroy = destroy;
+
+ cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+ g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, connect_cb, conn,
+ (GDestroyNotify) connect_remove);
+}
+
+static void accept_add(GIOChannel *io, BtIOConnect connect, gpointer user_data,
+ GDestroyNotify destroy)
+{
+ struct accept *accept;
+ GIOCondition cond;
+
+ accept = g_new0(struct accept, 1);
+ accept->connect = connect;
+ accept->user_data = user_data;
+ accept->destroy = destroy;
+
+ cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+ g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, accept_cb, accept,
+ (GDestroyNotify) accept_remove);
+}
+
+static int l2cap_bind(int sock, const bdaddr_t *src, uint16_t psm,
+ uint16_t cid, GError **err)
+{
+ struct sockaddr_l2 addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ bacpy(&addr.l2_bdaddr, src);
+
+ if (cid)
+ addr.l2_cid = htobs(cid);
+ else
+ addr.l2_psm = htobs(psm);
+
+ if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ ERROR_FAILED(err, "l2cap_bind", errno);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int l2cap_connect(int sock, const bdaddr_t *dst,
+ uint16_t psm, uint16_t cid)
+{
+ int err;
+ struct sockaddr_l2 addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ bacpy(&addr.l2_bdaddr, dst);
+ if (cid)
+ addr.l2_cid = htobs(cid);
+ else
+ addr.l2_psm = htobs(psm);
+
+ err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
+ if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
+ return err;
+
+ return 0;
+}
+
+static int l2cap_set_master(int sock, int master)
+{
+ int flags;
+ socklen_t len;
+
+ len = sizeof(flags);
+ if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, &len) < 0)
+ return -errno;
+
+ if (master) {
+ if (flags & L2CAP_LM_MASTER)
+ return 0;
+ flags |= L2CAP_LM_MASTER;
+ } else {
+ if (!(flags & L2CAP_LM_MASTER))
+ return 0;
+ flags &= ~L2CAP_LM_MASTER;
+ }
+
+ if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, sizeof(flags)) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int rfcomm_set_master(int sock, int master)
+{
+ int flags;
+ socklen_t len;
+
+ len = sizeof(flags);
+ if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, &len) < 0)
+ return -errno;
+
+ if (master) {
+ if (flags & RFCOMM_LM_MASTER)
+ return 0;
+ flags |= RFCOMM_LM_MASTER;
+ } else {
+ if (!(flags & RFCOMM_LM_MASTER))
+ return 0;
+ flags &= ~RFCOMM_LM_MASTER;
+ }
+
+ if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, sizeof(flags)) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int l2cap_set_lm(int sock, int level)
+{
+ int lm_map[] = {
+ 0,
+ L2CAP_LM_AUTH,
+ L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT,
+ L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE,
+ }, opt = lm_map[level];
+
+ if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int rfcomm_set_lm(int sock, int level)
+{
+ int lm_map[] = {
+ 0,
+ RFCOMM_LM_AUTH,
+ RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT,
+ RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE,
+ }, opt = lm_map[level];
+
+ if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, sizeof(opt)) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static gboolean set_sec_level(int sock, BtIOType type, int level, GError **err)
+{
+ struct bt_security sec;
+ int ret;
+
+ if (level < BT_SECURITY_LOW || level > BT_SECURITY_HIGH) {
+ g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+ "Valid security level range is %d-%d",
+ BT_SECURITY_LOW, BT_SECURITY_HIGH);
+ return FALSE;
+ }
+
+ memset(&sec, 0, sizeof(sec));
+ sec.level = level;
+
+ if (setsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec,
+ sizeof(sec)) == 0)
+ return TRUE;
+
+ if (errno != ENOPROTOOPT) {
+ ERROR_FAILED(err, "setsockopt(BT_SECURITY)", errno);
+ return FALSE;
+ }
+
+ if (type == BT_IO_L2CAP)
+ ret = l2cap_set_lm(sock, level);
+ else
+ ret = rfcomm_set_lm(sock, level);
+
+ if (ret < 0) {
+ ERROR_FAILED(err, "setsockopt(LM)", -ret);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static int l2cap_get_lm(int sock, int *sec_level)
+{
+ int opt;
+ socklen_t len;
+
+ len = sizeof(opt);
+ if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, &len) < 0)
+ return -errno;
+
+ *sec_level = 0;
+
+ if (opt & L2CAP_LM_AUTH)
+ *sec_level = BT_SECURITY_LOW;
+ if (opt & L2CAP_LM_ENCRYPT)
+ *sec_level = BT_SECURITY_MEDIUM;
+ if (opt & L2CAP_LM_SECURE)
+ *sec_level = BT_SECURITY_HIGH;
+
+ return 0;
+}
+
+static int rfcomm_get_lm(int sock, int *sec_level)
+{
+ int opt;
+ socklen_t len;
+
+ len = sizeof(opt);
+ if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, &len) < 0)
+ return -errno;
+
+ *sec_level = 0;
+
+ if (opt & RFCOMM_LM_AUTH)
+ *sec_level = BT_SECURITY_LOW;
+ if (opt & RFCOMM_LM_ENCRYPT)
+ *sec_level = BT_SECURITY_MEDIUM;
+ if (opt & RFCOMM_LM_SECURE)
+ *sec_level = BT_SECURITY_HIGH;
+
+ return 0;
+}
+
+static gboolean get_sec_level(int sock, BtIOType type, int *level,
+ GError **err)
+{
+ struct bt_security sec;
+ socklen_t len;
+ int ret;
+
+ memset(&sec, 0, sizeof(sec));
+ len = sizeof(sec);
+ if (getsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) == 0) {
+ *level = sec.level;
+ return TRUE;
+ }
+
+ if (errno != ENOPROTOOPT) {
+ ERROR_FAILED(err, "getsockopt(BT_SECURITY)", errno);
+ return FALSE;
+ }
+
+ if (type == BT_IO_L2CAP)
+ ret = l2cap_get_lm(sock, level);
+ else
+ ret = rfcomm_get_lm(sock, level);
+
+ if (ret < 0) {
+ ERROR_FAILED(err, "getsockopt(LM)", -ret);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean l2cap_set(int sock, int sec_level, uint16_t imtu, uint16_t omtu,
+ uint8_t mode, int master, GError **err)
+{
+ if (imtu || omtu || mode) {
+ struct l2cap_options l2o;
+ socklen_t len;
+
+ memset(&l2o, 0, sizeof(l2o));
+ len = sizeof(l2o);
+ if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o,
+ &len) < 0) {
+ ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
+ return FALSE;
+ }
+
+ if (imtu)
+ l2o.imtu = imtu;
+ if (omtu)
+ l2o.omtu = omtu;
+ if (mode)
+ l2o.mode = mode;
+
+ if (setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o,
+ sizeof(l2o)) < 0) {
+ ERROR_FAILED(err, "setsockopt(L2CAP_OPTIONS)", errno);
+ return FALSE;
+ }
+ }
+
+ if (master >= 0 && l2cap_set_master(sock, master) < 0) {
+ ERROR_FAILED(err, "l2cap_set_master", errno);
+ return FALSE;
+ }
+
+ if (sec_level && !set_sec_level(sock, BT_IO_L2CAP, sec_level, err))
+ return FALSE;
+
+ return TRUE;
+}
+
+static int rfcomm_bind(int sock,
+ const bdaddr_t *src, uint8_t channel, GError **err)
+{
+ struct sockaddr_rc addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.rc_family = AF_BLUETOOTH;
+ bacpy(&addr.rc_bdaddr, src);
+ addr.rc_channel = channel;
+
+ if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ ERROR_FAILED(err, "rfcomm_bind", errno);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int rfcomm_connect(int sock, const bdaddr_t *dst, uint8_t channel)
+{
+ int err;
+ struct sockaddr_rc addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.rc_family = AF_BLUETOOTH;
+ bacpy(&addr.rc_bdaddr, dst);
+ addr.rc_channel = channel;
+
+ err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
+ if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
+ return err;
+
+ return 0;
+}
+
+static gboolean rfcomm_set(int sock, int sec_level, int master, GError **err)
+{
+ if (sec_level && !set_sec_level(sock, BT_IO_RFCOMM, sec_level, err))
+ return FALSE;
+
+ if (master >= 0 && rfcomm_set_master(sock, master) < 0) {
+ ERROR_FAILED(err, "rfcomm_set_master", errno);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static int sco_bind(int sock, const bdaddr_t *src, GError **err)
+{
+ struct sockaddr_sco addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sco_family = AF_BLUETOOTH;
+ bacpy(&addr.sco_bdaddr, src);
+
+ if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ ERROR_FAILED(err, "sco_bind", errno);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int sco_connect(int sock, const bdaddr_t *dst)
+{
+ struct sockaddr_sco addr;
+ int err;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sco_family = AF_BLUETOOTH;
+ bacpy(&addr.sco_bdaddr, dst);
+
+ err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
+ if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
+ return err;
+
+ return 0;
+}
+
+static gboolean sco_set(int sock, uint16_t mtu, GError **err)
+{
+ struct sco_options sco_opt;
+ socklen_t len;
+
+ if (!mtu)
+ return TRUE;
+
+ len = sizeof(sco_opt);
+ memset(&sco_opt, 0, len);
+ if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
+ ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
+ return FALSE;
+ }
+
+ sco_opt.mtu = mtu;
+ if (setsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt,
+ sizeof(sco_opt)) < 0) {
+ ERROR_FAILED(err, "setsockopt(SCO_OPTIONS)", errno);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean parse_set_opts(struct set_opts *opts, GError **err,
+ BtIOOption opt1, va_list args)
+{
+ BtIOOption opt = opt1;
+ const char *str;
+
+ memset(opts, 0, sizeof(*opts));
+
+ /* Set defaults */
+ opts->defer = DEFAULT_DEFER_TIMEOUT;
+ opts->master = -1;
+ opts->sec_level = BT_IO_SEC_MEDIUM;
+ opts->mode = L2CAP_MODE_BASIC;
+
+ while (opt != BT_IO_OPT_INVALID) {
+ switch (opt) {
+ case BT_IO_OPT_SOURCE:
+ str = va_arg(args, const char *);
+ if (strncasecmp(str, "hci", 3) == 0)
+ hci_devba(atoi(str + 3), &opts->src);
+ else
+ str2ba(str, &opts->src);
+ break;
+ case BT_IO_OPT_SOURCE_BDADDR:
+ bacpy(&opts->src, va_arg(args, const bdaddr_t *));
+ break;
+ case BT_IO_OPT_DEST:
+ str2ba(va_arg(args, const char *), &opts->dst);
+ break;
+ case BT_IO_OPT_DEST_BDADDR:
+ bacpy(&opts->dst, va_arg(args, const bdaddr_t *));
+ break;
+ case BT_IO_OPT_DEFER_TIMEOUT:
+ opts->defer = va_arg(args, int);
+ break;
+ case BT_IO_OPT_SEC_LEVEL:
+ opts->sec_level = va_arg(args, int);
+ break;
+ case BT_IO_OPT_CHANNEL:
+ opts->channel = va_arg(args, int);
+ break;
+ case BT_IO_OPT_PSM:
+ opts->psm = va_arg(args, int);
+ break;
+ case BT_IO_OPT_CID:
+ opts->cid = va_arg(args, int);
+ break;
+ case BT_IO_OPT_MTU:
+ opts->mtu = va_arg(args, int);
+ opts->imtu = opts->mtu;
+ opts->omtu = opts->mtu;
+ break;
+ case BT_IO_OPT_OMTU:
+ opts->omtu = va_arg(args, int);
+ if (!opts->mtu)
+ opts->mtu = opts->omtu;
+ break;
+ case BT_IO_OPT_IMTU:
+ opts->imtu = va_arg(args, int);
+ if (!opts->mtu)
+ opts->mtu = opts->imtu;
+ break;
+ case BT_IO_OPT_MASTER:
+ opts->master = va_arg(args, gboolean);
+ break;
+ case BT_IO_OPT_MODE:
+ opts->mode = va_arg(args, int);
+ break;
+ default:
+ g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+ "Unknown option %d", opt);
+ return FALSE;
+ }
+
+ opt = va_arg(args, int);
+ }
+
+ return TRUE;
+}
+
+static gboolean get_peers(int sock, struct sockaddr *src, struct sockaddr *dst,
+ socklen_t len, GError **err)
+{
+ socklen_t olen;
+
+ memset(src, 0, len);
+ olen = len;
+ if (getsockname(sock, src, &olen) < 0) {
+ ERROR_FAILED(err, "getsockname", errno);
+ return FALSE;
+ }
+
+ memset(dst, 0, len);
+ olen = len;
+ if (getpeername(sock, dst, &olen) < 0) {
+ ERROR_FAILED(err, "getpeername", errno);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static int l2cap_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
+{
+ struct l2cap_conninfo info;
+ socklen_t len;
+
+ len = sizeof(info);
+ if (getsockopt(sock, SOL_L2CAP, L2CAP_CONNINFO, &info, &len) < 0)
+ return -errno;
+
+ if (handle)
+ *handle = info.hci_handle;
+
+ if (dev_class)
+ memcpy(dev_class, info.dev_class, 3);
+
+ return 0;
+}
+
+static gboolean l2cap_get(int sock, GError **err, BtIOOption opt1,
+ va_list args)
+{
+ BtIOOption opt = opt1;
+ struct sockaddr_l2 src, dst;
+ struct l2cap_options l2o;
+ int flags;
+ uint8_t dev_class[3];
+ uint16_t handle;
+ socklen_t len;
+
+ len = sizeof(l2o);
+ memset(&l2o, 0, len);
+ if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) {
+ ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
+ return FALSE;
+ }
+
+ if (!get_peers(sock, (struct sockaddr *) &src,
+ (struct sockaddr *) &dst, sizeof(src), err))
+ return FALSE;
+
+ while (opt != BT_IO_OPT_INVALID) {
+ switch (opt) {
+ case BT_IO_OPT_SOURCE:
+ ba2str(&src.l2_bdaddr, va_arg(args, char *));
+ break;
+ case BT_IO_OPT_SOURCE_BDADDR:
+ bacpy(va_arg(args, bdaddr_t *), &src.l2_bdaddr);
+ break;
+ case BT_IO_OPT_DEST:
+ ba2str(&dst.l2_bdaddr, va_arg(args, char *));
+ break;
+ case BT_IO_OPT_DEST_BDADDR:
+ bacpy(va_arg(args, bdaddr_t *), &dst.l2_bdaddr);
+ break;
+ case BT_IO_OPT_DEFER_TIMEOUT:
+ len = sizeof(int);
+ if (getsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP,
+ va_arg(args, int *), &len) < 0) {
+ ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
+ errno);
+ return FALSE;
+ }
+ break;
+ case BT_IO_OPT_SEC_LEVEL:
+ if (!get_sec_level(sock, BT_IO_L2CAP,
+ va_arg(args, int *), err))
+ return FALSE;
+ break;
+ case BT_IO_OPT_PSM:
+ *(va_arg(args, uint16_t *)) = src.l2_psm ?
+ src.l2_psm : dst.l2_psm;
+ break;
+ case BT_IO_OPT_CID:
+ *(va_arg(args, uint16_t *)) = src.l2_cid ?
+ src.l2_cid : dst.l2_cid;
+ break;
+ case BT_IO_OPT_OMTU:
+ *(va_arg(args, uint16_t *)) = l2o.omtu;
+ break;
+ case BT_IO_OPT_IMTU:
+ *(va_arg(args, uint16_t *)) = l2o.imtu;
+ break;
+ case BT_IO_OPT_MASTER:
+ len = sizeof(flags);
+ if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags,
+ &len) < 0) {
+ ERROR_FAILED(err, "getsockopt(L2CAP_LM)",
+ errno);
+ return FALSE;
+ }
+ *(va_arg(args, gboolean *)) =
+ (flags & L2CAP_LM_MASTER) ? TRUE : FALSE;
+ break;
+ case BT_IO_OPT_HANDLE:
+ if (l2cap_get_info(sock, &handle, dev_class) < 0) {
+ ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
+ return FALSE;
+ }
+ *(va_arg(args, uint16_t *)) = handle;
+ break;
+ case BT_IO_OPT_CLASS:
+ if (l2cap_get_info(sock, &handle, dev_class) < 0) {
+ ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
+ return FALSE;
+ }
+ memcpy(va_arg(args, uint8_t *), dev_class, 3);
+ break;
+ case BT_IO_OPT_MODE:
+ *(va_arg(args, uint8_t *)) = l2o.mode;
+ break;
+ default:
+ g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+ "Unknown option %d", opt);
+ return FALSE;
+ }
+
+ opt = va_arg(args, int);
+ }
+
+ return TRUE;
+}
+
+static int rfcomm_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
+{
+ struct rfcomm_conninfo info;
+ socklen_t len;
+
+ len = sizeof(info);
+ if (getsockopt(sock, SOL_RFCOMM, RFCOMM_CONNINFO, &info, &len) < 0)
+ return -errno;
+
+ if (handle)
+ *handle = info.hci_handle;
+
+ if (dev_class)
+ memcpy(dev_class, info.dev_class, 3);
+
+ return 0;
+}
+
+static gboolean rfcomm_get(int sock, GError **err, BtIOOption opt1,
+ va_list args)
+{
+ BtIOOption opt = opt1;
+ struct sockaddr_rc src, dst;
+ int flags;
+ socklen_t len;
+ uint8_t dev_class[3];
+ uint16_t handle;
+
+ if (!get_peers(sock, (struct sockaddr *) &src,
+ (struct sockaddr *) &dst, sizeof(src), err))
+ return FALSE;
+
+ while (opt != BT_IO_OPT_INVALID) {
+ switch (opt) {
+ case BT_IO_OPT_SOURCE:
+ ba2str(&src.rc_bdaddr, va_arg(args, char *));
+ break;
+ case BT_IO_OPT_SOURCE_BDADDR:
+ bacpy(va_arg(args, bdaddr_t *), &src.rc_bdaddr);
+ break;
+ case BT_IO_OPT_DEST:
+ ba2str(&dst.rc_bdaddr, va_arg(args, char *));
+ break;
+ case BT_IO_OPT_DEST_BDADDR:
+ bacpy(va_arg(args, bdaddr_t *), &dst.rc_bdaddr);
+ break;
+ case BT_IO_OPT_DEFER_TIMEOUT:
+ len = sizeof(int);
+ if (getsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP,
+ va_arg(args, int *), &len) < 0) {
+ ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
+ errno);
+ return FALSE;
+ }
+ break;
+ case BT_IO_OPT_SEC_LEVEL:
+ if (!get_sec_level(sock, BT_IO_RFCOMM,
+ va_arg(args, int *), err))
+ return FALSE;
+ break;
+ case BT_IO_OPT_CHANNEL:
+ *(va_arg(args, uint8_t *)) = src.rc_channel ?
+ src.rc_channel : dst.rc_channel;
+ break;
+ case BT_IO_OPT_SOURCE_CHANNEL:
+ *(va_arg(args, uint8_t *)) = src.rc_channel;
+ break;
+ case BT_IO_OPT_DEST_CHANNEL:
+ *(va_arg(args, uint8_t *)) = dst.rc_channel;
+ break;
+ case BT_IO_OPT_MASTER:
+ len = sizeof(flags);
+ if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags,
+ &len) < 0) {
+ ERROR_FAILED(err, "getsockopt(RFCOMM_LM)",
+ errno);
+ return FALSE;
+ }
+ *(va_arg(args, gboolean *)) =
+ (flags & RFCOMM_LM_MASTER) ? TRUE : FALSE;
+ break;
+ case BT_IO_OPT_HANDLE:
+ if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
+ ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
+ return FALSE;
+ }
+ *(va_arg(args, uint16_t *)) = handle;
+ break;
+ case BT_IO_OPT_CLASS:
+ if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
+ ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
+ return FALSE;
+ }
+ memcpy(va_arg(args, uint8_t *), dev_class, 3);
+ break;
+ default:
+ g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+ "Unknown option %d", opt);
+ return FALSE;
+ }
+
+ opt = va_arg(args, int);
+ }
+
+ return TRUE;
+}
+
+static int sco_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
+{
+ struct sco_conninfo info;
+ socklen_t len;
+
+ len = sizeof(info);
+ if (getsockopt(sock, SOL_SCO, SCO_CONNINFO, &info, &len) < 0)
+ return -errno;
+
+ if (handle)
+ *handle = info.hci_handle;
+
+ if (dev_class)
+ memcpy(dev_class, info.dev_class, 3);
+
+ return 0;
+}
+
+static gboolean sco_get(int sock, GError **err, BtIOOption opt1, va_list args)
+{
+ BtIOOption opt = opt1;
+ struct sockaddr_sco src, dst;
+ struct sco_options sco_opt;
+ socklen_t len;
+ uint8_t dev_class[3];
+ uint16_t handle;
+
+ len = sizeof(sco_opt);
+ memset(&sco_opt, 0, len);
+ if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
+ ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
+ return FALSE;
+ }
+
+ if (!get_peers(sock, (struct sockaddr *) &src,
+ (struct sockaddr *) &dst, sizeof(src), err))
+ return FALSE;
+
+ while (opt != BT_IO_OPT_INVALID) {
+ switch (opt) {
+ case BT_IO_OPT_SOURCE:
+ ba2str(&src.sco_bdaddr, va_arg(args, char *));
+ break;
+ case BT_IO_OPT_SOURCE_BDADDR:
+ bacpy(va_arg(args, bdaddr_t *), &src.sco_bdaddr);
+ break;
+ case BT_IO_OPT_DEST:
+ ba2str(&dst.sco_bdaddr, va_arg(args, char *));
+ break;
+ case BT_IO_OPT_DEST_BDADDR:
+ bacpy(va_arg(args, bdaddr_t *), &dst.sco_bdaddr);
+ break;
+ case BT_IO_OPT_MTU:
+ case BT_IO_OPT_IMTU:
+ case BT_IO_OPT_OMTU:
+ *(va_arg(args, uint16_t *)) = sco_opt.mtu;
+ break;
+ case BT_IO_OPT_HANDLE:
+ if (sco_get_info(sock, &handle, dev_class) < 0) {
+ ERROR_FAILED(err, "SCO_CONNINFO", errno);
+ return FALSE;
+ }
+ *(va_arg(args, uint16_t *)) = handle;
+ break;
+ case BT_IO_OPT_CLASS:
+ if (sco_get_info(sock, &handle, dev_class) < 0) {
+ ERROR_FAILED(err, "SCO_CONNINFO", errno);
+ return FALSE;
+ }
+ memcpy(va_arg(args, uint8_t *), dev_class, 3);
+ break;
+ default:
+ g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+ "Unknown option %d", opt);
+ return FALSE;
+ }
+
+ opt = va_arg(args, int);
+ }
+
+ return TRUE;
+}
+
+static gboolean get_valist(GIOChannel *io, BtIOType type, GError **err,
+ BtIOOption opt1, va_list args)
+{
+ int sock;
+
+ sock = g_io_channel_unix_get_fd(io);
+
+ switch (type) {
+ case BT_IO_L2RAW:
+ case BT_IO_L2CAP:
+ return l2cap_get(sock, err, opt1, args);
+ case BT_IO_RFCOMM:
+ return rfcomm_get(sock, err, opt1, args);
+ case BT_IO_SCO:
+ return sco_get(sock, err, opt1, args);
+ }
+
+ g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+ "Unknown BtIO type %d", type);
+ return FALSE;
+}
+
+gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data,
+ GDestroyNotify destroy, GError **err)
+{
+ int sock;
+ char c;
+ struct pollfd pfd;
+
+ sock = g_io_channel_unix_get_fd(io);
+
+ memset(&pfd, 0, sizeof(pfd));
+ pfd.fd = sock;
+ pfd.events = POLLOUT;
+
+ if (poll(&pfd, 1, 0) < 0) {
+ ERROR_FAILED(err, "poll", errno);
+ return FALSE;
+ }
+
+ if (!(pfd.revents & POLLOUT)) {
+ int ret;
+ ret = read(sock, &c, 1);
+ }
+
+ accept_add(io, connect, user_data, destroy);
+
+ return TRUE;
+}
+
+gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
+ BtIOOption opt1, ...)
+{
+ va_list args;
+ gboolean ret;
+ struct set_opts opts;
+ int sock;
+
+ va_start(args, opt1);
+ ret = parse_set_opts(&opts, err, opt1, args);
+ va_end(args);
+
+ if (!ret)
+ return ret;
+
+ sock = g_io_channel_unix_get_fd(io);
+
+ switch (type) {
+ case BT_IO_L2RAW:
+ case BT_IO_L2CAP:
+ return l2cap_set(sock, opts.sec_level, opts.imtu, opts.omtu,
+ opts.mode, opts.master, err);
+ case BT_IO_RFCOMM:
+ return rfcomm_set(sock, opts.sec_level, opts.master, err);
+ case BT_IO_SCO:
+ return sco_set(sock, opts.mtu, err);
+ }
+
+ g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+ "Unknown BtIO type %d", type);
+ return FALSE;
+}
+
+gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
+ BtIOOption opt1, ...)
+{
+ va_list args;
+ gboolean ret;
+
+ va_start(args, opt1);
+ ret = get_valist(io, type, err, opt1, args);
+ va_end(args);
+
+ return ret;
+}
+
+static GIOChannel *create_io(BtIOType type, gboolean server,
+ struct set_opts *opts, GError **err)
+{
+ int sock;
+ GIOChannel *io;
+
+ switch (type) {
+ case BT_IO_L2RAW:
+ sock = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP);
+ if (sock < 0) {
+ ERROR_FAILED(err, "socket(RAW, L2CAP)", errno);
+ return NULL;
+ }
+ if (l2cap_bind(sock, &opts->src, server ? opts->psm : 0,
+ opts->cid, err) < 0)
+ goto failed;
+ if (!l2cap_set(sock, opts->sec_level, 0, 0, 0, -1, err))
+ goto failed;
+ break;
+ case BT_IO_L2CAP:
+ sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+ if (sock < 0) {
+ ERROR_FAILED(err, "socket(SEQPACKET, L2CAP)", errno);
+ return NULL;
+ }
+ if (l2cap_bind(sock, &opts->src, server ? opts->psm : 0,
+ opts->cid, err) < 0)
+ goto failed;
+ if (!l2cap_set(sock, opts->sec_level, opts->imtu, opts->omtu,
+ opts->mode, opts->master, err))
+ goto failed;
+ break;
+ case BT_IO_RFCOMM:
+ sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+ if (sock < 0) {
+ ERROR_FAILED(err, "socket(STREAM, RFCOMM)", errno);
+ return NULL;
+ }
+ if (rfcomm_bind(sock, &opts->src,
+ server ? opts->channel : 0, err) < 0)
+ goto failed;
+ if (!rfcomm_set(sock, opts->sec_level, opts->master, err))
+ goto failed;
+ break;
+ case BT_IO_SCO:
+ sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
+ if (sock < 0) {
+ ERROR_FAILED(err, "socket(SEQPACKET, SCO)", errno);
+ return NULL;
+ }
+ if (sco_bind(sock, &opts->src, err) < 0)
+ goto failed;
+ if (!sco_set(sock, opts->mtu, err))
+ goto failed;
+ break;
+ default:
+ g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+ "Unknown BtIO type %d", type);
+ return NULL;
+ }
+
+ io = g_io_channel_unix_new(sock);
+
+ g_io_channel_set_close_on_unref(io, TRUE);
+ g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL);
+
+ return io;
+
+failed:
+ close(sock);
+
+ return NULL;
+}
+
+GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
+ gpointer user_data, GDestroyNotify destroy,
+ GError **gerr, BtIOOption opt1, ...)
+{
+ GIOChannel *io;
+ va_list args;
+ struct set_opts opts;
+ int err, sock;
+ gboolean ret;
+
+ va_start(args, opt1);
+ ret = parse_set_opts(&opts, gerr, opt1, args);
+ va_end(args);
+
+ if (ret == FALSE)
+ return NULL;
+
+ io = create_io(type, FALSE, &opts, gerr);
+ if (io == NULL)
+ return NULL;
+
+ sock = g_io_channel_unix_get_fd(io);
+
+ switch (type) {
+ case BT_IO_L2RAW:
+ err = l2cap_connect(sock, &opts.dst, 0, opts.cid);
+ break;
+ case BT_IO_L2CAP:
+ err = l2cap_connect(sock, &opts.dst, opts.psm, opts.cid);
+ break;
+ case BT_IO_RFCOMM:
+ err = rfcomm_connect(sock, &opts.dst, opts.channel);
+ break;
+ case BT_IO_SCO:
+ err = sco_connect(sock, &opts.dst);
+ break;
+ default:
+ g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+ "Unknown BtIO type %d", type);
+ return NULL;
+ }
+
+ if (err < 0) {
+ g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED,
+ "connect: %s (%d)", strerror(-err), -err);
+ g_io_channel_unref(io);
+ return NULL;
+ }
+
+ connect_add(io, connect, user_data, destroy);
+
+ return io;
+}
+
+GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect,
+ BtIOConfirm confirm, gpointer user_data,
+ GDestroyNotify destroy, GError **err,
+ BtIOOption opt1, ...)
+{
+ GIOChannel *io;
+ va_list args;
+ struct set_opts opts;
+ int sock;
+ gboolean ret;
+
+ if (type == BT_IO_L2RAW) {
+ g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+ "Server L2CAP RAW sockets not supported");
+ return NULL;
+ }
+
+ va_start(args, opt1);
+ ret = parse_set_opts(&opts, err, opt1, args);
+ va_end(args);
+
+ if (ret == FALSE)
+ return NULL;
+
+ io = create_io(type, TRUE, &opts, err);
+ if (io == NULL)
+ return NULL;
+
+ sock = g_io_channel_unix_get_fd(io);
+
+ if (confirm)
+ setsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP, &opts.defer,
+ sizeof(opts.defer));
+
+ if (listen(sock, 5) < 0) {
+ ERROR_FAILED(err, "listen", errno);
+ g_io_channel_unref(io);
+ return NULL;
+ }
+
+ server_add(io, connect, confirm, user_data, destroy);
+
+ return io;
+}
+
+GQuark bt_io_error_quark(void)
+{
+ return g_quark_from_static_string("bt-io-error-quark");
+}
diff --git a/btio/btio.h b/btio/btio.h
new file mode 100644
index 0000000..53e8eaa
--- /dev/null
+++ b/btio/btio.h
@@ -0,0 +1,98 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2009-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2009-2010 Nokia Corporation
+ *
+ *
+ * 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
+ *
+ */
+#ifndef BT_IO_H
+#define BT_IO_H
+
+#include <glib.h>
+
+typedef enum {
+ BT_IO_ERROR_DISCONNECTED,
+ BT_IO_ERROR_CONNECT_FAILED,
+ BT_IO_ERROR_FAILED,
+ BT_IO_ERROR_INVALID_ARGS,
+} BtIOError;
+
+#define BT_IO_ERROR bt_io_error_quark()
+
+GQuark bt_io_error_quark(void);
+
+typedef enum {
+ BT_IO_L2RAW,
+ BT_IO_L2CAP,
+ BT_IO_RFCOMM,
+ BT_IO_SCO,
+} BtIOType;
+
+typedef enum {
+ BT_IO_OPT_INVALID = 0,
+ BT_IO_OPT_SOURCE,
+ BT_IO_OPT_SOURCE_BDADDR,
+ BT_IO_OPT_DEST,
+ BT_IO_OPT_DEST_BDADDR,
+ BT_IO_OPT_DEFER_TIMEOUT,
+ BT_IO_OPT_SEC_LEVEL,
+ BT_IO_OPT_CHANNEL,
+ BT_IO_OPT_SOURCE_CHANNEL,
+ BT_IO_OPT_DEST_CHANNEL,
+ BT_IO_OPT_PSM,
+ BT_IO_OPT_CID,
+ BT_IO_OPT_MTU,
+ BT_IO_OPT_OMTU,
+ BT_IO_OPT_IMTU,
+ BT_IO_OPT_MASTER,
+ BT_IO_OPT_HANDLE,
+ BT_IO_OPT_CLASS,
+ BT_IO_OPT_MODE,
+} BtIOOption;
+
+typedef enum {
+ BT_IO_SEC_SDP = 0,
+ BT_IO_SEC_LOW,
+ BT_IO_SEC_MEDIUM,
+ BT_IO_SEC_HIGH,
+} BtIOSecLevel;
+
+typedef void (*BtIOConfirm)(GIOChannel *io, gpointer user_data);
+
+typedef void (*BtIOConnect)(GIOChannel *io, GError *err, gpointer user_data);
+
+gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data,
+ GDestroyNotify destroy, GError **err);
+
+gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
+ BtIOOption opt1, ...);
+
+gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
+ BtIOOption opt1, ...);
+
+GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
+ gpointer user_data, GDestroyNotify destroy,
+ GError **err, BtIOOption opt1, ...);
+
+GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect,
+ BtIOConfirm confirm, gpointer user_data,
+ GDestroyNotify destroy, GError **err,
+ BtIOOption opt1, ...);
+
+#endif
diff --git a/compat/bnep.c b/compat/bnep.c
new file mode 100644
index 0000000..281350b
--- /dev/null
+++ b/compat/bnep.c
@@ -0,0 +1,339 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 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 <sys/socket.h>
+#include <sys/ioctl.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/bnep.h>
+
+#include <netinet/in.h>
+
+#include "pand.h"
+
+static int ctl;
+
+/* Compatibility with old ioctls */
+#define OLD_BNEPCONADD 1
+#define OLD_BNEPCONDEL 2
+#define OLD_BNEPGETCONLIST 3
+#define OLD_BNEPGETCONINFO 4
+
+static unsigned long bnepconnadd;
+static unsigned long bnepconndel;
+static unsigned long bnepgetconnlist;
+static unsigned long bnepgetconninfo;
+
+static struct {
+ char *str;
+ uint16_t uuid;
+} __svc[] = {
+ { "PANU", BNEP_SVC_PANU },
+ { "NAP", BNEP_SVC_NAP },
+ { "GN", BNEP_SVC_GN },
+ { NULL }
+};
+
+int bnep_str2svc(char *svc, uint16_t *uuid)
+{
+ int i;
+ for (i = 0; __svc[i].str; i++)
+ if (!strcasecmp(svc, __svc[i].str)) {
+ *uuid = __svc[i].uuid;
+ return 0;
+ }
+ return -1;
+}
+
+char *bnep_svc2str(uint16_t uuid)
+{
+ int i;
+ for (i = 0; __svc[i].str; i++)
+ if (__svc[i].uuid == uuid)
+ return __svc[i].str;
+ return NULL;
+}
+
+int bnep_init(void)
+{
+ ctl = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_BNEP);
+ if (ctl < 0) {
+ perror("Failed to open control socket");
+ return 1;
+ }
+
+ /* Temporary ioctl compatibility hack */
+ {
+ struct bnep_connlist_req req;
+ struct bnep_conninfo ci[1];
+
+ req.cnum = 1;
+ req.ci = ci;
+
+ if (!ioctl(ctl, BNEPGETCONNLIST, &req)) {
+ /* New ioctls */
+ bnepconnadd = BNEPCONNADD;
+ bnepconndel = BNEPCONNDEL;
+ bnepgetconnlist = BNEPGETCONNLIST;
+ bnepgetconninfo = BNEPGETCONNINFO;
+ } else {
+ /* Old ioctls */
+ bnepconnadd = OLD_BNEPCONADD;
+ bnepconndel = OLD_BNEPCONDEL;
+ bnepgetconnlist = OLD_BNEPGETCONLIST;
+ bnepgetconninfo = OLD_BNEPGETCONINFO;
+ }
+ }
+
+ return 0;
+}
+
+int bnep_cleanup(void)
+{
+ close(ctl);
+ return 0;
+}
+
+int bnep_show_connections(void)
+{
+ struct bnep_connlist_req req;
+ struct bnep_conninfo ci[48];
+ unsigned int i;
+
+ req.cnum = 48;
+ req.ci = ci;
+ if (ioctl(ctl, bnepgetconnlist, &req)) {
+ perror("Failed to get connection list");
+ return -1;
+ }
+
+ for (i = 0; i < req.cnum; i++) {
+ char addr[18];
+ ba2str((bdaddr_t *) ci[i].dst, addr);
+ printf("%s %s %s\n", ci[i].device,
+ addr, bnep_svc2str(ci[i].role));
+ }
+ return 0;
+}
+
+int bnep_kill_connection(uint8_t *dst)
+{
+ struct bnep_conndel_req req;
+
+ memcpy(req.dst, dst, ETH_ALEN);
+ req.flags = 0;
+ if (ioctl(ctl, bnepconndel, &req)) {
+ perror("Failed to kill connection");
+ return -1;
+ }
+ return 0;
+}
+
+int bnep_kill_all_connections(void)
+{
+ struct bnep_connlist_req req;
+ struct bnep_conninfo ci[48];
+ unsigned int i;
+
+ req.cnum = 48;
+ req.ci = ci;
+ if (ioctl(ctl, bnepgetconnlist, &req)) {
+ perror("Failed to get connection list");
+ return -1;
+ }
+
+ for (i = 0; i < req.cnum; i++) {
+ struct bnep_conndel_req req;
+ memcpy(req.dst, ci[i].dst, ETH_ALEN);
+ req.flags = 0;
+ ioctl(ctl, bnepconndel, &req);
+ }
+ return 0;
+}
+
+static int bnep_connadd(int sk, uint16_t role, char *dev)
+{
+ struct bnep_connadd_req req;
+
+ strncpy(req.device, dev, 16);
+ req.device[15] = '\0';
+ req.sock = sk;
+ req.role = role;
+ if (ioctl(ctl, bnepconnadd, &req))
+ return -1;
+ strncpy(dev, req.device, 16);
+ return 0;
+}
+
+struct __service_16 {
+ uint16_t dst;
+ uint16_t src;
+} __attribute__ ((packed));
+
+struct __service_32 {
+ uint16_t unused1;
+ uint16_t dst;
+ uint16_t unused2;
+ uint16_t src;
+} __attribute__ ((packed));
+
+struct __service_128 {
+ uint16_t unused1;
+ uint16_t dst;
+ uint16_t unused2[8];
+ uint16_t src;
+ uint16_t unused3[7];
+} __attribute__ ((packed));
+
+int bnep_accept_connection(int sk, uint16_t role, char *dev)
+{
+ struct bnep_setup_conn_req *req;
+ struct bnep_control_rsp *rsp;
+ unsigned char pkt[BNEP_MTU];
+ ssize_t r;
+
+ r = recv(sk, pkt, BNEP_MTU, 0);
+ if (r <= 0)
+ return -1;
+
+ errno = EPROTO;
+
+ if ((size_t) r < sizeof(*req))
+ return -1;
+
+ req = (void *) pkt;
+
+ /* Highest known Control command ID
+ * is BNEP_FILTER_MULT_ADDR_RSP = 0x06 */
+ if (req->type == BNEP_CONTROL &&
+ req->ctrl > BNEP_FILTER_MULT_ADDR_RSP) {
+ uint8_t pkt[3];
+
+ pkt[0] = BNEP_CONTROL;
+ pkt[1] = BNEP_CMD_NOT_UNDERSTOOD;
+ pkt[2] = req->ctrl;
+
+ send(sk, pkt, sizeof(pkt), 0);
+
+ return -1;
+ }
+
+ if (req->type != BNEP_CONTROL || req->ctrl != BNEP_SETUP_CONN_REQ)
+ return -1;
+
+ /* FIXME: Check role UUIDs */
+
+ rsp = (void *) pkt;
+ rsp->type = BNEP_CONTROL;
+ rsp->ctrl = BNEP_SETUP_CONN_RSP;
+ rsp->resp = htons(BNEP_SUCCESS);
+ if (send(sk, rsp, sizeof(*rsp), 0) < 0)
+ return -1;
+
+ return bnep_connadd(sk, role, dev);
+}
+
+/* Create BNEP connection
+ * sk - Connect L2CAP socket
+ * role - Local role
+ * service - Remote service
+ * dev - Network device (contains actual dev name on return)
+ */
+int bnep_create_connection(int sk, uint16_t role, uint16_t svc, char *dev)
+{
+ struct bnep_setup_conn_req *req;
+ struct bnep_control_rsp *rsp;
+ struct __service_16 *s;
+ struct timeval timeo;
+ unsigned char pkt[BNEP_MTU];
+ ssize_t r;
+
+ /* Send request */
+ req = (void *) pkt;
+ req->type = BNEP_CONTROL;
+ req->ctrl = BNEP_SETUP_CONN_REQ;
+ req->uuid_size = 2; /* 16bit UUID */
+
+ s = (void *) req->service;
+ s->dst = htons(svc);
+ s->src = htons(role);
+
+ memset(&timeo, 0, sizeof(timeo));
+ timeo.tv_sec = 30;
+
+ setsockopt(sk, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo));
+
+ if (send(sk, pkt, sizeof(*req) + sizeof(*s), 0) < 0)
+ return -1;
+
+receive:
+ /* Get response */
+ r = recv(sk, pkt, BNEP_MTU, 0);
+ if (r <= 0)
+ return -1;
+
+ memset(&timeo, 0, sizeof(timeo));
+ timeo.tv_sec = 0;
+
+ setsockopt(sk, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo));
+
+ errno = EPROTO;
+
+ if ((size_t) r < sizeof(*rsp))
+ return -1;
+
+ rsp = (void *) pkt;
+ if (rsp->type != BNEP_CONTROL)
+ return -1;
+
+ if (rsp->ctrl != BNEP_SETUP_CONN_RSP)
+ goto receive;
+
+ r = ntohs(rsp->resp);
+
+ switch (r) {
+ case BNEP_SUCCESS:
+ break;
+
+ case BNEP_CONN_INVALID_DST:
+ case BNEP_CONN_INVALID_SRC:
+ case BNEP_CONN_INVALID_SVC:
+ errno = EPROTO;
+ return -1;
+
+ case BNEP_CONN_NOT_ALLOWED:
+ errno = EACCES;
+ return -1;
+ }
+
+ return bnep_connadd(sk, role, dev);
+}
diff --git a/compat/dun.c b/compat/dun.c
new file mode 100644
index 0000000..59f036f
--- /dev/null
+++ b/compat/dun.c
@@ -0,0 +1,334 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 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 <syslog.h>
+#include <dirent.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+
+#include "dund.h"
+#include "lib.h"
+
+#define PROC_BASE "/proc"
+
+static int for_each_port(int (*func)(struct rfcomm_dev_info *, unsigned long), unsigned long arg)
+{
+ struct rfcomm_dev_list_req *dl;
+ struct rfcomm_dev_info *di;
+ long r = 0;
+ int sk, i;
+
+ sk = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_RFCOMM);
+ if (sk < 0 ) {
+ perror("Can't open RFCOMM control socket");
+ exit(1);
+ }
+
+ dl = malloc(sizeof(*dl) + RFCOMM_MAX_DEV * sizeof(*di));
+ if (!dl) {
+ perror("Can't allocate request memory");
+ close(sk);
+ exit(1);
+ }
+
+ dl->dev_num = RFCOMM_MAX_DEV;
+ di = dl->dev_info;
+
+ if (ioctl(sk, RFCOMMGETDEVLIST, (void *) dl) < 0) {
+ perror("Can't get device list");
+ exit(1);
+ }
+
+ for (i = 0; i < dl->dev_num; i++) {
+ r = func(di + i, arg);
+ if (r) break;
+ }
+
+ close(sk);
+ free(dl);
+ return r;
+}
+
+static int uses_rfcomm(char *path, char *dev)
+{
+ struct dirent *de;
+ DIR *dir;
+
+ dir = opendir(path);
+ if (!dir)
+ return 0;
+
+ if (chdir(path) < 0)
+ return 0;
+
+ while ((de = readdir(dir)) != NULL) {
+ char link[PATH_MAX + 1];
+ int len = readlink(de->d_name, link, sizeof(link));
+ if (len > 0) {
+ link[len] = 0;
+ if (strstr(link, dev)) {
+ closedir(dir);
+ return 1;
+ }
+ }
+ }
+
+ closedir(dir);
+
+ return 0;
+}
+
+static int find_pppd(int id, pid_t *pid)
+{
+ struct dirent *de;
+ char path[PATH_MAX + 1];
+ char dev[10];
+ int empty = 1;
+ DIR *dir;
+
+ dir = opendir(PROC_BASE);
+ if (!dir) {
+ perror(PROC_BASE);
+ return -1;
+ }
+
+ sprintf(dev, "rfcomm%d", id);
+
+ *pid = 0;
+ while ((de = readdir(dir)) != NULL) {
+ empty = 0;
+ if (isdigit(de->d_name[0])) {
+ sprintf(path, "%s/%s/fd", PROC_BASE, de->d_name);
+ if (uses_rfcomm(path, dev)) {
+ *pid = atoi(de->d_name);
+ break;
+ }
+ }
+ }
+ closedir(dir);
+
+ if (empty)
+ fprintf(stderr, "%s is empty (not mounted ?)\n", PROC_BASE);
+
+ return *pid != 0;
+}
+
+static int dun_exec(char *tty, char *prog, char **args)
+{
+ int pid = fork();
+ int fd;
+
+ switch (pid) {
+ case -1:
+ return -1;
+
+ case 0:
+ break;
+
+ default:
+ return pid;
+ }
+
+ setsid();
+
+ /* Close all FDs */
+ for (fd = 3; fd < 20; fd++)
+ close(fd);
+
+ execvp(prog, args);
+
+ syslog(LOG_ERR, "Error while executing %s", prog);
+
+ exit(1);
+}
+
+static int dun_create_tty(int sk, char *tty, int size)
+{
+ struct sockaddr_rc sa;
+ struct stat st;
+ socklen_t alen;
+ int id, try = 30;
+
+ struct rfcomm_dev_req req = {
+ flags: (1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP),
+ dev_id: -1
+ };
+
+ alen = sizeof(sa);
+ if (getpeername(sk, (struct sockaddr *) &sa, &alen) < 0)
+ return -1;
+ bacpy(&req.dst, &sa.rc_bdaddr);
+
+ alen = sizeof(sa);
+ if (getsockname(sk, (struct sockaddr *) &sa, &alen) < 0)
+ return -1;
+ bacpy(&req.src, &sa.rc_bdaddr);
+ req.channel = sa.rc_channel;
+
+ id = ioctl(sk, RFCOMMCREATEDEV, &req);
+ if (id < 0)
+ return id;
+
+ snprintf(tty, size, "/dev/rfcomm%d", id);
+ while (stat(tty, &st) < 0) {
+ snprintf(tty, size, "/dev/bluetooth/rfcomm/%d", id);
+ if (stat(tty, &st) < 0) {
+ snprintf(tty, size, "/dev/rfcomm%d", id);
+ if (try--) {
+ usleep(100 * 1000);
+ continue;
+ }
+
+ memset(&req, 0, sizeof(req));
+ req.dev_id = id;
+ ioctl(sk, RFCOMMRELEASEDEV, &req);
+
+ return -1;
+ }
+ }
+
+ return id;
+}
+
+int dun_init(void)
+{
+ return 0;
+}
+
+int dun_cleanup(void)
+{
+ return 0;
+}
+
+static int show_conn(struct rfcomm_dev_info *di, unsigned long arg)
+{
+ pid_t pid;
+
+ if (di->state == BT_CONNECTED &&
+ (di->flags & (1<<RFCOMM_REUSE_DLC)) &&
+ (di->flags & (1<<RFCOMM_TTY_ATTACHED)) &&
+ (di->flags & (1<<RFCOMM_RELEASE_ONHUP))) {
+
+ if (find_pppd(di->id, &pid)) {
+ char dst[18];
+ ba2str(&di->dst, dst);
+
+ printf("rfcomm%d: %s channel %d pppd pid %d\n",
+ di->id, dst, di->channel, pid);
+ }
+ }
+ return 0;
+}
+
+static int kill_conn(struct rfcomm_dev_info *di, unsigned long arg)
+{
+ bdaddr_t *dst = (bdaddr_t *) arg;
+ pid_t pid;
+
+ if (di->state == BT_CONNECTED &&
+ (di->flags & (1<<RFCOMM_REUSE_DLC)) &&
+ (di->flags & (1<<RFCOMM_TTY_ATTACHED)) &&
+ (di->flags & (1<<RFCOMM_RELEASE_ONHUP))) {
+
+ if (dst && bacmp(&di->dst, dst))
+ return 0;
+
+ if (find_pppd(di->id, &pid)) {
+ if (kill(pid, SIGINT) < 0)
+ perror("Kill");
+
+ if (!dst)
+ return 0;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int dun_show_connections(void)
+{
+ for_each_port(show_conn, 0);
+ return 0;
+}
+
+int dun_kill_connection(uint8_t *dst)
+{
+ for_each_port(kill_conn, (unsigned long) dst);
+ return 0;
+}
+
+int dun_kill_all_connections(void)
+{
+ for_each_port(kill_conn, 0);
+ return 0;
+}
+
+int dun_open_connection(int sk, char *pppd, char **args, int wait)
+{
+ char tty[100];
+ int pid;
+
+ if (dun_create_tty(sk, tty, sizeof(tty) - 1) < 0) {
+ syslog(LOG_ERR, "RFCOMM TTY creation failed. %s(%d)", strerror(errno), errno);
+ return -1;
+ }
+
+ args[0] = "pppd";
+ args[1] = tty;
+ args[2] = "nodetach";
+
+ pid = dun_exec(tty, pppd, args);
+ if (pid < 0) {
+ syslog(LOG_ERR, "Exec failed. %s(%d)", strerror(errno), errno);
+ return -1;
+ }
+
+ if (wait) {
+ int status;
+ waitpid(pid, &status, 0);
+ /* FIXME: Check for waitpid errors */
+ }
+
+ return 0;
+}
diff --git a/compat/dund.1 b/compat/dund.1
new file mode 100644
index 0000000..09fb7f7
--- /dev/null
+++ b/compat/dund.1
@@ -0,0 +1,72 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.29.
+.TH BlueZ "1" "February 2003" "DUN daemon" "User Commands"
+.SH NAME
+dund \- BlueZ Bluetooth dial-up networking daemon
+.SH DESCRIPTION
+DUN daemon
+.SH SYNOPSIS
+dund <options> [pppd options]
+.SH OPTIONS
+.TP
+\fB\-\-show\fR \fB\-\-list\fR \fB\-l\fR
+Show active DUN connections
+.TP
+\fB\-\-listen\fR \fB\-s\fR
+Listen for DUN connections
+.TP
+\fB\-\-dialup\fR \fB\-u\fR
+Listen for dialup/telephone connections
+.TP
+\fB\-\-connect\fR \fB\-c\fR <bdaddr>
+Create DUN connection
+.TP
+\fB\-\-mrouter\fR \fB\-m\fR <bdaddr>
+Create mRouter connection
+.TP
+\fB\-\-search\fR \fB\-Q[duration]\fR
+Search and connect
+.TP
+\fB\-\-kill\fR \fB\-k\fR <bdaddr>
+Kill DUN connection
+.TP
+\fB\-\-killall\fR \fB\-K\fR
+Kill all DUN connections
+.TP
+\fB\-\-channel\fR \fB\-C\fR <channel>
+RFCOMM channel
+.TP
+\fB\-\-device\fR \fB\-i\fR <bdaddr>
+Source bdaddr
+.TP
+\fB\-\-nosdp\fR \fB\-D\fR
+Disable SDP
+.TP
+\fB\-\-auth\fR \fB\-A\fR
+Enable authentification
+.TP
+\fB\-\-encrypt\fR \fB\-E\fR
+Enable encryption
+.TP
+\fB\-\-secure\fR \fB\-S\fR
+Secure connection
+.TP
+\fB\-\-master\fR \fB\-M\fR
+Become the master of a piconet
+.TP
+\fB\-\-nodetach\fR \fB\-n\fR
+Do not become a daemon
+.TP
+\fB\-\-persist\fR \fB\-p[interval]\fR
+Persist mode
+.TP
+\fB\-\-pppd\fR \fB\-d\fR <pppd>
+Location of the PPP daemon (pppd)
+.TP
+\fB\-\-msdun\fR \fB\-X\fR [timeo]
+Enable Microsoft dialup networking support
+.TP
+\fB\-\-activesync\fR \fB\-a\fR
+Enable Microsoft ActiveSync networking
+.TP
+\fB\-\-cache\fR \fB\-C\fR [valid]
+Enable address cache
diff --git a/compat/dund.c b/compat/dund.c
new file mode 100644
index 0000000..af1b536
--- /dev/null
+++ b/compat/dund.c
@@ -0,0 +1,645 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 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 <syslog.h>
+#include <signal.h>
+#include <getopt.h>
+
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/hidp.h>
+
+#include "sdp.h"
+#include "dund.h"
+#include "lib.h"
+
+volatile sig_atomic_t __io_canceled;
+
+/* MS dialup networking support (i.e. CLIENT / CLIENTSERVER thing) */
+static int msdun = 0;
+
+static char *pppd = "/usr/sbin/pppd";
+static char *pppd_opts[DUN_MAX_PPP_OPTS] =
+ {
+ /* First 3 are reserved */
+ "", "", "",
+ "noauth",
+ "noipdefault",
+ NULL
+ };
+
+static int detach = 1;
+static int persist;
+static int use_sdp = 1;
+static int auth;
+static int encrypt;
+static int secure;
+static int master;
+static int type = LANACCESS;
+static int search_duration = 10;
+static uint use_cache;
+
+static int channel;
+
+static struct {
+ uint valid;
+ char dst[40];
+ bdaddr_t bdaddr;
+ int channel;
+} cache;
+
+static bdaddr_t src_addr = *BDADDR_ANY;
+static int src_dev = -1;
+
+volatile int terminate;
+
+enum {
+ NONE,
+ SHOW,
+ LISTEN,
+ CONNECT,
+ KILL
+} modes;
+
+static int create_connection(char *dst, bdaddr_t *bdaddr, int mrouter);
+
+static int do_listen(void)
+{
+ struct sockaddr_rc sa;
+ int sk, lm;
+
+ if (type == MROUTER) {
+ if (!cache.valid)
+ return -1;
+
+ if (create_connection(cache.dst, &cache.bdaddr, type) < 0) {
+ syslog(LOG_ERR, "Cannot connect to mRouter device. %s(%d)",
+ strerror(errno), errno);
+ return -1;
+ }
+ }
+
+ if (!channel)
+ channel = DUN_DEFAULT_CHANNEL;
+
+ if (use_sdp)
+ dun_sdp_register(&src_addr, channel, type);
+
+ if (type == MROUTER)
+ syslog(LOG_INFO, "Waiting for mRouter callback on channel %d", channel);
+
+ /* Create RFCOMM socket */
+ sk = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+ if (sk < 0) {
+ syslog(LOG_ERR, "Cannot create RFCOMM socket. %s(%d)",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ sa.rc_family = AF_BLUETOOTH;
+ sa.rc_channel = channel;
+ sa.rc_bdaddr = src_addr;
+
+ if (bind(sk, (struct sockaddr *) &sa, sizeof(sa))) {
+ syslog(LOG_ERR, "Bind failed. %s(%d)", strerror(errno), errno);
+ return -1;
+ }
+
+ /* Set link mode */
+ lm = 0;
+ if (master)
+ lm |= RFCOMM_LM_MASTER;
+ if (auth)
+ lm |= RFCOMM_LM_AUTH;
+ if (encrypt)
+ lm |= RFCOMM_LM_ENCRYPT;
+ if (secure)
+ lm |= RFCOMM_LM_SECURE;
+
+ if (lm && setsockopt(sk, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) {
+ syslog(LOG_ERR, "Failed to set link mode. %s(%d)", strerror(errno), errno);
+ return -1;
+ }
+
+ listen(sk, 10);
+
+ while (!terminate) {
+ socklen_t alen = sizeof(sa);
+ int nsk;
+ char ba[40];
+ char ch[10];
+
+ nsk = accept(sk, (struct sockaddr *) &sa, &alen);
+ if (nsk < 0) {
+ syslog(LOG_ERR, "Accept failed. %s(%d)", strerror(errno), errno);
+ continue;
+ }
+
+ switch (fork()) {
+ case 0:
+ break;
+ case -1:
+ syslog(LOG_ERR, "Fork failed. %s(%d)", strerror(errno), errno);
+ default:
+ close(nsk);
+ if (type == MROUTER) {
+ close(sk);
+ terminate = 1;
+ }
+ continue;
+ }
+
+ close(sk);
+
+ if (msdun && ms_dun(nsk, 1, msdun) < 0) {
+ syslog(LOG_ERR, "MSDUN failed. %s(%d)", strerror(errno), errno);
+ exit(0);
+ }
+
+ ba2str(&sa.rc_bdaddr, ba);
+ snprintf(ch, sizeof(ch), "%d", channel);
+
+ /* Setup environment */
+ setenv("DUN_BDADDR", ba, 1);
+ setenv("DUN_CHANNEL", ch, 1);
+
+ if (!dun_open_connection(nsk, pppd, pppd_opts, 0))
+ syslog(LOG_INFO, "New connection from %s", ba);
+
+ close(nsk);
+ exit(0);
+ }
+
+ if (use_sdp)
+ dun_sdp_unregister();
+ return 0;
+}
+
+/* Connect and initiate RFCOMM session
+ * Returns:
+ * -1 - critical error (exit persist mode)
+ * 1 - non critical error
+ * 0 - success
+ */
+static int create_connection(char *dst, bdaddr_t *bdaddr, int mrouter)
+{
+ struct sockaddr_rc sa;
+ int sk, err = 0, ch;
+
+ if (use_cache && cache.valid && cache.channel) {
+ /* Use cached channel */
+ ch = cache.channel;
+
+ } else if (!channel) {
+ syslog(LOG_INFO, "Searching for %s on %s", mrouter ? "SP" : "LAP", dst);
+
+ if (dun_sdp_search(&src_addr, bdaddr, &ch, mrouter) <= 0)
+ return 0;
+ } else
+ ch = channel;
+
+ syslog(LOG_INFO, "Connecting to %s channel %d", dst, ch);
+
+ sk = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+ if (sk < 0) {
+ syslog(LOG_ERR, "Cannot create RFCOMM socket. %s(%d)",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ sa.rc_family = AF_BLUETOOTH;
+ sa.rc_channel = 0;
+ sa.rc_bdaddr = src_addr;
+
+ if (bind(sk, (struct sockaddr *) &sa, sizeof(sa)))
+ syslog(LOG_ERR, "Bind failed. %s(%d)",
+ strerror(errno), errno);
+
+ sa.rc_channel = ch;
+ sa.rc_bdaddr = *bdaddr;
+
+ if (!connect(sk, (struct sockaddr *) &sa, sizeof(sa)) ) {
+ if (mrouter) {
+ sleep(1);
+ close(sk);
+ return 0;
+ }
+
+ syslog(LOG_INFO, "Connection established");
+
+ if (msdun && ms_dun(sk, 0, msdun) < 0) {
+ syslog(LOG_ERR, "MSDUN failed. %s(%d)", strerror(errno), errno);
+ err = 1;
+ goto out;
+ }
+
+ if (!dun_open_connection(sk, pppd, pppd_opts, (persist > 0)))
+ err = 0;
+ else
+ err = 1;
+ } else {
+ syslog(LOG_ERR, "Connect to %s failed. %s(%d)",
+ dst, strerror(errno), errno);
+ err = 1;
+ }
+
+out:
+ if (use_cache) {
+ if (!err) {
+ /* Succesesful connection, validate cache */
+ strcpy(cache.dst, dst);
+ bacpy(&cache.bdaddr, bdaddr);
+ cache.channel = ch;
+ cache.valid = use_cache;
+ } else {
+ cache.channel = 0;
+ cache.valid--;
+ }
+ }
+
+ close(sk);
+ return err;
+}
+
+/* Search and connect
+ * Returns:
+ * -1 - critical error (exit persist mode)
+ * 1 - non critical error
+ * 0 - success
+ */
+static int do_connect(void)
+{
+ inquiry_info *ii;
+ int reconnect = 0;
+ int i, n, r = 0;
+
+ do {
+ if (reconnect)
+ sleep(persist);
+ reconnect = 1;
+
+ if (cache.valid) {
+ /* Use cached bdaddr */
+ r = create_connection(cache.dst, &cache.bdaddr, 0);
+ if (r < 0) {
+ terminate = 1;
+ break;
+ }
+ continue;
+ }
+
+ syslog(LOG_INFO, "Inquiring");
+
+ /* FIXME: Should we use non general LAP here ? */
+
+ ii = NULL;
+ n = hci_inquiry(src_dev, search_duration, 0, NULL, &ii, 0);
+ if (n < 0) {
+ syslog(LOG_ERR, "Inquiry failed. %s(%d)", strerror(errno), errno);
+ continue;
+ }
+
+ for (i = 0; i < n; i++) {
+ char dst[40];
+ ba2str(&ii[i].bdaddr, dst);
+
+ r = create_connection(dst, &ii[i].bdaddr, 0);
+ if (r < 0) {
+ terminate = 1;
+ break;
+ }
+ }
+ bt_free(ii);
+ } while (!terminate && persist);
+
+ return r;
+}
+
+static void do_show(void)
+{
+ dun_show_connections();
+}
+
+static void do_kill(char *dst)
+{
+ if (dst) {
+ bdaddr_t ba;
+ str2ba(dst, &ba);
+ dun_kill_connection((void *) &ba);
+ } else
+ dun_kill_all_connections();
+}
+
+static void sig_hup(int sig)
+{
+ return;
+}
+
+static void sig_term(int sig)
+{
+ io_cancel();
+ terminate = 1;
+}
+
+static struct option main_lopts[] = {
+ { "help", 0, 0, 'h' },
+ { "listen", 0, 0, 's' },
+ { "connect", 1, 0, 'c' },
+ { "search", 2, 0, 'Q' },
+ { "kill", 1, 0, 'k' },
+ { "killall", 0, 0, 'K' },
+ { "channel", 1, 0, 'P' },
+ { "device", 1, 0, 'i' },
+ { "nosdp", 0, 0, 'D' },
+ { "list", 0, 0, 'l' },
+ { "show", 0, 0, 'l' },
+ { "nodetach", 0, 0, 'n' },
+ { "persist", 2, 0, 'p' },
+ { "auth", 0, 0, 'A' },
+ { "encrypt", 0, 0, 'E' },
+ { "secure", 0, 0, 'S' },
+ { "master", 0, 0, 'M' },
+ { "cache", 0, 0, 'C' },
+ { "pppd", 1, 0, 'd' },
+ { "msdun", 2, 0, 'X' },
+ { "activesync", 0, 0, 'a' },
+ { "mrouter", 1, 0, 'm' },
+ { "dialup", 0, 0, 'u' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *main_sopts = "hsc:k:Kr:i:lnp::DQ::AESMP:C::P:Xam:u";
+
+static const char *main_help =
+ "Bluetooth LAP (LAN Access over PPP) daemon version %s\n"
+ "Usage:\n"
+ "\tdund <options> [pppd options]\n"
+ "Options:\n"
+ "\t--show --list -l Show active LAP connections\n"
+ "\t--listen -s Listen for LAP connections\n"
+ "\t--dialup -u Pretend to be a dialup/telephone\n"
+ "\t--connect -c <bdaddr> Create LAP connection\n"
+ "\t--mrouter -m <bdaddr> Create mRouter connection\n"
+ "\t--search -Q[duration] Search and connect\n"
+ "\t--kill -k <bdaddr> Kill LAP connection\n"
+ "\t--killall -K Kill all LAP connections\n"
+ "\t--channel -P <channel> RFCOMM channel\n"
+ "\t--device -i <bdaddr> Source bdaddr\n"
+ "\t--nosdp -D Disable SDP\n"
+ "\t--auth -A Enable authentication\n"
+ "\t--encrypt -E Enable encryption\n"
+ "\t--secure -S Secure connection\n"
+ "\t--master -M Become the master of a piconet\n"
+ "\t--nodetach -n Do not become a daemon\n"
+ "\t--persist -p[interval] Persist mode\n"
+ "\t--pppd -d <pppd> Location of the PPP daemon (pppd)\n"
+ "\t--msdun -X[timeo] Enable Microsoft dialup networking support\n"
+ "\t--activesync -a Enable Microsoft ActiveSync networking\n"
+ "\t--cache -C[valid] Enable address cache\n";
+
+int main(int argc, char *argv[])
+{
+ char *dst = NULL, *src = NULL;
+ struct sigaction sa;
+ int mode = NONE;
+ int opt;
+
+ while ((opt=getopt_long(argc, argv, main_sopts, main_lopts, NULL)) != -1) {
+ switch(opt) {
+ case 'l':
+ mode = SHOW;
+ detach = 0;
+ break;
+
+ case 's':
+ mode = LISTEN;
+ type = LANACCESS;
+ break;
+
+ case 'c':
+ mode = CONNECT;
+ dst = strdup(optarg);
+ break;
+
+ case 'Q':
+ mode = CONNECT;
+ dst = NULL;
+ if (optarg)
+ search_duration = atoi(optarg);
+ break;
+
+ case 'k':
+ mode = KILL;
+ detach = 0;
+ dst = strdup(optarg);
+ break;
+
+ case 'K':
+ mode = KILL;
+ detach = 0;
+ dst = NULL;
+ break;
+
+ case 'P':
+ channel = atoi(optarg);
+ break;
+
+ case 'i':
+ src = strdup(optarg);
+ break;
+
+ case 'D':
+ use_sdp = 0;
+ break;
+
+ case 'A':
+ auth = 1;
+ break;
+
+ case 'E':
+ encrypt = 1;
+ break;
+
+ case 'S':
+ secure = 1;
+ break;
+
+ case 'M':
+ master = 1;
+ break;
+
+ case 'n':
+ detach = 0;
+ break;
+
+ case 'p':
+ if (optarg)
+ persist = atoi(optarg);
+ else
+ persist = 5;
+ break;
+
+ case 'C':
+ if (optarg)
+ use_cache = atoi(optarg);
+ else
+ use_cache = 2;
+ break;
+
+ case 'd':
+ pppd = strdup(optarg);
+ break;
+
+ case 'X':
+ if (optarg)
+ msdun = atoi(optarg);
+ else
+ msdun = 10;
+ break;
+
+ case 'a':
+ msdun = 10;
+ type = ACTIVESYNC;
+ break;
+
+ case 'm':
+ mode = LISTEN;
+ dst = strdup(optarg);
+ type = MROUTER;
+ break;
+
+ case 'u':
+ mode = LISTEN;
+ type = DIALUP;
+ break;
+
+ case 'h':
+ default:
+ printf(main_help, VERSION);
+ exit(0);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /* The rest is pppd options */
+ if (argc > 0) {
+ for (opt = 3; argc && opt < DUN_MAX_PPP_OPTS - 1;
+ argc--, opt++)
+ pppd_opts[opt] = *argv++;
+ pppd_opts[opt] = NULL;
+ }
+
+ io_init();
+
+ if (dun_init()) {
+ free(dst);
+ return -1;
+ }
+
+ /* Check non daemon modes first */
+ switch (mode) {
+ case SHOW:
+ do_show();
+ free(dst);
+ return 0;
+
+ case KILL:
+ do_kill(dst);
+ free(dst);
+ return 0;
+
+ case NONE:
+ printf(main_help, VERSION);
+ free(dst);
+ return 0;
+ }
+
+ /* Initialize signals */
+ 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);
+
+ sa.sa_handler = sig_hup;
+ sigaction(SIGHUP, &sa, NULL);
+
+ if (detach && daemon(0, 0)) {
+ perror("Can't start daemon");
+ exit(1);
+ }
+
+ openlog("dund", LOG_PID | LOG_NDELAY | LOG_PERROR, LOG_DAEMON);
+ syslog(LOG_INFO, "Bluetooth DUN daemon version %s", VERSION);
+
+ if (src) {
+ src_dev = hci_devid(src);
+ if (src_dev < 0 || hci_devba(src_dev, &src_addr) < 0) {
+ syslog(LOG_ERR, "Invalid source. %s(%d)", strerror(errno), errno);
+ free(dst);
+ return -1;
+ }
+ }
+
+ if (dst) {
+ strncpy(cache.dst, dst, sizeof(cache.dst) - 1);
+ str2ba(dst, &cache.bdaddr);
+
+ /* Disable cache invalidation */
+ use_cache = cache.valid = ~0;
+ }
+
+ switch (mode) {
+ case CONNECT:
+ do_connect();
+ break;
+
+ case LISTEN:
+ do_listen();
+ break;
+ }
+
+ free(dst);
+ return 0;
+}
diff --git a/compat/dund.h b/compat/dund.h
new file mode 100644
index 0000000..e3a4ef6
--- /dev/null
+++ b/compat/dund.h
@@ -0,0 +1,40 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 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
+ *
+ */
+
+#define DUN_CONFIG_DIR "/etc/bluetooth/dun"
+
+#define DUN_DEFAULT_CHANNEL 1
+
+#define DUN_MAX_PPP_OPTS 40
+
+int dun_init(void);
+int dun_cleanup(void);
+
+int dun_show_connections(void);
+int dun_kill_connection(uint8_t *dst);
+int dun_kill_all_connections(void);
+
+int dun_open_connection(int sk, char *pppd, char **pppd_opts, int wait);
+
+int ms_dun(int fd, int server, int timeo);
diff --git a/compat/fakehid.c b/compat/fakehid.c
new file mode 100644
index 0000000..b996d10
--- /dev/null
+++ b/compat/fakehid.c
@@ -0,0 +1,669 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2003-2010 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
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/poll.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/hidp.h>
+
+#include "hidd.h"
+#include "uinput.h"
+
+#include <math.h>
+
+#ifdef NEED_PPOLL
+#include "ppoll.h"
+#endif
+
+static volatile sig_atomic_t __io_canceled = 0;
+
+static void sig_hup(int sig)
+{
+}
+
+static void sig_term(int sig)
+{
+ __io_canceled = 1;
+}
+
+static void send_event(int fd, uint16_t type, uint16_t code, int32_t value)
+{
+ struct uinput_event event;
+ int len;
+
+ if (fd <= fileno(stderr))
+ return;
+
+ memset(&event, 0, sizeof(event));
+ event.type = type;
+ event.code = code;
+ event.value = value;
+
+ len = write(fd, &event, sizeof(event));
+}
+
+static int uinput_create(char *name, int keyboard, int mouse)
+{
+ struct uinput_dev dev;
+ int fd, aux;
+
+ fd = open("/dev/uinput", O_RDWR);
+ if (fd < 0) {
+ fd = open("/dev/input/uinput", O_RDWR);
+ if (fd < 0) {
+ fd = open("/dev/misc/uinput", O_RDWR);
+ if (fd < 0) {
+ fprintf(stderr, "Can't open input device: %s (%d)\n",
+ strerror(errno), errno);
+ return -1;
+ }
+ }
+ }
+
+ memset(&dev, 0, sizeof(dev));
+
+ if (name)
+ strncpy(dev.name, name, UINPUT_MAX_NAME_SIZE - 1);
+
+ dev.id.bustype = BUS_BLUETOOTH;
+ dev.id.vendor = 0x0000;
+ dev.id.product = 0x0000;
+ dev.id.version = 0x0000;
+
+ if (write(fd, &dev, sizeof(dev)) < 0) {
+ fprintf(stderr, "Can't write device information: %s (%d)\n",
+ strerror(errno), errno);
+ close(fd);
+ return -1;
+ }
+
+ if (mouse) {
+ ioctl(fd, UI_SET_EVBIT, EV_REL);
+
+ for (aux = REL_X; aux <= REL_MISC; aux++)
+ ioctl(fd, UI_SET_RELBIT, aux);
+ }
+
+ if (keyboard) {
+ ioctl(fd, UI_SET_EVBIT, EV_KEY);
+ ioctl(fd, UI_SET_EVBIT, EV_LED);
+ ioctl(fd, UI_SET_EVBIT, EV_REP);
+
+ for (aux = KEY_RESERVED; aux <= KEY_UNKNOWN; aux++)
+ ioctl(fd, UI_SET_KEYBIT, aux);
+
+ //for (aux = LED_NUML; aux <= LED_MISC; aux++)
+ // ioctl(fd, UI_SET_LEDBIT, aux);
+ }
+
+ if (mouse) {
+ ioctl(fd, UI_SET_EVBIT, EV_KEY);
+
+ for (aux = BTN_LEFT; aux <= BTN_BACK; aux++)
+ ioctl(fd, UI_SET_KEYBIT, aux);
+ }
+
+ ioctl(fd, UI_DEV_CREATE);
+
+ return fd;
+}
+
+static int rfcomm_connect(const bdaddr_t *src, const bdaddr_t *dst, 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, src);
+
+ 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, dst);
+ 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 func(int fd)
+{
+}
+
+static void back(int fd)
+{
+}
+
+static void next(int fd)
+{
+}
+
+static void button(int fd, unsigned int button, int is_press)
+{
+ switch (button) {
+ case 1:
+ send_event(fd, EV_KEY, BTN_LEFT, is_press);
+ break;
+ case 3:
+ send_event(fd, EV_KEY, BTN_RIGHT, is_press);
+ break;
+ }
+
+ send_event(fd, EV_SYN, SYN_REPORT, 0);
+}
+
+static void move(int fd, unsigned int direction)
+{
+ double angle;
+ int32_t x, y;
+
+ angle = (direction * 22.5) * 3.1415926 / 180;
+ x = (int) (sin(angle) * 8);
+ y = (int) (cos(angle) * -8);
+
+ send_event(fd, EV_REL, REL_X, x);
+ send_event(fd, EV_REL, REL_Y, y);
+
+ send_event(fd, EV_SYN, SYN_REPORT, 0);
+}
+
+static inline void epox_decode(int fd, unsigned char event)
+{
+ switch (event) {
+ case 48:
+ func(fd); break;
+ case 55:
+ back(fd); break;
+ case 56:
+ next(fd); break;
+ case 53:
+ button(fd, 1, 1); break;
+ case 121:
+ button(fd, 1, 0); break;
+ case 113:
+ break;
+ case 54:
+ button(fd, 3, 1); break;
+ case 120:
+ button(fd, 3, 0); break;
+ case 112:
+ break;
+ case 51:
+ move(fd, 0); break;
+ case 97:
+ move(fd, 1); break;
+ case 65:
+ move(fd, 2); break;
+ case 98:
+ move(fd, 3); break;
+ case 50:
+ move(fd, 4); break;
+ case 99:
+ move(fd, 5); break;
+ case 67:
+ move(fd, 6); break;
+ case 101:
+ move(fd, 7); break;
+ case 52:
+ move(fd, 8); break;
+ case 100:
+ move(fd, 9); break;
+ case 66:
+ move(fd, 10); break;
+ case 102:
+ move(fd, 11); break;
+ case 49:
+ move(fd, 12); break;
+ case 103:
+ move(fd, 13); break;
+ case 57:
+ move(fd, 14); break;
+ case 104:
+ move(fd, 15); break;
+ case 69:
+ break;
+ default:
+ printf("Unknown event code %d\n", event);
+ break;
+ }
+}
+
+int epox_presenter(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel)
+{
+ unsigned char buf[16];
+ struct sigaction sa;
+ struct pollfd p;
+ sigset_t sigs;
+ char addr[18];
+ int i, fd, sk, len;
+
+ sk = rfcomm_connect(src, dst, channel);
+ if (sk < 0)
+ return -1;
+
+ fd = uinput_create("Bluetooth Presenter", 0, 1);
+ if (fd < 0) {
+ close(sk);
+ return -1;
+ }
+
+ ba2str(dst, addr);
+
+ printf("Connected to %s on channel %d\n", addr, channel);
+ printf("Press CTRL-C for hangup\n");
+
+ 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);
+
+ sa.sa_handler = sig_hup;
+ sigaction(SIGHUP, &sa, NULL);
+
+ sigfillset(&sigs);
+ sigdelset(&sigs, SIGCHLD);
+ sigdelset(&sigs, SIGPIPE);
+ sigdelset(&sigs, SIGTERM);
+ sigdelset(&sigs, SIGINT);
+ sigdelset(&sigs, SIGHUP);
+
+ p.fd = sk;
+ p.events = POLLIN | POLLERR | POLLHUP;
+
+ while (!__io_canceled) {
+ p.revents = 0;
+ if (ppoll(&p, 1, NULL, &sigs) < 1)
+ continue;
+
+ len = read(sk, buf, sizeof(buf));
+ if (len < 0)
+ break;
+
+ for (i = 0; i < len; i++)
+ epox_decode(fd, buf[i]);
+ }
+
+ printf("Disconnected\n");
+
+ ioctl(fd, UI_DEV_DESTROY);
+
+ close(fd);
+ close(sk);
+
+ return 0;
+}
+
+int headset_presenter(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel)
+{
+ printf("Not implemented\n");
+ return -1;
+}
+
+/* The strange meta key close to Ctrl has been assigned to Esc,
+ Fn key to CtrlR and the left space to Alt*/
+
+static unsigned char jthree_keycodes[63] = {
+ KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6,
+ KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T,
+ KEY_A, KEY_S, KEY_D, KEY_F, KEY_G,
+ KEY_Z, KEY_X, KEY_C, KEY_V, KEY_B,
+ KEY_LEFTALT, KEY_TAB, KEY_CAPSLOCK, KEY_ESC,
+ KEY_7, KEY_8, KEY_9, KEY_0, KEY_MINUS, KEY_EQUAL, KEY_BACKSPACE,
+ KEY_Y, KEY_U, KEY_I, KEY_O, KEY_P, KEY_LEFTBRACE, KEY_RIGHTBRACE,
+ KEY_H, KEY_J, KEY_K, KEY_L, KEY_SEMICOLON, KEY_APOSTROPHE, KEY_ENTER,
+ KEY_N, KEY_M, KEY_COMMA, KEY_DOT, KEY_SLASH, KEY_UP,
+ KEY_SPACE, KEY_COMPOSE, KEY_LEFT, KEY_DOWN, KEY_RIGHT,
+ KEY_LEFTCTRL, KEY_RIGHTSHIFT, KEY_LEFTSHIFT, KEY_DELETE, KEY_RIGHTCTRL, KEY_RIGHTALT,
+};
+
+static inline void jthree_decode(int fd, unsigned char event)
+{
+ if (event > 63)
+ send_event(fd, EV_KEY, jthree_keycodes[event & 0x3f], 0);
+ else
+ send_event(fd, EV_KEY, jthree_keycodes[event - 1], 1);
+}
+
+int jthree_keyboard(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel)
+{
+ unsigned char buf[16];
+ struct sigaction sa;
+ struct pollfd p;
+ sigset_t sigs;
+ char addr[18];
+ int i, fd, sk, len;
+
+ sk = rfcomm_connect(src, dst, channel);
+ if (sk < 0)
+ return -1;
+
+ fd = uinput_create("J-Three Keyboard", 1, 0);
+ if (fd < 0) {
+ close(sk);
+ return -1;
+ }
+
+ ba2str(dst, addr);
+
+ printf("Connected to %s on channel %d\n", addr, channel);
+ printf("Press CTRL-C for hangup\n");
+
+ 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);
+
+ sa.sa_handler = sig_hup;
+ sigaction(SIGHUP, &sa, NULL);
+
+ sigfillset(&sigs);
+ sigdelset(&sigs, SIGCHLD);
+ sigdelset(&sigs, SIGPIPE);
+ sigdelset(&sigs, SIGTERM);
+ sigdelset(&sigs, SIGINT);
+ sigdelset(&sigs, SIGHUP);
+
+ p.fd = sk;
+ p.events = POLLIN | POLLERR | POLLHUP;
+
+ while (!__io_canceled) {
+ p.revents = 0;
+ if (ppoll(&p, 1, NULL, &sigs) < 1)
+ continue;
+
+ len = read(sk, buf, sizeof(buf));
+ if (len < 0)
+ break;
+
+ for (i = 0; i < len; i++)
+ jthree_decode(fd, buf[i]);
+ }
+
+ printf("Disconnected\n");
+
+ ioctl(fd, UI_DEV_DESTROY);
+
+ close(fd);
+ close(sk);
+
+ return 0;
+}
+
+static const int celluon_xlate_num[10] = {
+ KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9
+};
+
+static const int celluon_xlate_char[26] = {
+ KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J,
+ KEY_K, KEY_L, KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T,
+ KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z
+};
+
+static int celluon_xlate(int c)
+{
+ if (c >= '0' && c <= '9')
+ return celluon_xlate_num[c - '0'];
+
+ if (c >= 'A' && c <= 'Z')
+ return celluon_xlate_char[c - 'A'];
+
+ switch (c) {
+ case 0x08:
+ return KEY_BACKSPACE;
+ case 0x09:
+ return KEY_TAB;
+ case 0x0d:
+ return KEY_ENTER;
+ case 0x11:
+ return KEY_LEFTCTRL;
+ case 0x14:
+ return KEY_CAPSLOCK;
+ case 0x20:
+ return KEY_SPACE;
+ case 0x25:
+ return KEY_LEFT;
+ case 0x26:
+ return KEY_UP;
+ case 0x27:
+ return KEY_RIGHT;
+ case 0x28:
+ return KEY_DOWN;
+ case 0x2e:
+ return KEY_DELETE;
+ case 0x5b:
+ return KEY_MENU;
+ case 0xa1:
+ return KEY_RIGHTSHIFT;
+ case 0xa0:
+ return KEY_LEFTSHIFT;
+ case 0xba:
+ return KEY_SEMICOLON;
+ case 0xbd:
+ return KEY_MINUS;
+ case 0xbc:
+ return KEY_COMMA;
+ case 0xbb:
+ return KEY_EQUAL;
+ case 0xbe:
+ return KEY_DOT;
+ case 0xbf:
+ return KEY_SLASH;
+ case 0xc0:
+ return KEY_GRAVE;
+ case 0xdb:
+ return KEY_LEFTBRACE;
+ case 0xdc:
+ return KEY_BACKSLASH;
+ case 0xdd:
+ return KEY_RIGHTBRACE;
+ case 0xde:
+ return KEY_APOSTROPHE;
+ case 0xff03:
+ return KEY_HOMEPAGE;
+ case 0xff04:
+ return KEY_TIME;
+ case 0xff06:
+ return KEY_OPEN;
+ case 0xff07:
+ return KEY_LIST;
+ case 0xff08:
+ return KEY_MAIL;
+ case 0xff30:
+ return KEY_CALC;
+ case 0xff1a: /* Map FN to ALT */
+ return KEY_LEFTALT;
+ case 0xff2f:
+ return KEY_INFO;
+ default:
+ printf("Unknown key %x\n", c);
+ return c;
+ }
+}
+
+struct celluon_state {
+ int len; /* Expected length of current packet */
+ int count; /* Number of bytes received */
+ int action;
+ int key;
+};
+
+static void celluon_decode(int fd, struct celluon_state *s, uint8_t c)
+{
+ if (s->count < 2 && c != 0xa5) {
+ /* Lost Sync */
+ s->count = 0;
+ return;
+ }
+
+ switch (s->count) {
+ case 0:
+ /* New packet - Reset state */
+ s->len = 30;
+ s->key = 0;
+ break;
+ case 1:
+ break;
+ case 6:
+ s->action = c;
+ break;
+ case 28:
+ s->key = c;
+ if (c == 0xff)
+ s->len = 31;
+ break;
+ case 29:
+ case 30:
+ if (s->count == s->len - 1) {
+ /* TODO: Verify checksum */
+ if (s->action < 2) {
+ send_event(fd, EV_KEY, celluon_xlate(s->key),
+ s->action);
+ }
+ s->count = -1;
+ } else {
+ s->key = (s->key << 8) | c;
+ }
+ break;
+ }
+
+ s->count++;
+
+ return;
+}
+
+int celluon_keyboard(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel)
+{
+ unsigned char buf[16];
+ struct sigaction sa;
+ struct pollfd p;
+ sigset_t sigs;
+ char addr[18];
+ int i, fd, sk, len;
+ struct celluon_state s;
+
+ sk = rfcomm_connect(src, dst, channel);
+ if (sk < 0)
+ return -1;
+
+ fd = uinput_create("Celluon Keyboard", 1, 0);
+ if (fd < 0) {
+ close(sk);
+ return -1;
+ }
+
+ ba2str(dst, addr);
+
+ printf("Connected to %s on channel %d\n", addr, channel);
+ printf("Press CTRL-C for hangup\n");
+
+ 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);
+
+ sa.sa_handler = sig_hup;
+ sigaction(SIGHUP, &sa, NULL);
+
+ sigfillset(&sigs);
+ sigdelset(&sigs, SIGCHLD);
+ sigdelset(&sigs, SIGPIPE);
+ sigdelset(&sigs, SIGTERM);
+ sigdelset(&sigs, SIGINT);
+ sigdelset(&sigs, SIGHUP);
+
+ p.fd = sk;
+ p.events = POLLIN | POLLERR | POLLHUP;
+
+ memset(&s, 0, sizeof(s));
+
+ while (!__io_canceled) {
+ p.revents = 0;
+ if (ppoll(&p, 1, NULL, &sigs) < 1)
+ continue;
+
+ len = read(sk, buf, sizeof(buf));
+ if (len < 0)
+ break;
+
+ for (i = 0; i < len; i++)
+ celluon_decode(fd, &s, buf[i]);
+ }
+
+ printf("Disconnected\n");
+
+ ioctl(fd, UI_DEV_DESTROY);
+
+ close(fd);
+ close(sk);
+
+ return 0;
+}
diff --git a/compat/hidd.1 b/compat/hidd.1
new file mode 100644
index 0000000..b186ac2
--- /dev/null
+++ b/compat/hidd.1
@@ -0,0 +1,41 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.33.
+.TH HIDD "1" "May 2004" "hidd - Bluetooth HID daemon" "User Commands"
+.SH NAME
+hidd \- Bluetooth HID daemon
+.SH DESCRIPTION
+hidd - Bluetooth HID daemon
+.SS "Usage:"
+.IP
+hidd [options] [commands]
+.SH OPTIONS
+.TP
+\fB\-i\fR <hciX|bdaddr>
+Local HCI device or BD Address
+.TP
+\fB\-t\fR <timeout>
+Set idle timeout (in minutes)
+.TP
+\fB\-n\fR, \fB\-\-nodaemon\fR
+Don't fork daemon to background
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Display help
+.SS "Commands:"
+.TP
+\fB\-\-server\fR
+Start HID server
+.TP
+\fB\-\-search\fR
+Search for HID devices
+.TP
+\fB\-\-connect\fR <bdaddr>
+Connect remote HID device
+.TP
+\fB\-\-kill\fR <bdaddr>
+Terminate HID connection
+.TP
+\fB\-\-killall\fR
+Terminate all connections
+.TP
+\fB\-\-show\fR
+List current HID connections
diff --git a/compat/hidd.c b/compat/hidd.c
new file mode 100644
index 0000000..88944cf
--- /dev/null
+++ b/compat/hidd.c
@@ -0,0 +1,862 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2003-2010 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
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <signal.h>
+#include <getopt.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>
+#include <bluetooth/sdp.h>
+#include <bluetooth/hidp.h>
+
+#include "sdp.h"
+#include "hidd.h"
+
+#ifdef NEED_PPOLL
+#include "ppoll.h"
+#endif
+
+enum {
+ NONE,
+ SHOW,
+ SERVER,
+ SEARCH,
+ CONNECT,
+ KILL
+};
+
+static volatile sig_atomic_t __io_canceled = 0;
+
+static void sig_hup(int sig)
+{
+}
+
+static void sig_term(int sig)
+{
+ __io_canceled = 1;
+}
+
+static int l2cap_connect(bdaddr_t *src, bdaddr_t *dst, unsigned short psm)
+{
+ struct sockaddr_l2 addr;
+ struct l2cap_options opts;
+ int sk;
+
+ if ((sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) < 0)
+ return -1;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ bacpy(&addr.l2_bdaddr, src);
+
+ if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ close(sk);
+ return -1;
+ }
+
+ memset(&opts, 0, sizeof(opts));
+ opts.imtu = HIDP_DEFAULT_MTU;
+ opts.omtu = HIDP_DEFAULT_MTU;
+ opts.flush_to = 0xffff;
+
+ setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts));
+
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ bacpy(&addr.l2_bdaddr, dst);
+ addr.l2_psm = htobs(psm);
+
+ if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ close(sk);
+ return -1;
+ }
+
+ return sk;
+}
+
+static int l2cap_listen(const bdaddr_t *bdaddr, unsigned short psm, int lm, int backlog)
+{
+ struct sockaddr_l2 addr;
+ struct l2cap_options opts;
+ int sk;
+
+ if ((sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) < 0)
+ return -1;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ bacpy(&addr.l2_bdaddr, bdaddr);
+ addr.l2_psm = htobs(psm);
+
+ if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ close(sk);
+ return -1;
+ }
+
+ setsockopt(sk, SOL_L2CAP, L2CAP_LM, &lm, sizeof(lm));
+
+ memset(&opts, 0, sizeof(opts));
+ opts.imtu = HIDP_DEFAULT_MTU;
+ opts.omtu = HIDP_DEFAULT_MTU;
+ opts.flush_to = 0xffff;
+
+ setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts));
+
+ if (listen(sk, backlog) < 0) {
+ close(sk);
+ return -1;
+ }
+
+ return sk;
+}
+
+static int l2cap_accept(int sk, bdaddr_t *bdaddr)
+{
+ struct sockaddr_l2 addr;
+ socklen_t addrlen;
+ int nsk;
+
+ memset(&addr, 0, sizeof(addr));
+ addrlen = sizeof(addr);
+
+ if ((nsk = accept(sk, (struct sockaddr *) &addr, &addrlen)) < 0)
+ return -1;
+
+ if (bdaddr)
+ bacpy(bdaddr, &addr.l2_bdaddr);
+
+ return nsk;
+}
+
+static int request_authentication(bdaddr_t *src, bdaddr_t *dst)
+{
+ struct hci_conn_info_req *cr;
+ char addr[18];
+ int err, dd, dev_id;
+
+ ba2str(src, addr);
+ dev_id = hci_devid(addr);
+ if (dev_id < 0)
+ return dev_id;
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0)
+ return dd;
+
+ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+ if (!cr)
+ return -ENOMEM;
+
+ bacpy(&cr->bdaddr, dst);
+ cr->type = ACL_LINK;
+ err = ioctl(dd, HCIGETCONNINFO, (unsigned long) cr);
+ if (err < 0) {
+ free(cr);
+ hci_close_dev(dd);
+ return err;
+ }
+
+ err = hci_authenticate_link(dd, htobs(cr->conn_info->handle), 25000);
+
+ free(cr);
+ hci_close_dev(dd);
+
+ return err;
+}
+
+static int request_encryption(bdaddr_t *src, bdaddr_t *dst)
+{
+ struct hci_conn_info_req *cr;
+ char addr[18];
+ int err, dd, dev_id;
+
+ ba2str(src, addr);
+ dev_id = hci_devid(addr);
+ if (dev_id < 0)
+ return dev_id;
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0)
+ return dd;
+
+ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+ if (!cr)
+ return -ENOMEM;
+
+ bacpy(&cr->bdaddr, dst);
+ cr->type = ACL_LINK;
+ err = ioctl(dd, HCIGETCONNINFO, (unsigned long) cr);
+ if (err < 0) {
+ free(cr);
+ hci_close_dev(dd);
+ return err;
+ }
+
+ err = hci_encrypt_link(dd, htobs(cr->conn_info->handle), 1, 25000);
+
+ free(cr);
+ hci_close_dev(dd);
+
+ return err;
+}
+
+static void enable_sixaxis(int csk)
+{
+ const unsigned char buf[] = {
+ 0x53 /*HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE*/,
+ 0xf4, 0x42, 0x03, 0x00, 0x00 };
+ int err;
+
+ err = write(csk, buf, sizeof(buf));
+}
+
+static int create_device(int ctl, int csk, int isk, uint8_t subclass, int nosdp, int nocheck, int bootonly, int encrypt, int timeout)
+{
+ struct hidp_connadd_req req;
+ struct sockaddr_l2 addr;
+ socklen_t addrlen;
+ bdaddr_t src, dst;
+ char bda[18];
+ int err;
+
+ memset(&addr, 0, sizeof(addr));
+ addrlen = sizeof(addr);
+
+ if (getsockname(csk, (struct sockaddr *) &addr, &addrlen) < 0)
+ return -1;
+
+ bacpy(&src, &addr.l2_bdaddr);
+
+ memset(&addr, 0, sizeof(addr));
+ addrlen = sizeof(addr);
+
+ if (getpeername(csk, (struct sockaddr *) &addr, &addrlen) < 0)
+ return -1;
+
+ bacpy(&dst, &addr.l2_bdaddr);
+
+ memset(&req, 0, sizeof(req));
+ req.ctrl_sock = csk;
+ req.intr_sock = isk;
+ req.flags = 0;
+ req.idle_to = timeout * 60;
+
+ err = get_stored_device_info(&src, &dst, &req);
+ if (!err)
+ goto create;
+
+ if (!nocheck) {
+ ba2str(&dst, bda);
+ syslog(LOG_ERR, "Rejected connection from unknown device %s", bda);
+ /* Return no error to avoid run_server() complaining too */
+ return 0;
+ }
+
+ if (!nosdp) {
+ err = get_sdp_device_info(&src, &dst, &req);
+ if (err < 0)
+ goto error;
+ } else {
+ struct l2cap_conninfo conn;
+ socklen_t size;
+ uint8_t class[3];
+
+ memset(&conn, 0, sizeof(conn));
+ size = sizeof(conn);
+ if (getsockopt(csk, SOL_L2CAP, L2CAP_CONNINFO, &conn, &size) < 0)
+ memset(class, 0, 3);
+ else
+ memcpy(class, conn.dev_class, 3);
+
+ if (class[1] == 0x25 && (class[2] == 0x00 || class[2] == 0x01))
+ req.subclass = class[0];
+ else
+ req.subclass = 0xc0;
+ }
+
+create:
+ if (subclass != 0x00)
+ req.subclass = subclass;
+
+ ba2str(&dst, bda);
+ syslog(LOG_INFO, "New HID device %s (%s)", bda, req.name);
+
+ if (encrypt && (req.subclass & 0x40)) {
+ err = request_authentication(&src, &dst);
+ if (err < 0) {
+ syslog(LOG_ERR, "Authentication for %s failed", bda);
+ goto error;
+ }
+
+ err = request_encryption(&src, &dst);
+ if (err < 0)
+ syslog(LOG_ERR, "Encryption for %s failed", bda);
+ }
+
+ if (bootonly) {
+ req.rd_size = 0;
+ req.flags |= (1 << HIDP_BOOT_PROTOCOL_MODE);
+ }
+
+ if (req.vendor == 0x054c && req.product == 0x0268)
+ enable_sixaxis(csk);
+
+ err = ioctl(ctl, HIDPCONNADD, &req);
+
+error:
+ if (req.rd_data)
+ free(req.rd_data);
+
+ return err;
+}
+
+static void run_server(int ctl, int csk, int isk, uint8_t subclass, int nosdp, int nocheck, int bootonly, int encrypt, int timeout)
+{
+ struct pollfd p[2];
+ sigset_t sigs;
+ short events;
+ int err, ncsk, nisk;
+
+ sigfillset(&sigs);
+ sigdelset(&sigs, SIGCHLD);
+ sigdelset(&sigs, SIGPIPE);
+ sigdelset(&sigs, SIGTERM);
+ sigdelset(&sigs, SIGINT);
+ sigdelset(&sigs, SIGHUP);
+
+ p[0].fd = csk;
+ p[0].events = POLLIN | POLLERR | POLLHUP;
+
+ p[1].fd = isk;
+ p[1].events = POLLIN | POLLERR | POLLHUP;
+
+ while (!__io_canceled) {
+ p[0].revents = 0;
+ p[1].revents = 0;
+
+ if (ppoll(p, 2, NULL, &sigs) < 1)
+ continue;
+
+ events = p[0].revents | p[1].revents;
+
+ if (events & POLLIN) {
+ ncsk = l2cap_accept(csk, NULL);
+ nisk = l2cap_accept(isk, NULL);
+
+ err = create_device(ctl, ncsk, nisk, subclass, nosdp, nocheck, bootonly, encrypt, timeout);
+ if (err < 0)
+ syslog(LOG_ERR, "HID create error %d (%s)",
+ errno, strerror(errno));
+
+ close(nisk);
+ sleep(1);
+ close(ncsk);
+ }
+ }
+}
+
+static char *hidp_state[] = {
+ "unknown",
+ "connected",
+ "open",
+ "bound",
+ "listening",
+ "connecting",
+ "connecting",
+ "config",
+ "disconnecting",
+ "closed"
+};
+
+static char *hidp_flagstostr(uint32_t flags)
+{
+ static char str[100];
+ str[0] = 0;
+
+ strcat(str, "[");
+
+ if (flags & (1 << HIDP_BOOT_PROTOCOL_MODE))
+ strcat(str, "boot-protocol");
+
+ strcat(str, "]");
+
+ return str;
+}
+
+static void do_show(int ctl)
+{
+ struct hidp_connlist_req req;
+ struct hidp_conninfo ci[16];
+ char addr[18];
+ unsigned int i;
+
+ req.cnum = 16;
+ req.ci = ci;
+
+ if (ioctl(ctl, HIDPGETCONNLIST, &req) < 0) {
+ perror("Can't get connection list");
+ close(ctl);
+ exit(1);
+ }
+
+ for (i = 0; i < req.cnum; i++) {
+ ba2str(&ci[i].bdaddr, addr);
+ printf("%s %s [%04x:%04x] %s %s\n", addr, ci[i].name,
+ ci[i].vendor, ci[i].product, hidp_state[ci[i].state],
+ ci[i].flags ? hidp_flagstostr(ci[i].flags) : "");
+ }
+}
+
+static void do_connect(int ctl, bdaddr_t *src, bdaddr_t *dst, uint8_t subclass, int fakehid, int bootonly, int encrypt, int timeout)
+{
+ struct hidp_connadd_req req;
+ uint16_t uuid = HID_SVCLASS_ID;
+ uint8_t channel = 0;
+ char name[256];
+ int csk, isk, err;
+
+ memset(&req, 0, sizeof(req));
+ name[0] = '\0';
+
+ err = get_sdp_device_info(src, dst, &req);
+ if (err < 0 && fakehid)
+ err = get_alternate_device_info(src, dst,
+ &uuid, &channel, name, sizeof(name) - 1);
+
+ if (err < 0) {
+ perror("Can't get device information");
+ close(ctl);
+ exit(1);
+ }
+
+ switch (uuid) {
+ case HID_SVCLASS_ID:
+ goto connect;
+
+ case SERIAL_PORT_SVCLASS_ID:
+ if (subclass == 0x40 || !strcmp(name, "Cable Replacement")) {
+ if (epox_presenter(src, dst, channel) < 0) {
+ close(ctl);
+ exit(1);
+ }
+ break;
+ }
+ if (subclass == 0x1f || !strcmp(name, "SPP slave")) {
+ if (jthree_keyboard(src, dst, channel) < 0) {
+ close(ctl);
+ exit(1);
+ }
+ break;
+ }
+ if (subclass == 0x02 || !strcmp(name, "Serial Port")) {
+ if (celluon_keyboard(src, dst, channel) < 0) {
+ close(ctl);
+ exit(1);
+ }
+ break;
+ }
+ break;
+
+ case HEADSET_SVCLASS_ID:
+ case HANDSFREE_SVCLASS_ID:
+ if (headset_presenter(src, dst, channel) < 0) {
+ close(ctl);
+ exit(1);
+ }
+ break;
+ }
+
+ return;
+
+connect:
+ csk = l2cap_connect(src, dst, L2CAP_PSM_HIDP_CTRL);
+ if (csk < 0) {
+ perror("Can't create HID control channel");
+ close(ctl);
+ exit(1);
+ }
+
+ isk = l2cap_connect(src, dst, L2CAP_PSM_HIDP_INTR);
+ if (isk < 0) {
+ perror("Can't create HID interrupt channel");
+ close(csk);
+ close(ctl);
+ exit(1);
+ }
+
+ err = create_device(ctl, csk, isk, subclass, 1, 1, bootonly, encrypt, timeout);
+ if (err < 0) {
+ fprintf(stderr, "HID create error %d (%s)\n",
+ errno, strerror(errno));
+ close(isk);
+ sleep(1);
+ close(csk);
+ close(ctl);
+ exit(1);
+ }
+}
+
+static void do_search(int ctl, bdaddr_t *bdaddr, uint8_t subclass, int fakehid, int bootonly, int encrypt, int timeout)
+{
+ inquiry_info *info = NULL;
+ bdaddr_t src, dst;
+ int i, dev_id, num_rsp, length, flags;
+ char addr[18];
+ uint8_t class[3];
+
+ ba2str(bdaddr, addr);
+ dev_id = hci_devid(addr);
+ if (dev_id < 0) {
+ dev_id = hci_get_route(NULL);
+ hci_devba(dev_id, &src);
+ } else
+ bacpy(&src, bdaddr);
+
+ length = 8; /* ~10 seconds */
+ num_rsp = 0;
+ flags = IREQ_CACHE_FLUSH;
+
+ printf("Searching ...\n");
+
+ num_rsp = hci_inquiry(dev_id, length, num_rsp, NULL, &info, flags);
+
+ for (i = 0; i < num_rsp; i++) {
+ memcpy(class, (info+i)->dev_class, 3);
+ if (class[1] == 0x25 && (class[2] == 0x00 || class[2] == 0x01)) {
+ bacpy(&dst, &(info+i)->bdaddr);
+ ba2str(&dst, addr);
+
+ printf("\tConnecting to device %s\n", addr);
+ do_connect(ctl, &src, &dst, subclass, fakehid, bootonly, encrypt, timeout);
+ }
+ }
+
+ if (!fakehid)
+ goto done;
+
+ for (i = 0; i < num_rsp; i++) {
+ memcpy(class, (info+i)->dev_class, 3);
+ if ((class[0] == 0x00 && class[2] == 0x00 &&
+ (class[1] == 0x40 || class[1] == 0x1f)) ||
+ (class[0] == 0x10 && class[1] == 0x02 && class[2] == 0x40)) {
+ bacpy(&dst, &(info+i)->bdaddr);
+ ba2str(&dst, addr);
+
+ printf("\tConnecting to device %s\n", addr);
+ do_connect(ctl, &src, &dst, subclass, 1, bootonly, 0, timeout);
+ }
+ }
+
+done:
+ bt_free(info);
+
+ if (!num_rsp) {
+ fprintf(stderr, "\tNo devices in range or visible\n");
+ close(ctl);
+ exit(1);
+ }
+}
+
+static void do_kill(int ctl, bdaddr_t *bdaddr, uint32_t flags)
+{
+ struct hidp_conndel_req req;
+ struct hidp_connlist_req cl;
+ struct hidp_conninfo ci[16];
+ unsigned int i;
+
+ if (!bacmp(bdaddr, BDADDR_ALL)) {
+ cl.cnum = 16;
+ cl.ci = ci;
+
+ if (ioctl(ctl, HIDPGETCONNLIST, &cl) < 0) {
+ perror("Can't get connection list");
+ close(ctl);
+ exit(1);
+ }
+
+ for (i = 0; i < cl.cnum; i++) {
+ bacpy(&req.bdaddr, &ci[i].bdaddr);
+ req.flags = flags;
+
+ if (ioctl(ctl, HIDPCONNDEL, &req) < 0) {
+ perror("Can't release connection");
+ close(ctl);
+ exit(1);
+ }
+ }
+
+ } else {
+ bacpy(&req.bdaddr, bdaddr);
+ req.flags = flags;
+
+ if (ioctl(ctl, HIDPCONNDEL, &req) < 0) {
+ perror("Can't release connection");
+ close(ctl);
+ exit(1);
+ }
+ }
+}
+
+static void usage(void)
+{
+ printf("hidd - Bluetooth HID daemon version %s\n\n", VERSION);
+
+ printf("Usage:\n"
+ "\thidd [options] [commands]\n"
+ "\n");
+
+ printf("Options:\n"
+ "\t-i <hciX|bdaddr> Local HCI device or BD Address\n"
+ "\t-t <timeout> Set idle timeout (in minutes)\n"
+ "\t-b <subclass> Overwrite the boot mode subclass\n"
+ "\t-n, --nodaemon Don't fork daemon to background\n"
+ "\t-h, --help Display help\n"
+ "\n");
+
+ printf("Commands:\n"
+ "\t--server Start HID server\n"
+ "\t--search Search for HID devices\n"
+ "\t--connect <bdaddr> Connect remote HID device\n"
+ "\t--unplug <bdaddr> Unplug the HID connection\n"
+ "\t--kill <bdaddr> Terminate HID connection\n"
+ "\t--killall Terminate all connections\n"
+ "\t--show List current HID connections\n"
+ "\n");
+}
+
+static struct option main_options[] = {
+ { "help", 0, 0, 'h' },
+ { "nodaemon", 0, 0, 'n' },
+ { "subclass", 1, 0, 'b' },
+ { "timeout", 1, 0, 't' },
+ { "device", 1, 0, 'i' },
+ { "master", 0, 0, 'M' },
+ { "encrypt", 0, 0, 'E' },
+ { "nosdp", 0, 0, 'D' },
+ { "nocheck", 0, 0, 'Z' },
+ { "bootonly", 0, 0, 'B' },
+ { "hidonly", 0, 0, 'H' },
+ { "show", 0, 0, 'l' },
+ { "list", 0, 0, 'l' },
+ { "server", 0, 0, 'd' },
+ { "listen", 0, 0, 'd' },
+ { "search", 0, 0, 's' },
+ { "create", 1, 0, 'c' },
+ { "connect", 1, 0, 'c' },
+ { "disconnect", 1, 0, 'k' },
+ { "terminate", 1, 0, 'k' },
+ { "release", 1, 0, 'k' },
+ { "kill", 1, 0, 'k' },
+ { "killall", 0, 0, 'K' },
+ { "unplug", 1, 0, 'u' },
+ { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ struct sigaction sa;
+ bdaddr_t bdaddr, dev;
+ uint32_t flags = 0;
+ uint8_t subclass = 0x00;
+ char addr[18];
+ int log_option = LOG_NDELAY | LOG_PID;
+ int opt, ctl, csk, isk;
+ int mode = SHOW, detach = 1, nosdp = 0, nocheck = 0, bootonly = 0;
+ int fakehid = 1, encrypt = 0, timeout = 30, lm = 0;
+
+ bacpy(&bdaddr, BDADDR_ANY);
+
+ while ((opt = getopt_long(argc, argv, "+i:nt:b:MEDZBHldsc:k:Ku:h", main_options, NULL)) != -1) {
+ switch(opt) {
+ case 'i':
+ if (!strncasecmp(optarg, "hci", 3))
+ hci_devba(atoi(optarg + 3), &bdaddr);
+ else
+ str2ba(optarg, &bdaddr);
+ break;
+ case 'n':
+ detach = 0;
+ break;
+ case 't':
+ timeout = atoi(optarg);
+ break;
+ case 'b':
+ if (!strncasecmp(optarg, "0x", 2))
+ subclass = (uint8_t) strtol(optarg, NULL, 16);
+ else
+ subclass = atoi(optarg);
+ break;
+ case 'M':
+ lm |= L2CAP_LM_MASTER;
+ break;
+ case 'E':
+ encrypt = 1;
+ break;
+ case 'D':
+ nosdp = 1;
+ break;
+ case 'Z':
+ nocheck = 1;
+ break;
+ case 'B':
+ bootonly = 1;
+ break;
+ case 'H':
+ fakehid = 0;
+ break;
+ case 'l':
+ mode = SHOW;
+ break;
+ case 'd':
+ mode = SERVER;
+ break;
+ case 's':
+ mode = SEARCH;
+ break;
+ case 'c':
+ str2ba(optarg, &dev);
+ mode = CONNECT;
+ break;
+ case 'k':
+ str2ba(optarg, &dev);
+ mode = KILL;
+ break;
+ case 'K':
+ bacpy(&dev, BDADDR_ALL);
+ mode = KILL;
+ break;
+ case 'u':
+ str2ba(optarg, &dev);
+ flags = (1 << HIDP_VIRTUAL_CABLE_UNPLUG);
+ mode = KILL;
+ break;
+ case 'h':
+ usage();
+ exit(0);
+ default:
+ exit(0);
+ }
+ }
+
+ ba2str(&bdaddr, addr);
+
+ ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HIDP);
+ if (ctl < 0) {
+ perror("Can't open HIDP control socket");
+ exit(1);
+ }
+
+ switch (mode) {
+ case SERVER:
+ csk = l2cap_listen(&bdaddr, L2CAP_PSM_HIDP_CTRL, lm, 10);
+ if (csk < 0) {
+ perror("Can't listen on HID control channel");
+ close(ctl);
+ exit(1);
+ }
+
+ isk = l2cap_listen(&bdaddr, L2CAP_PSM_HIDP_INTR, lm, 10);
+ if (isk < 0) {
+ perror("Can't listen on HID interrupt channel");
+ close(ctl);
+ close(csk);
+ exit(1);
+ }
+ break;
+
+ case SEARCH:
+ do_search(ctl, &bdaddr, subclass, fakehid, bootonly, encrypt, timeout);
+ close(ctl);
+ exit(0);
+
+ case CONNECT:
+ do_connect(ctl, &bdaddr, &dev, subclass, fakehid, bootonly, encrypt, timeout);
+ close(ctl);
+ exit(0);
+
+ case KILL:
+ do_kill(ctl, &dev, flags);
+ close(ctl);
+ exit(0);
+
+ default:
+ do_show(ctl);
+ close(ctl);
+ exit(0);
+ }
+
+ if (detach) {
+ if (daemon(0, 0)) {
+ perror("Can't start daemon");
+ exit(1);
+ }
+ } else
+ log_option |= LOG_PERROR;
+
+ openlog("hidd", log_option, LOG_DAEMON);
+
+ if (bacmp(&bdaddr, BDADDR_ANY))
+ syslog(LOG_INFO, "Bluetooth HID daemon (%s)", addr);
+ else
+ syslog(LOG_INFO, "Bluetooth HID daemon");
+
+ 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_hup;
+ sigaction(SIGHUP, &sa, NULL);
+
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGCHLD, &sa, NULL);
+ sigaction(SIGPIPE, &sa, NULL);
+
+ run_server(ctl, csk, isk, subclass, nosdp, nocheck, bootonly, encrypt, timeout);
+
+ syslog(LOG_INFO, "Exit");
+
+ close(csk);
+ close(isk);
+ close(ctl);
+
+ return 0;
+}
diff --git a/compat/hidd.h b/compat/hidd.h
new file mode 100644
index 0000000..0536967
--- /dev/null
+++ b/compat/hidd.h
@@ -0,0 +1,30 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2003-2010 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
+ *
+ */
+
+#define L2CAP_PSM_HIDP_CTRL 0x11
+#define L2CAP_PSM_HIDP_INTR 0x13
+
+int epox_presenter(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel);
+int headset_presenter(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel);
+int jthree_keyboard(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel);
+int celluon_keyboard(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel);
diff --git a/compat/lib.h b/compat/lib.h
new file mode 100644
index 0000000..3b3aeb5
--- /dev/null
+++ b/compat/lib.h
@@ -0,0 +1,86 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 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
+ *
+ */
+
+#include <sys/types.h>
+#include <errno.h>
+#include <signal.h>
+
+#ifndef min
+#define min(a,b) ( (a)<(b) ? (a):(b) )
+#endif
+
+/* IO cancelation */
+extern volatile sig_atomic_t __io_canceled;
+
+static inline void io_init(void)
+{
+ __io_canceled = 0;
+}
+
+static inline void io_cancel(void)
+{
+ __io_canceled = 1;
+}
+
+/* Read exactly len bytes (Signal safe)*/
+static inline int read_n(int fd, char *buf, int len)
+{
+ register int t = 0, w;
+
+ 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, char *buf, int len)
+{
+ register int t = 0, w;
+
+ 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;
+}
diff --git a/compat/msdun.c b/compat/msdun.c
new file mode 100644
index 0000000..ae88c0c
--- /dev/null
+++ b/compat/msdun.c
@@ -0,0 +1,153 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 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 <stdint.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <setjmp.h>
+#include <string.h>
+
+#include "lib.h"
+#include "dund.h"
+
+#define MS_PPP 2
+#define MS_SUCCESS 1
+#define MS_FAILED -1
+#define MS_TIMEOUT -2
+
+static sigjmp_buf jmp;
+static int retry;
+static int timeout;
+
+static void sig_alarm(int sig)
+{
+ siglongjmp(jmp, MS_TIMEOUT);
+}
+
+static int w4_str(int fd, char *str)
+{
+ char buf[40];
+ unsigned len = 0;
+ int r;
+
+ while (1) {
+ r = read(fd, buf + len, sizeof(buf) - len - 1);
+ if (r < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ break;
+ }
+ if (!r)
+ break;
+
+ len += r;
+
+ if (len < strlen(str))
+ continue;
+ buf[len] = 0;
+
+ if (strstr(buf, str))
+ return MS_SUCCESS;
+
+ /* Detect PPP */
+ if (strchr(buf, '~'))
+ return MS_PPP;
+ }
+ return MS_FAILED;
+}
+
+static int ms_server(int fd)
+{
+ switch (w4_str(fd, "CLIENT")) {
+ case MS_SUCCESS:
+ write_n(fd, "CLIENTSERVER", 12);
+ case MS_PPP:
+ return MS_SUCCESS;
+ default:
+ return MS_FAILED;
+ }
+}
+
+static int ms_client(int fd)
+{
+ write_n(fd, "CLIENT", 6);
+ return w4_str(fd, "CLIENTSERVER");
+}
+
+int ms_dun(int fd, int server, int timeo)
+{
+ sig_t osig;
+
+ retry = 4;
+ timeout = timeo;
+
+ if (!server)
+ timeout /= retry;
+
+ osig = signal(SIGALRM, sig_alarm);
+
+ while (1) {
+ int r = sigsetjmp(jmp, 1);
+ if (r) {
+ if (r == MS_TIMEOUT && !server && --retry)
+ continue;
+
+ alarm(0);
+ signal(SIGALRM, osig);
+
+ switch (r) {
+ case MS_SUCCESS:
+ case MS_PPP:
+ errno = 0;
+ return 0;
+
+ case MS_FAILED:
+ errno = EPROTO;
+ break;
+
+ case MS_TIMEOUT:
+ errno = ETIMEDOUT;
+ break;
+ }
+ return -1;
+ }
+
+ alarm(timeout);
+
+ if (server)
+ r = ms_server(fd);
+ else
+ r = ms_client(fd);
+
+ siglongjmp(jmp, r);
+ }
+}
diff --git a/compat/pand.1 b/compat/pand.1
new file mode 100644
index 0000000..4603b8b
--- /dev/null
+++ b/compat/pand.1
@@ -0,0 +1,77 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.29.
+.TH BlueZ "1" "February 2003" "PAN daemon" "User Commands"
+.SH NAME
+pand \- BlueZ Bluetooth PAN daemon
+.SH DESCRIPTION
+The pand PAN daemon allows your computer to connect to ethernet
+networks using Bluetooth.
+.SH SYNPOSIS
+pand <options>
+.SH OPTIONS
+.TP
+\fB\-\-show\fR \fB\-\-list\fR \fB\-l\fR
+Show active PAN connections
+.TP
+\fB\-\-listen\fR \fB\-s\fR
+Listen for PAN connections
+.TP
+\fB\-\-connect\fR \fB\-c\fR <bdaddr>
+Create PAN connection
+.TP
+\fB\-\-search\fR \fB\-Q[duration]\fR
+Search and connect
+.TP
+\fB\-\-kill\fR \fB\-k\fR <bdaddr>
+Kill PAN connection
+.TP
+\fB\-\-killall\fR \fB\-K\fR
+Kill all PAN connections
+.TP
+\fB\-\-role\fR \fB\-r\fR <role>
+Local PAN role (PANU, NAP, GN)
+.TP
+\fB\-\-service\fR \fB\-d\fR <role>
+Remote PAN service (PANU, NAP, GN)
+.TP
+\fB\-\-ethernet\fR \fB\-e\fR <name>
+Network interface name
+.TP
+\fB\-\-device\fR \fB\-i\fR <bdaddr>
+Source bdaddr
+.TP
+\fB\-\-nosdp\fR \fB\-D\fR
+Disable SDP
+.TP
+\fB\-\-encrypt\fR \fB\-E\fR
+Enable encryption
+.TP
+\fB\-\-secure\fR \fB\-S\fR
+Secure connection
+.TP
+\fB\-\-master\fR \fB\-M\fR
+Become the master of a piconet
+.TP
+\fB\-\-nodetach\fR \fB\-n\fR
+Do not become a daemon
+.TP
+\fB\-\-persist\fR \fB\-p[interval]\fR
+Persist mode
+.TP
+\fB\-\-cache\fR \fB\-C[valid]\fR
+Cache addresses
+.TP
+\fB\-\-pidfile\fR \fB\-P <pidfile>\fR
+Create PID file
+.TP
+\fB\-\-devup\fR \fB\-u <script>\fR
+Script to run when interface comes up
+.TP
+\fB\-\-devdown\fR \fB\-o <script>\fR
+Script to run when interface comes down
+.TP
+\fB\-\-autozap\fR \fB\-z\fR
+Disconnect automatically on exit
+
+.SH SCRIPTS
+The devup/devdown script will be called with bluetooth device as first argument
+and bluetooth destination address as second argument.
diff --git a/compat/pand.c b/compat/pand.c
new file mode 100644
index 0000000..b82650e
--- /dev/null
+++ b/compat/pand.c
@@ -0,0 +1,811 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 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
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <signal.h>
+#include <getopt.h>
+#include <sys/poll.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/bnep.h>
+#include <bluetooth/hidp.h>
+
+#include "sdp.h"
+#include "pand.h"
+
+#ifdef NEED_PPOLL
+#include "ppoll.h"
+#endif
+
+static uint16_t role = BNEP_SVC_PANU; /* Local role (ie service) */
+static uint16_t service = BNEP_SVC_NAP; /* Remote service */
+
+static int detach = 1;
+static int persist;
+static int use_sdp = 1;
+static int use_cache;
+static int link_mode = 0;
+static int cleanup;
+static int search_duration = 10;
+
+static struct {
+ int valid;
+ char dst[40];
+ bdaddr_t bdaddr;
+} cache;
+
+static char netdev[16] = "bnep%d";
+static char *pidfile = NULL;
+static char *devupcmd = NULL;
+static char *devdowncmd = NULL;
+
+static bdaddr_t src_addr = *BDADDR_ANY;
+static int src_dev = -1;
+
+static volatile int terminate;
+
+static void do_kill(char *dst);
+
+enum {
+ NONE,
+ SHOW,
+ LISTEN,
+ CONNECT,
+ KILL
+} modes;
+
+struct script_arg {
+ char dev[20];
+ char dst[20];
+ int sk;
+ int nsk;
+};
+
+static void run_script(char *script, char *dev, char *dst, int sk, int nsk)
+{
+ char *argv[4];
+ struct sigaction sa;
+
+ if (!script)
+ return;
+
+ if (access(script, R_OK | X_OK))
+ return;
+
+ if (fork())
+ return;
+
+ if (sk >= 0)
+ close(sk);
+
+ if (nsk >= 0)
+ close(nsk);
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = SIG_DFL;
+ sigaction(SIGCHLD, &sa, NULL);
+ sigaction(SIGPIPE, &sa, NULL);
+
+ argv[0] = script;
+ argv[1] = dev;
+ argv[2] = dst;
+ argv[3] = NULL;
+
+ execv(script, argv);
+
+ exit(1);
+}
+
+/* Wait for disconnect or error condition on the socket */
+static int w4_hup(int sk, struct script_arg *down_cmd)
+{
+ struct pollfd pf;
+ sigset_t sigs;
+ int n;
+
+ sigfillset(&sigs);
+ sigdelset(&sigs, SIGCHLD);
+ sigdelset(&sigs, SIGPIPE);
+ sigdelset(&sigs, SIGTERM);
+ sigdelset(&sigs, SIGINT);
+ sigdelset(&sigs, SIGHUP);
+
+ while (!terminate) {
+ pf.fd = sk;
+ pf.events = POLLERR | POLLHUP;
+
+ n = ppoll(&pf, 1, NULL, &sigs);
+
+ if (n < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+
+ syslog(LOG_ERR, "Poll failed. %s(%d)",
+ strerror(errno), errno);
+
+ return 1;
+ }
+
+ if (n) {
+ int err = 0;
+ socklen_t olen = sizeof(err);
+
+ getsockopt(sk, SOL_SOCKET, SO_ERROR, &err, &olen);
+
+ syslog(LOG_INFO, "%s disconnected%s%s", netdev,
+ err ? " : " : "", err ? strerror(err) : "");
+
+ if (down_cmd)
+ run_script(devdowncmd,
+ down_cmd->dev, down_cmd->dst,
+ down_cmd->sk, down_cmd->nsk);
+
+ close(sk);
+
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+static int do_listen(void)
+{
+ struct l2cap_options l2o;
+ struct sockaddr_l2 l2a;
+ socklen_t olen;
+ int sk, lm;
+
+ if (use_sdp)
+ bnep_sdp_register(&src_addr, role);
+
+ /* Create L2CAP socket and bind it to PSM BNEP */
+ sk = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+ if (sk < 0) {
+ syslog(LOG_ERR, "Cannot create L2CAP socket. %s(%d)",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ memset(&l2a, 0, sizeof(l2a));
+ l2a.l2_family = AF_BLUETOOTH;
+ bacpy(&l2a.l2_bdaddr, &src_addr);
+ l2a.l2_psm = htobs(BNEP_PSM);
+
+ if (bind(sk, (struct sockaddr *) &l2a, sizeof(l2a))) {
+ syslog(LOG_ERR, "Bind failed. %s(%d)",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ /* Setup L2CAP options according to BNEP spec */
+ memset(&l2o, 0, sizeof(l2o));
+ olen = sizeof(l2o);
+ if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &olen) < 0) {
+ syslog(LOG_ERR, "Failed to get L2CAP options. %s(%d)",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ l2o.imtu = l2o.omtu = BNEP_MTU;
+ if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, sizeof(l2o)) < 0) {
+ syslog(LOG_ERR, "Failed to set L2CAP options. %s(%d)",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ /* Set link mode */
+ lm = link_mode;
+ if (lm && setsockopt(sk, SOL_L2CAP, L2CAP_LM, &lm, sizeof(lm)) < 0) {
+ syslog(LOG_ERR, "Failed to set link mode. %s(%d)",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ listen(sk, 10);
+
+ while (!terminate) {
+ socklen_t alen = sizeof(l2a);
+ char devname[16];
+ int nsk;
+
+ nsk = accept(sk, (struct sockaddr *) &l2a, &alen);
+ if (nsk < 0) {
+ syslog(LOG_ERR, "Accept failed. %s(%d)",
+ strerror(errno), errno);
+ continue;
+ }
+
+ switch (fork()) {
+ case 0:
+ break;
+ case -1:
+ syslog(LOG_ERR, "Fork failed. %s(%d)",
+ strerror(errno), errno);
+ default:
+ close(nsk);
+ continue;
+ }
+
+ strncpy(devname, netdev, 16);
+ devname[15] = '\0';
+
+ if (!bnep_accept_connection(nsk, role, devname)) {
+ char str[40];
+ struct script_arg down_cmd;
+
+ ba2str(&l2a.l2_bdaddr, str);
+
+ syslog(LOG_INFO, "New connection from %s at %s",
+ str, devname);
+
+ run_script(devupcmd, devname, str, sk, nsk);
+
+ memset(&down_cmd, 0, sizeof(struct script_arg));
+ strncpy(down_cmd.dev, devname, strlen(devname) + 1);
+ strncpy(down_cmd.dst, str, strlen(str) + 1);
+ down_cmd.sk = sk;
+ down_cmd.nsk = nsk;
+ w4_hup(nsk, &down_cmd);
+ } else {
+ syslog(LOG_ERR, "Connection failed. %s(%d)",
+ strerror(errno), errno);
+ }
+
+ close(nsk);
+ exit(0);
+ }
+
+ if (use_sdp)
+ bnep_sdp_unregister();
+
+ return 0;
+}
+
+/* Connect and initiate BNEP session
+ * Returns:
+ * -1 - critical error (exit persist mode)
+ * 1 - non critical error
+ * 0 - success
+ */
+static int create_connection(char *dst, bdaddr_t *bdaddr)
+{
+ struct l2cap_options l2o;
+ struct sockaddr_l2 l2a;
+ socklen_t olen;
+ int sk, r = 0;
+ struct script_arg down_cmd;
+
+ syslog(LOG_INFO, "Connecting to %s", dst);
+
+ sk = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+ if (sk < 0) {
+ syslog(LOG_ERR, "Cannot create L2CAP socket. %s(%d)",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ /* Setup L2CAP options according to BNEP spec */
+ memset(&l2o, 0, sizeof(l2o));
+ olen = sizeof(l2o);
+ getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &olen);
+ l2o.imtu = l2o.omtu = BNEP_MTU;
+ setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, sizeof(l2o));
+
+ memset(&l2a, 0, sizeof(l2a));
+ l2a.l2_family = AF_BLUETOOTH;
+ bacpy(&l2a.l2_bdaddr, &src_addr);
+
+ if (bind(sk, (struct sockaddr *) &l2a, sizeof(l2a)))
+ syslog(LOG_ERR, "Bind failed. %s(%d)",
+ strerror(errno), errno);
+
+ memset(&l2a, 0, sizeof(l2a));
+ l2a.l2_family = AF_BLUETOOTH;
+ bacpy(&l2a.l2_bdaddr, bdaddr);
+ l2a.l2_psm = htobs(BNEP_PSM);
+
+ if (!connect(sk, (struct sockaddr *) &l2a, sizeof(l2a)) &&
+ !bnep_create_connection(sk, role, service, netdev)) {
+
+ syslog(LOG_INFO, "%s connected", netdev);
+
+ run_script(devupcmd, netdev, dst, sk, -1);
+
+ if (persist || devdowncmd) {
+ memset(&down_cmd, 0, sizeof(struct script_arg));
+ strncpy(down_cmd.dev, netdev, strlen(netdev) + 1);
+ strncpy(down_cmd.dst, dst, strlen(dst) + 1);
+ down_cmd.sk = sk;
+ down_cmd.nsk = -1;
+ w4_hup(sk, &down_cmd);
+
+ if (terminate && cleanup) {
+ syslog(LOG_INFO, "Disconnecting from %s.", dst);
+ do_kill(dst);
+ }
+ }
+
+ r = 0;
+ } else {
+ syslog(LOG_ERR, "Connect to %s failed. %s(%d)",
+ dst, strerror(errno), errno);
+ r = 1;
+ }
+
+ close(sk);
+
+ if (use_cache) {
+ if (!r) {
+ /* Succesesful connection, validate cache */
+ strcpy(cache.dst, dst);
+ bacpy(&cache.bdaddr, bdaddr);
+ cache.valid = use_cache;
+ } else
+ cache.valid--;
+ }
+
+ return r;
+}
+
+/* Search and connect
+ * Returns:
+ * -1 - critical error (exit persist mode)
+ * 1 - non critical error
+ * 0 - success
+ */
+static int do_connect(void)
+{
+ inquiry_info *ii;
+ int reconnect = 0;
+ int i, n, r = 0;
+
+ do {
+ if (reconnect)
+ sleep(persist);
+ reconnect = 1;
+
+ if (cache.valid > 0) {
+ /* Use cached bdaddr */
+ r = create_connection(cache.dst, &cache.bdaddr);
+ if (r < 0) {
+ terminate = 1;
+ break;
+ }
+ continue;
+ }
+
+ syslog(LOG_INFO, "Inquiring");
+
+ /* FIXME: Should we use non general LAP here ? */
+
+ ii = NULL;
+ n = hci_inquiry(src_dev, search_duration, 0, NULL, &ii, 0);
+ if (n < 0) {
+ syslog(LOG_ERR, "Inquiry failed. %s(%d)",
+ strerror(errno), errno);
+ continue;
+ }
+
+ for (i = 0; i < n; i++) {
+ char dst[40];
+ ba2str(&ii[i].bdaddr, dst);
+
+ if (use_sdp) {
+ syslog(LOG_INFO, "Searching for %s on %s",
+ bnep_svc2str(service), dst);
+
+ if (bnep_sdp_search(&src_addr, &ii[i].bdaddr, service) <= 0)
+ continue;
+ }
+
+ r = create_connection(dst, &ii[i].bdaddr);
+ if (r < 0) {
+ terminate = 1;
+ break;
+ }
+ }
+ bt_free(ii);
+ } while (!terminate && persist);
+
+ return r;
+}
+
+static void do_show(void)
+{
+ bnep_show_connections();
+}
+
+static void do_kill(char *dst)
+{
+ if (dst) {
+ bdaddr_t *ba = strtoba(dst);
+ bnep_kill_connection((void *) ba);
+ free(ba);
+ } else {
+ bnep_kill_all_connections();
+ }
+}
+
+static void sig_hup(int sig)
+{
+ return;
+}
+
+static void sig_term(int sig)
+{
+ terminate = 1;
+}
+
+static int write_pidfile(void)
+{
+ int fd;
+ FILE *f;
+ pid_t pid;
+
+ do {
+ fd = open(pidfile, O_WRONLY|O_TRUNC|O_CREAT|O_EXCL, 0644);
+ if (fd == -1) {
+ /* Try to open the file for read. */
+ fd = open(pidfile, O_RDONLY);
+ if (fd < 0) {
+ syslog(LOG_ERR, "Could not read old pidfile: %s(%d)",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ /* We're already running; send a SIGHUP (we presume that they
+ * are calling ifup for a reason, so they probably want to
+ * rescan) and then exit cleanly and let things go on in the
+ * background. Muck with the filename so that we don't go
+ * deleting the pid file for the already-running instance.
+ */
+ f = fdopen(fd, "r");
+ if (!f) {
+ syslog(LOG_ERR, "Could not fdopen old pidfile: %s(%d)",
+ strerror(errno), errno);
+ close(fd);
+ return -1;
+ }
+
+ pid = 0;
+ if (fscanf(f, "%d", &pid) != 1)
+ pid = 0;
+ fclose(f);
+
+ if (pid) {
+ /* Try to kill it. */
+ if (kill(pid, SIGHUP) == -1) {
+ /* No such pid; remove the bogus pid file. */
+ syslog(LOG_INFO, "Removing stale pidfile");
+ unlink(pidfile);
+ fd = -1;
+ } else {
+ /* Got it. Don't mess with the pid file on
+ * our way out. */
+ syslog(LOG_INFO, "Signalling existing process %d and exiting\n", pid);
+ pidfile = NULL;
+ return -1;
+ }
+ }
+ }
+ } while(fd == -1);
+
+ f = fdopen(fd, "w");
+ if (!f) {
+ syslog(LOG_ERR, "Could not fdopen new pidfile: %s(%d)",
+ strerror(errno), errno);
+ close(fd);
+ unlink(pidfile);
+ return -1;
+ }
+
+ fprintf(f, "%d\n", getpid());
+ fclose(f);
+
+ return 0;
+}
+
+static struct option main_lopts[] = {
+ { "help", 0, 0, 'h' },
+ { "listen", 0, 0, 's' },
+ { "connect", 1, 0, 'c' },
+ { "search", 2, 0, 'Q' },
+ { "kill", 1, 0, 'k' },
+ { "killall", 0, 0, 'K' },
+ { "role", 1, 0, 'r' },
+ { "service", 1, 0, 'd' },
+ { "ethernet", 1, 0, 'e' },
+ { "device", 1, 0, 'i' },
+ { "nosdp", 0, 0, 'D' },
+ { "list", 0, 0, 'l' },
+ { "show", 0, 0, 'l' },
+ { "nodetach", 0, 0, 'n' },
+ { "persist", 2, 0, 'p' },
+ { "auth", 0, 0, 'A' },
+ { "encrypt", 0, 0, 'E' },
+ { "secure", 0, 0, 'S' },
+ { "master", 0, 0, 'M' },
+ { "cache", 0, 0, 'C' },
+ { "pidfile", 1, 0, 'P' },
+ { "devup", 1, 0, 'u' },
+ { "devdown", 1, 0, 'o' },
+ { "autozap", 0, 0, 'z' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *main_sopts = "hsc:k:Kr:d:e:i:lnp::DQ::AESMC::P:u:o:z";
+
+static const char *main_help =
+ "Bluetooth PAN daemon version %s\n"
+ "Usage:\n"
+ "\tpand <options>\n"
+ "Options:\n"
+ "\t--show --list -l Show active PAN connections\n"
+ "\t--listen -s Listen for PAN connections\n"
+ "\t--connect -c <bdaddr> Create PAN connection\n"
+ "\t--autozap -z Disconnect automatically on exit\n"
+ "\t--search -Q[duration] Search and connect\n"
+ "\t--kill -k <bdaddr> Kill PAN connection\n"
+ "\t--killall -K Kill all PAN connections\n"
+ "\t--role -r <role> Local PAN role (PANU, NAP, GN)\n"
+ "\t--service -d <role> Remote PAN service (PANU, NAP, GN)\n"
+ "\t--ethernet -e <name> Network interface name\n"
+ "\t--device -i <bdaddr> Source bdaddr\n"
+ "\t--nosdp -D Disable SDP\n"
+ "\t--auth -A Enable authentication\n"
+ "\t--encrypt -E Enable encryption\n"
+ "\t--secure -S Secure connection\n"
+ "\t--master -M Become the master of a piconet\n"
+ "\t--nodetach -n Do not become a daemon\n"
+ "\t--persist -p[interval] Persist mode\n"
+ "\t--cache -C[valid] Cache addresses\n"
+ "\t--pidfile -P <pidfile> Create PID file\n"
+ "\t--devup -u <script> Script to run when interface comes up\n"
+ "\t--devdown -o <script> Script to run when interface comes down\n";
+
+int main(int argc, char *argv[])
+{
+ char *dst = NULL, *src = NULL;
+ struct sigaction sa;
+ int mode = NONE;
+ int opt;
+
+ while ((opt=getopt_long(argc, argv, main_sopts, main_lopts, NULL)) != -1) {
+ switch(opt) {
+ case 'l':
+ mode = SHOW;
+ detach = 0;
+ break;
+
+ case 's':
+ mode = LISTEN;
+ break;
+
+ case 'c':
+ mode = CONNECT;
+ dst = strdup(optarg);
+ break;
+
+ case 'Q':
+ mode = CONNECT;
+ if (optarg)
+ search_duration = atoi(optarg);
+ break;
+
+ case 'k':
+ mode = KILL;
+ detach = 0;
+ dst = strdup(optarg);
+ break;
+
+ case 'K':
+ mode = KILL;
+ detach = 0;
+ break;
+
+ case 'i':
+ src = strdup(optarg);
+ break;
+
+ case 'r':
+ bnep_str2svc(optarg, &role);
+ break;
+
+ case 'd':
+ bnep_str2svc(optarg, &service);
+ break;
+
+ case 'D':
+ use_sdp = 0;
+ break;
+
+ case 'A':
+ link_mode |= L2CAP_LM_AUTH;
+ break;
+
+ case 'E':
+ link_mode |= L2CAP_LM_ENCRYPT;
+ break;
+
+ case 'S':
+ link_mode |= L2CAP_LM_SECURE;
+ break;
+
+ case 'M':
+ link_mode |= L2CAP_LM_MASTER;
+ break;
+
+ case 'e':
+ strncpy(netdev, optarg, 16);
+ netdev[15] = '\0';
+ break;
+
+ case 'n':
+ detach = 0;
+ break;
+
+ case 'p':
+ if (optarg)
+ persist = atoi(optarg);
+ else
+ persist = 5;
+ break;
+
+ case 'C':
+ if (optarg)
+ use_cache = atoi(optarg);
+ else
+ use_cache = 2;
+ break;
+
+ case 'P':
+ pidfile = strdup(optarg);
+ break;
+
+ case 'u':
+ devupcmd = strdup(optarg);
+ break;
+
+ case 'o':
+ devdowncmd = strdup(optarg);
+ break;
+
+ case 'z':
+ cleanup = 1;
+ break;
+
+ case 'h':
+ default:
+ printf(main_help, VERSION);
+ exit(0);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ if (bnep_init()) {
+ free(dst);
+ return -1;
+ }
+
+ /* Check non daemon modes first */
+ switch (mode) {
+ case SHOW:
+ do_show();
+ free(dst);
+ return 0;
+
+ case KILL:
+ do_kill(dst);
+ free(dst);
+ return 0;
+
+ case NONE:
+ printf(main_help, VERSION);
+ free(dst);
+ return 0;
+ }
+
+ /* Initialize signals */
+ 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_hup;
+ sigaction(SIGHUP, &sa, NULL);
+
+ sa.sa_handler = sig_term;
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+
+ if (detach && daemon(0, 0)) {
+ perror("Can't start daemon");
+ exit(1);
+ }
+
+ openlog("pand", LOG_PID | LOG_NDELAY | LOG_PERROR, LOG_DAEMON);
+ syslog(LOG_INFO, "Bluetooth PAN daemon version %s", VERSION);
+
+ if (src) {
+ src_dev = hci_devid(src);
+ if (src_dev < 0 || hci_devba(src_dev, &src_addr) < 0) {
+ syslog(LOG_ERR, "Invalid source. %s(%d)",
+ strerror(errno), errno);
+ free(dst);
+ return -1;
+ }
+ }
+
+ if (pidfile && write_pidfile()) {
+ free(dst);
+ return -1;
+ }
+
+ if (dst) {
+ /* Disable cache invalidation */
+ use_cache = 0;
+
+ strncpy(cache.dst, dst, sizeof(cache.dst) - 1);
+ str2ba(dst, &cache.bdaddr);
+ cache.valid = 1;
+ free(dst);
+ }
+
+ switch (mode) {
+ case CONNECT:
+ do_connect();
+ break;
+
+ case LISTEN:
+ do_listen();
+ break;
+ }
+
+ if (pidfile)
+ unlink(pidfile);
+
+ return 0;
+}
diff --git a/compat/pand.h b/compat/pand.h
new file mode 100644
index 0000000..08b5bdc
--- /dev/null
+++ b/compat/pand.h
@@ -0,0 +1,36 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 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
+ *
+ */
+
+int bnep_init(void);
+int bnep_cleanup(void);
+
+int bnep_str2svc(char *svc, uint16_t *uuid);
+char *bnep_svc2str(uint16_t uuid);
+
+int bnep_show_connections(void);
+int bnep_kill_connection(uint8_t *dst);
+int bnep_kill_all_connections(void);
+
+int bnep_accept_connection(int sk, uint16_t role, char *dev);
+int bnep_create_connection(int sk, uint16_t role, uint16_t svc, char *dev);
diff --git a/compat/sdp.c b/compat/sdp.c
new file mode 100644
index 0000000..f384844
--- /dev/null
+++ b/compat/sdp.c
@@ -0,0 +1,720 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2003-2010 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 <string.h>
+#include <syslog.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+#include <bluetooth/hidp.h>
+#include <bluetooth/bnep.h>
+
+#include "textfile.h"
+#include "sdp.h"
+
+static sdp_record_t *record = NULL;
+static sdp_session_t *session = NULL;
+
+static void add_lang_attr(sdp_record_t *r)
+{
+ sdp_lang_attr_t base_lang;
+ sdp_list_t *langs = 0;
+
+ /* UTF-8 MIBenum (http://www.iana.org/assignments/character-sets) */
+ base_lang.code_ISO639 = (0x65 << 8) | 0x6e;
+ base_lang.encoding = 106;
+ base_lang.base_offset = SDP_PRIMARY_LANG_BASE;
+ langs = sdp_list_append(0, &base_lang);
+ sdp_set_lang_attr(r, langs);
+ sdp_list_free(langs, 0);
+}
+
+static void epox_endian_quirk(unsigned char *data, int size)
+{
+ /* USAGE_PAGE (Keyboard) 05 07
+ * USAGE_MINIMUM (0) 19 00
+ * USAGE_MAXIMUM (65280) 2A 00 FF <= must be FF 00
+ * LOGICAL_MINIMUM (0) 15 00
+ * LOGICAL_MAXIMUM (65280) 26 00 FF <= must be FF 00
+ */
+ unsigned char pattern[] = { 0x05, 0x07, 0x19, 0x00, 0x2a, 0x00, 0xff,
+ 0x15, 0x00, 0x26, 0x00, 0xff };
+ unsigned int i;
+
+ if (!data)
+ return;
+
+ for (i = 0; i < size - sizeof(pattern); i++) {
+ if (!memcmp(data + i, pattern, sizeof(pattern))) {
+ data[i + 5] = 0xff;
+ data[i + 6] = 0x00;
+ data[i + 10] = 0xff;
+ data[i + 11] = 0x00;
+ }
+ }
+}
+
+static int store_device_info(const bdaddr_t *src, const bdaddr_t *dst, struct hidp_connadd_req *req)
+{
+ char filename[PATH_MAX + 1], addr[18], *str, *desc;
+ int i, err, size;
+
+ ba2str(src, addr);
+ create_name(filename, PATH_MAX, STORAGEDIR, addr, "hidd");
+
+ size = 15 + 3 + 3 + 5 + (req->rd_size * 2) + 1 + 9 + strlen(req->name) + 2;
+ str = malloc(size);
+ if (!str)
+ return -ENOMEM;
+
+ desc = malloc((req->rd_size * 2) + 1);
+ if (!desc) {
+ free(str);
+ return -ENOMEM;
+ }
+
+ memset(desc, 0, (req->rd_size * 2) + 1);
+ for (i = 0; i < req->rd_size; i++)
+ sprintf(desc + (i * 2), "%2.2X", req->rd_data[i]);
+
+ snprintf(str, size - 1, "%04X:%04X:%04X %02X %02X %04X %s %08X %s",
+ req->vendor, req->product, req->version,
+ req->subclass, req->country, req->parser, desc,
+ req->flags, req->name);
+
+ free(desc);
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ ba2str(dst, addr);
+ err = textfile_put(filename, addr, str);
+
+ free(str);
+
+ return err;
+}
+
+int get_stored_device_info(const bdaddr_t *src, const bdaddr_t *dst, struct hidp_connadd_req *req)
+{
+ char filename[PATH_MAX + 1], addr[18], tmp[3], *str, *desc;
+ unsigned int vendor, product, version, subclass, country, parser, pos;
+ int i;
+
+ desc = malloc(4096);
+ if (!desc)
+ return -ENOMEM;
+
+ memset(desc, 0, 4096);
+
+ ba2str(src, addr);
+ create_name(filename, PATH_MAX, STORAGEDIR, addr, "hidd");
+
+ ba2str(dst, addr);
+ str = textfile_get(filename, addr);
+ if (!str) {
+ free(desc);
+ return -EIO;
+ }
+
+ sscanf(str, "%04X:%04X:%04X %02X %02X %04X %4095s %08X %n",
+ &vendor, &product, &version, &subclass, &country,
+ &parser, desc, &req->flags, &pos);
+
+
+ req->vendor = vendor;
+ req->product = product;
+ req->version = version;
+ req->subclass = subclass;
+ req->country = country;
+ req->parser = parser;
+
+ snprintf(req->name, 128, "%s", str + pos);
+
+ free(str);
+ req->rd_size = strlen(desc) / 2;
+ req->rd_data = malloc(req->rd_size);
+ if (!req->rd_data) {
+ free(desc);
+ return -ENOMEM;
+ }
+
+ memset(tmp, 0, sizeof(tmp));
+ for (i = 0; i < req->rd_size; i++) {
+ memcpy(tmp, desc + (i * 2), 2);
+ req->rd_data[i] = (uint8_t) strtol(tmp, NULL, 16);
+ }
+
+ free(desc);
+
+ return 0;
+}
+
+int get_sdp_device_info(const bdaddr_t *src, const bdaddr_t *dst, struct hidp_connadd_req *req)
+{
+ struct sockaddr_l2 addr;
+ socklen_t addrlen;
+ bdaddr_t bdaddr;
+ uint32_t range = 0x0000ffff;
+ sdp_session_t *s;
+ sdp_list_t *search, *attrid, *pnp_rsp, *hid_rsp;
+ sdp_record_t *rec;
+ sdp_data_t *pdlist, *pdlist2;
+ uuid_t svclass;
+ int err;
+
+ s = sdp_connect(src, dst, SDP_RETRY_IF_BUSY | SDP_WAIT_ON_CLOSE);
+ if (!s)
+ return -1;
+
+ sdp_uuid16_create(&svclass, PNP_INFO_SVCLASS_ID);
+ search = sdp_list_append(NULL, &svclass);
+ attrid = sdp_list_append(NULL, &range);
+
+ err = sdp_service_search_attr_req(s, search,
+ SDP_ATTR_REQ_RANGE, attrid, &pnp_rsp);
+
+ sdp_list_free(search, NULL);
+ sdp_list_free(attrid, NULL);
+
+ sdp_uuid16_create(&svclass, HID_SVCLASS_ID);
+ search = sdp_list_append(NULL, &svclass);
+ attrid = sdp_list_append(NULL, &range);
+
+ err = sdp_service_search_attr_req(s, search,
+ SDP_ATTR_REQ_RANGE, attrid, &hid_rsp);
+
+ sdp_list_free(search, NULL);
+ sdp_list_free(attrid, NULL);
+
+ memset(&addr, 0, sizeof(addr));
+ addrlen = sizeof(addr);
+
+ if (getsockname(s->sock, (struct sockaddr *) &addr, &addrlen) < 0)
+ bacpy(&bdaddr, src);
+ else
+ bacpy(&bdaddr, &addr.l2_bdaddr);
+
+ sdp_close(s);
+
+ if (err || !hid_rsp)
+ return -1;
+
+ if (pnp_rsp) {
+ rec = (sdp_record_t *) pnp_rsp->data;
+
+ pdlist = sdp_data_get(rec, 0x0201);
+ req->vendor = pdlist ? pdlist->val.uint16 : 0x0000;
+
+ pdlist = sdp_data_get(rec, 0x0202);
+ req->product = pdlist ? pdlist->val.uint16 : 0x0000;
+
+ pdlist = sdp_data_get(rec, 0x0203);
+ req->version = pdlist ? pdlist->val.uint16 : 0x0000;
+
+ sdp_record_free(rec);
+ }
+
+ rec = (sdp_record_t *) hid_rsp->data;
+
+ pdlist2 = sdp_data_get(rec, 0x0100);
+ if (pdlist2)
+ strncpy(req->name, pdlist2->val.str, sizeof(req->name) - 1);
+ else {
+ pdlist = sdp_data_get(rec, 0x0101);
+ pdlist2 = sdp_data_get(rec, 0x0102);
+ if (pdlist) {
+ if (pdlist2) {
+ if (strncmp(pdlist->val.str, pdlist2->val.str, 5)) {
+ strncpy(req->name, pdlist2->val.str, sizeof(req->name) - 1);
+ strcat(req->name, " ");
+ }
+ strncat(req->name, pdlist->val.str,
+ sizeof(req->name) - strlen(req->name));
+ } else
+ strncpy(req->name, pdlist->val.str, sizeof(req->name) - 1);
+ }
+ }
+
+ pdlist = sdp_data_get(rec, 0x0201);
+ req->parser = pdlist ? pdlist->val.uint16 : 0x0100;
+
+ pdlist = sdp_data_get(rec, 0x0202);
+ req->subclass = pdlist ? pdlist->val.uint8 : 0;
+
+ pdlist = sdp_data_get(rec, 0x0203);
+ req->country = pdlist ? pdlist->val.uint8 : 0;
+
+ pdlist = sdp_data_get(rec, 0x0206);
+ if (pdlist) {
+ pdlist = pdlist->val.dataseq;
+ pdlist = pdlist->val.dataseq;
+ pdlist = pdlist->next;
+
+ req->rd_data = malloc(pdlist->unitSize);
+ if (req->rd_data) {
+ memcpy(req->rd_data, (unsigned char *) pdlist->val.str, pdlist->unitSize);
+ req->rd_size = pdlist->unitSize;
+ epox_endian_quirk(req->rd_data, req->rd_size);
+ }
+ }
+
+ sdp_record_free(rec);
+
+ if (bacmp(&bdaddr, BDADDR_ANY))
+ store_device_info(&bdaddr, dst, req);
+
+ return 0;
+}
+
+int get_alternate_device_info(const bdaddr_t *src, const bdaddr_t *dst, uint16_t *uuid, uint8_t *channel, char *name, size_t len)
+{
+ uint16_t attr1 = SDP_ATTR_PROTO_DESC_LIST;
+ uint16_t attr2 = SDP_ATTR_SVCNAME_PRIMARY;
+ sdp_session_t *s;
+ sdp_list_t *search, *attrid, *rsp;
+ uuid_t svclass;
+ int err;
+
+ s = sdp_connect(src, dst, SDP_RETRY_IF_BUSY | SDP_WAIT_ON_CLOSE);
+ if (!s)
+ return -1;
+
+ sdp_uuid16_create(&svclass, HEADSET_SVCLASS_ID);
+ search = sdp_list_append(NULL, &svclass);
+ attrid = sdp_list_append(NULL, &attr1);
+ attrid = sdp_list_append(attrid, &attr2);
+
+ err = sdp_service_search_attr_req(s, search,
+ SDP_ATTR_REQ_INDIVIDUAL, attrid, &rsp);
+
+ sdp_list_free(search, NULL);
+ sdp_list_free(attrid, NULL);
+
+ if (err <= 0) {
+ sdp_uuid16_create(&svclass, SERIAL_PORT_SVCLASS_ID);
+ search = sdp_list_append(NULL, &svclass);
+ attrid = sdp_list_append(NULL, &attr1);
+ attrid = sdp_list_append(attrid, &attr2);
+
+ err = sdp_service_search_attr_req(s, search,
+ SDP_ATTR_REQ_INDIVIDUAL, attrid, &rsp);
+
+ sdp_list_free(search, NULL);
+ sdp_list_free(attrid, NULL);
+
+ if (err < 0) {
+ sdp_close(s);
+ return err;
+ }
+
+ if (uuid)
+ *uuid = SERIAL_PORT_SVCLASS_ID;
+ } else {
+ if (uuid)
+ *uuid = HEADSET_SVCLASS_ID;
+ }
+
+ sdp_close(s);
+
+ for (; rsp; rsp = rsp->next) {
+ sdp_record_t *rec = (sdp_record_t *) rsp->data;
+ sdp_list_t *protos;
+
+ sdp_get_service_name(rec, name, len);
+
+ if (!sdp_get_access_protos(rec, &protos)) {
+ uint8_t ch = sdp_get_proto_port(protos, RFCOMM_UUID);
+ if (ch > 0) {
+ if (channel)
+ *channel = ch;
+ return 0;
+ }
+ }
+
+ sdp_record_free(rec);
+ }
+
+ return -EIO;
+}
+
+void bnep_sdp_unregister(void)
+{
+ if (record && sdp_record_unregister(session, record))
+ syslog(LOG_ERR, "Service record unregistration failed.");
+
+ sdp_close(session);
+}
+
+int bnep_sdp_register(bdaddr_t *device, uint16_t role)
+{
+ sdp_list_t *svclass, *pfseq, *apseq, *root, *aproto;
+ uuid_t root_uuid, pan, l2cap, bnep;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *proto[2];
+ sdp_data_t *v, *p;
+ uint16_t psm = 15, version = 0x0100;
+ uint16_t security_desc = 0;
+ uint16_t net_access_type = 0xfffe;
+ uint32_t max_net_access_rate = 0;
+ char *name = "BlueZ PAN";
+ char *desc = "BlueZ PAN Service";
+ int status;
+
+ session = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, 0);
+ if (!session) {
+ syslog(LOG_ERR, "Failed to connect to the local SDP server. %s(%d)",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ record = sdp_record_alloc();
+ if (!record) {
+ syslog(LOG_ERR, "Failed to allocate service record %s(%d)",
+ strerror(errno), errno);
+ sdp_close(session);
+ return -1;
+ }
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(record, root);
+ sdp_list_free(root, 0);
+
+ sdp_uuid16_create(&l2cap, L2CAP_UUID);
+ proto[0] = sdp_list_append(NULL, &l2cap);
+ p = sdp_data_alloc(SDP_UINT16, &psm);
+ proto[0] = sdp_list_append(proto[0], p);
+ apseq = sdp_list_append(NULL, proto[0]);
+
+ sdp_uuid16_create(&bnep, BNEP_UUID);
+ proto[1] = sdp_list_append(NULL, &bnep);
+ v = sdp_data_alloc(SDP_UINT16, &version);
+ proto[1] = sdp_list_append(proto[1], v);
+
+ /* Supported protocols */
+ {
+ uint16_t ptype[4] = {
+ 0x0800, /* IPv4 */
+ 0x0806, /* ARP */
+ };
+ sdp_data_t *head, *pseq;
+ int p;
+
+ for (p = 0, head = NULL; p < 2; p++) {
+ sdp_data_t *data = sdp_data_alloc(SDP_UINT16, &ptype[p]);
+ if (head)
+ sdp_seq_append(head, data);
+ else
+ head = data;
+ }
+ pseq = sdp_data_alloc(SDP_SEQ16, head);
+ proto[1] = sdp_list_append(proto[1], pseq);
+ }
+
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(NULL, apseq);
+ sdp_set_access_protos(record, aproto);
+
+ add_lang_attr(record);
+
+ sdp_list_free(proto[0], NULL);
+ sdp_list_free(proto[1], NULL);
+ sdp_list_free(apseq, NULL);
+ sdp_list_free(aproto, NULL);
+ sdp_data_free(p);
+ sdp_data_free(v);
+ sdp_attr_add_new(record, SDP_ATTR_SECURITY_DESC, SDP_UINT16, &security_desc);
+
+ switch (role) {
+ case BNEP_SVC_NAP:
+ sdp_uuid16_create(&pan, NAP_SVCLASS_ID);
+ svclass = sdp_list_append(NULL, &pan);
+ sdp_set_service_classes(record, svclass);
+
+ sdp_uuid16_create(&profile[0].uuid, NAP_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(NULL, &profile[0]);
+ sdp_set_profile_descs(record, pfseq);
+
+ sdp_set_info_attr(record, "Network Access Point", name, desc);
+
+ sdp_attr_add_new(record, SDP_ATTR_NET_ACCESS_TYPE, SDP_UINT16, &net_access_type);
+ sdp_attr_add_new(record, SDP_ATTR_MAX_NET_ACCESSRATE, SDP_UINT32, &max_net_access_rate);
+ break;
+
+ case BNEP_SVC_GN:
+ sdp_uuid16_create(&pan, GN_SVCLASS_ID);
+ svclass = sdp_list_append(NULL, &pan);
+ sdp_set_service_classes(record, svclass);
+
+ sdp_uuid16_create(&profile[0].uuid, GN_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(NULL, &profile[0]);
+ sdp_set_profile_descs(record, pfseq);
+
+ sdp_set_info_attr(record, "Group Network Service", name, desc);
+ break;
+
+ case BNEP_SVC_PANU:
+ sdp_uuid16_create(&pan, PANU_SVCLASS_ID);
+ svclass = sdp_list_append(NULL, &pan);
+ sdp_set_service_classes(record, svclass);
+ sdp_list_free(svclass, 0);
+
+ sdp_uuid16_create(&profile[0].uuid, PANU_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(NULL, &profile[0]);
+ sdp_set_profile_descs(record, pfseq);
+ sdp_list_free(pfseq, 0);
+
+ sdp_set_info_attr(record, "PAN User", name, desc);
+ break;
+ }
+
+ status = sdp_device_record_register(session, device, record, 0);
+ if (status) {
+ syslog(LOG_ERR, "SDP registration failed.");
+ sdp_record_free(record); record = NULL;
+ sdp_close(session);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Search for PAN service.
+ * Returns 1 if service is found and 0 otherwise. */
+int bnep_sdp_search(bdaddr_t *src, bdaddr_t *dst, uint16_t service)
+{
+ sdp_list_t *srch, *rsp = NULL;
+ sdp_session_t *s;
+ uuid_t svclass;
+ int err;
+
+ switch (service) {
+ case BNEP_SVC_PANU:
+ sdp_uuid16_create(&svclass, PANU_SVCLASS_ID);
+ break;
+ case BNEP_SVC_NAP:
+ sdp_uuid16_create(&svclass, NAP_SVCLASS_ID);
+ break;
+ case BNEP_SVC_GN:
+ sdp_uuid16_create(&svclass, GN_SVCLASS_ID);
+ break;
+ }
+
+ srch = sdp_list_append(NULL, &svclass);
+
+ s = sdp_connect(src, dst, 0);
+ if (!s) {
+ syslog(LOG_ERR, "Failed to connect to the SDP server. %s(%d)",
+ strerror(errno), errno);
+ return 0;
+ }
+
+ err = sdp_service_search_req(s, srch, 1, &rsp);
+ sdp_close(s);
+
+ /* Assume that search is successeful
+ * if at least one record is found */
+ if (!err && sdp_list_len(rsp))
+ return 1;
+
+ return 0;
+}
+
+static unsigned char async_uuid[] = { 0x03, 0x50, 0x27, 0x8F, 0x3D, 0xCA, 0x4E, 0x62,
+ 0x83, 0x1D, 0xA4, 0x11, 0x65, 0xFF, 0x90, 0x6C };
+
+void dun_sdp_unregister(void)
+{
+ if (record && sdp_record_unregister(session, record))
+ syslog(LOG_ERR, "Service record unregistration failed.");
+ sdp_close(session);
+}
+
+int dun_sdp_register(bdaddr_t *device, uint8_t channel, int type)
+{
+ sdp_list_t *svclass, *pfseq, *apseq, *root, *aproto;
+ uuid_t root_uuid, l2cap, rfcomm, dun;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *proto[2];
+ int status;
+
+ session = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, 0);
+ if (!session) {
+ syslog(LOG_ERR, "Failed to connect to the local SDP server. %s(%d)",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ record = sdp_record_alloc();
+ if (!record) {
+ syslog(LOG_ERR, "Failed to alloc service record");
+ return -1;
+ }
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(record, root);
+
+ sdp_uuid16_create(&l2cap, L2CAP_UUID);
+ proto[0] = sdp_list_append(NULL, &l2cap);
+ apseq = sdp_list_append(NULL, proto[0]);
+
+ sdp_uuid16_create(&rfcomm, RFCOMM_UUID);
+ proto[1] = sdp_list_append(NULL, &rfcomm);
+ proto[1] = sdp_list_append(proto[1], sdp_data_alloc(SDP_UINT8, &channel));
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(NULL, apseq);
+ sdp_set_access_protos(record, aproto);
+
+ switch (type) {
+ case MROUTER:
+ sdp_uuid16_create(&dun, SERIAL_PORT_SVCLASS_ID);
+ break;
+ case ACTIVESYNC:
+ sdp_uuid128_create(&dun, (void *) async_uuid);
+ break;
+ case DIALUP:
+ sdp_uuid16_create(&dun, DIALUP_NET_SVCLASS_ID);
+ break;
+ default:
+ sdp_uuid16_create(&dun, LAN_ACCESS_SVCLASS_ID);
+ break;
+ }
+
+ svclass = sdp_list_append(NULL, &dun);
+ sdp_set_service_classes(record, svclass);
+
+ switch (type) {
+ case LANACCESS:
+ sdp_uuid16_create(&profile[0].uuid, LAN_ACCESS_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(NULL, &profile[0]);
+ sdp_set_profile_descs(record, pfseq);
+ break;
+ case DIALUP:
+ sdp_uuid16_create(&profile[0].uuid, DIALUP_NET_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(NULL, &profile[0]);
+ sdp_set_profile_descs(record, pfseq);
+ break;
+ }
+
+ switch (type) {
+ case MROUTER:
+ sdp_set_info_attr(record, "mRouter", NULL, NULL);
+ break;
+ case ACTIVESYNC:
+ sdp_set_info_attr(record, "ActiveSync", NULL, NULL);
+ break;
+ case DIALUP:
+ sdp_set_info_attr(record, "Dialup Networking", NULL, NULL);
+ break;
+ default:
+ sdp_set_info_attr(record, "LAN Access Point", NULL, NULL);
+ break;
+ }
+
+ status = sdp_device_record_register(session, device, record, 0);
+ if (status) {
+ syslog(LOG_ERR, "SDP registration failed.");
+ sdp_record_free(record);
+ record = NULL;
+ return -1;
+ }
+ return 0;
+}
+
+int dun_sdp_search(bdaddr_t *src, bdaddr_t *dst, int *channel, int type)
+{
+ sdp_session_t *s;
+ sdp_list_t *srch, *attrs, *rsp;
+ uuid_t svclass;
+ uint16_t attr;
+ int err;
+
+ s = sdp_connect(src, dst, 0);
+ if (!s) {
+ syslog(LOG_ERR, "Failed to connect to the SDP server. %s(%d)",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ switch (type) {
+ case MROUTER:
+ sdp_uuid16_create(&svclass, SERIAL_PORT_SVCLASS_ID);
+ break;
+ case ACTIVESYNC:
+ sdp_uuid128_create(&svclass, (void *) async_uuid);
+ break;
+ case DIALUP:
+ sdp_uuid16_create(&svclass, DIALUP_NET_SVCLASS_ID);
+ break;
+ default:
+ sdp_uuid16_create(&svclass, LAN_ACCESS_SVCLASS_ID);
+ break;
+ }
+
+ srch = sdp_list_append(NULL, &svclass);
+
+ attr = SDP_ATTR_PROTO_DESC_LIST;
+ attrs = sdp_list_append(NULL, &attr);
+
+ err = sdp_service_search_attr_req(s, srch, SDP_ATTR_REQ_INDIVIDUAL, attrs, &rsp);
+
+ sdp_close(s);
+
+ if (err)
+ return 0;
+
+ for(; rsp; rsp = rsp->next) {
+ sdp_record_t *rec = (sdp_record_t *) rsp->data;
+ sdp_list_t *protos;
+
+ if (!sdp_get_access_protos(rec, &protos)) {
+ int ch = sdp_get_proto_port(protos, RFCOMM_UUID);
+ if (ch > 0) {
+ *channel = ch;
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/compat/sdp.h b/compat/sdp.h
new file mode 100644
index 0000000..6ca975f
--- /dev/null
+++ b/compat/sdp.h
@@ -0,0 +1,39 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2003-2010 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
+ *
+ */
+
+#define LANACCESS 0
+#define MROUTER 1
+#define ACTIVESYNC 2
+#define DIALUP 3
+
+int get_stored_device_info(const bdaddr_t *src, const bdaddr_t *dst, struct hidp_connadd_req *req);
+int get_sdp_device_info(const bdaddr_t *src, const bdaddr_t *dst, struct hidp_connadd_req *req);
+int get_alternate_device_info(const bdaddr_t *src, const bdaddr_t *dst, uint16_t *uuid, uint8_t *channel, char *name, size_t len);
+
+int bnep_sdp_search(bdaddr_t *src, bdaddr_t *dst, uint16_t service);
+int bnep_sdp_register(bdaddr_t *device, uint16_t role);
+void bnep_sdp_unregister(void);
+
+int dun_sdp_search(bdaddr_t *src, bdaddr_t *dst, int *channel, int type);
+int dun_sdp_register(bdaddr_t *device, uint8_t channel, int type);
+void dun_sdp_unregister(void);
diff --git a/compile b/compile
new file mode 100755
index 0000000..c0096a7
--- /dev/null
+++ b/compile
@@ -0,0 +1,143 @@
+#! /bin/sh
+# Wrapper for compilers which do not understand `-c -o'.
+
+scriptversion=2009-10-06.20; # UTC
+
+# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2009 Free Software
+# Foundation, Inc.
+# Written by Tom Tromey <tromey@cygnus.com>.
+#
+# 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, 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, see <http://www.gnu.org/licenses/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# This file is maintained in Automake, please report
+# bugs to <bug-automake@gnu.org> or send patches to
+# <automake-patches@gnu.org>.
+
+case $1 in
+ '')
+ echo "$0: No command. Try \`$0 --help' for more information." 1>&2
+ exit 1;
+ ;;
+ -h | --h*)
+ cat <<\EOF
+Usage: compile [--help] [--version] PROGRAM [ARGS]
+
+Wrapper for compilers which do not understand `-c -o'.
+Remove `-o dest.o' from ARGS, run PROGRAM with the remaining
+arguments, and rename the output as expected.
+
+If you are trying to build a whole package this is not the
+right script to run: please start by reading the file `INSTALL'.
+
+Report bugs to <bug-automake@gnu.org>.
+EOF
+ exit $?
+ ;;
+ -v | --v*)
+ echo "compile $scriptversion"
+ exit $?
+ ;;
+esac
+
+ofile=
+cfile=
+eat=
+
+for arg
+do
+ if test -n "$eat"; then
+ eat=
+ else
+ case $1 in
+ -o)
+ # configure might choose to run compile as `compile cc -o foo foo.c'.
+ # So we strip `-o arg' only if arg is an object.
+ eat=1
+ case $2 in
+ *.o | *.obj)
+ ofile=$2
+ ;;
+ *)
+ set x "$@" -o "$2"
+ shift
+ ;;
+ esac
+ ;;
+ *.c)
+ cfile=$1
+ set x "$@" "$1"
+ shift
+ ;;
+ *)
+ set x "$@" "$1"
+ shift
+ ;;
+ esac
+ fi
+ shift
+done
+
+if test -z "$ofile" || test -z "$cfile"; then
+ # If no `-o' option was seen then we might have been invoked from a
+ # pattern rule where we don't need one. That is ok -- this is a
+ # normal compilation that the losing compiler can handle. If no
+ # `.c' file was seen then we are probably linking. That is also
+ # ok.
+ exec "$@"
+fi
+
+# Name of file we expect compiler to create.
+cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'`
+
+# Create the lock directory.
+# Note: use `[/\\:.-]' here to ensure that we don't use the same name
+# that we are using for the .o file. Also, base the name on the expected
+# object file name, since that is what matters with a parallel build.
+lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d
+while true; do
+ if mkdir "$lockdir" >/dev/null 2>&1; then
+ break
+ fi
+ sleep 1
+done
+# FIXME: race condition here if user kills between mkdir and trap.
+trap "rmdir '$lockdir'; exit 1" 1 2 15
+
+# Run the compile.
+"$@"
+ret=$?
+
+if test -f "$cofile"; then
+ test "$cofile" = "$ofile" || mv "$cofile" "$ofile"
+elif test -f "${cofile}bj"; then
+ test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile"
+fi
+
+rmdir "$lockdir"
+exit $ret
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/config.guess b/config.guess
new file mode 100755
index 0000000..dc84c68
--- /dev/null
+++ b/config.guess
@@ -0,0 +1,1501 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+# Free Software Foundation, Inc.
+
+timestamp='2009-11-20'
+
+# This file 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 Street - Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+
+# Originally written by Per Bothner. Please send patches (context
+# diff format) to <config-patches@gnu.org> and include a ChangeLog
+# entry.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub. If it succeeds, it prints the system name on stdout, and
+# exits with 0. Otherwise, it exits with 1.
+#
+# You can get the latest version of this script from:
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit ;;
+ --version | -v )
+ echo "$version" ; exit ;;
+ --help | --h* | -h )
+ echo "$usage"; exit ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help" >&2
+ exit 1 ;;
+ * )
+ break ;;
+ esac
+done
+
+if test $# != 0; then
+ echo "$me: too many arguments$help" >&2
+ exit 1
+fi
+
+trap 'exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+set_cc_for_build='
+trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
+trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
+: ${TMPDIR=/tmp} ;
+ { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
+ { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
+dummy=$tmp/dummy ;
+tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
+case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,) echo "int x;" > $dummy.c ;
+ for c in cc gcc c89 c99 ; do
+ if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
+ CC_FOR_BUILD="$c"; break ;
+ fi ;
+ done ;
+ if test x"$CC_FOR_BUILD" = x ; then
+ CC_FOR_BUILD=no_compiler_found ;
+ fi
+ ;;
+ ,,*) CC_FOR_BUILD=$CC ;;
+ ,*,*) CC_FOR_BUILD=$HOST_CC ;;
+esac ; set_cc_for_build= ;'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+ PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+ *:NetBSD:*:*)
+ # NetBSD (nbsd) targets should (where applicable) match one or
+ # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
+ # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
+ # switched to ELF, *-*-netbsd* would select the old
+ # object file format. This provides both forward
+ # compatibility and a consistent mechanism for selecting the
+ # object file format.
+ #
+ # Note: NetBSD doesn't particularly care about the vendor
+ # portion of the name. We always set it to "unknown".
+ sysctl="sysctl -n hw.machine_arch"
+ UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
+ /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+ case "${UNAME_MACHINE_ARCH}" in
+ armeb) machine=armeb-unknown ;;
+ arm*) machine=arm-unknown ;;
+ sh3el) machine=shl-unknown ;;
+ sh3eb) machine=sh-unknown ;;
+ sh5el) machine=sh5le-unknown ;;
+ *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
+ esac
+ # The Operating System including object format, if it has switched
+ # to ELF recently, or will in the future.
+ case "${UNAME_MACHINE_ARCH}" in
+ arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+ eval $set_cc_for_build
+ if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ELF__
+ then
+ # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+ # Return netbsd for either. FIX?
+ os=netbsd
+ else
+ os=netbsdelf
+ fi
+ ;;
+ *)
+ os=netbsd
+ ;;
+ esac
+ # The OS release
+ # Debian GNU/NetBSD machines have a different userland, and
+ # thus, need a distinct triplet. However, they do not need
+ # kernel version information, so it can be replaced with a
+ # suitable tag, in the style of linux-gnu.
+ case "${UNAME_VERSION}" in
+ Debian*)
+ release='-gnu'
+ ;;
+ *)
+ release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ ;;
+ esac
+ # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+ # contains redundant information, the shorter form:
+ # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+ echo "${machine}-${os}${release}"
+ exit ;;
+ *:OpenBSD:*:*)
+ UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
+ echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
+ exit ;;
+ *:ekkoBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
+ exit ;;
+ *:SolidBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE}
+ exit ;;
+ macppc:MirBSD:*:*)
+ echo powerpc-unknown-mirbsd${UNAME_RELEASE}
+ exit ;;
+ *:MirBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
+ exit ;;
+ alpha:OSF1:*:*)
+ case $UNAME_RELEASE in
+ *4.0)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+ ;;
+ *5.*)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+ ;;
+ esac
+ # According to Compaq, /usr/sbin/psrinfo has been available on
+ # OSF/1 and Tru64 systems produced since 1995. I hope that
+ # covers most systems running today. This code pipes the CPU
+ # types through head -n 1, so we only detect the type of CPU 0.
+ ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+ case "$ALPHA_CPU_TYPE" in
+ "EV4 (21064)")
+ UNAME_MACHINE="alpha" ;;
+ "EV4.5 (21064)")
+ UNAME_MACHINE="alpha" ;;
+ "LCA4 (21066/21068)")
+ UNAME_MACHINE="alpha" ;;
+ "EV5 (21164)")
+ UNAME_MACHINE="alphaev5" ;;
+ "EV5.6 (21164A)")
+ UNAME_MACHINE="alphaev56" ;;
+ "EV5.6 (21164PC)")
+ UNAME_MACHINE="alphapca56" ;;
+ "EV5.7 (21164PC)")
+ UNAME_MACHINE="alphapca57" ;;
+ "EV6 (21264)")
+ UNAME_MACHINE="alphaev6" ;;
+ "EV6.7 (21264A)")
+ UNAME_MACHINE="alphaev67" ;;
+ "EV6.8CB (21264C)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.8AL (21264B)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.8CX (21264D)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.9A (21264/EV69A)")
+ UNAME_MACHINE="alphaev69" ;;
+ "EV7 (21364)")
+ UNAME_MACHINE="alphaev7" ;;
+ "EV7.9 (21364A)")
+ UNAME_MACHINE="alphaev79" ;;
+ esac
+ # A Pn.n version is a patched version.
+ # A Vn.n version is a released version.
+ # A Tn.n version is a released field test version.
+ # A Xn.n version is an unreleased experimental baselevel.
+ # 1.2 uses "1.2" for uname -r.
+ echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ exit ;;
+ Alpha\ *:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # Should we change UNAME_MACHINE based on the output of uname instead
+ # of the specific Alpha model?
+ echo alpha-pc-interix
+ exit ;;
+ 21064:Windows_NT:50:3)
+ echo alpha-dec-winnt3.5
+ exit ;;
+ Amiga*:UNIX_System_V:4.0:*)
+ echo m68k-unknown-sysv4
+ exit ;;
+ *:[Aa]miga[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-amigaos
+ exit ;;
+ *:[Mm]orph[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-morphos
+ exit ;;
+ *:OS/390:*:*)
+ echo i370-ibm-openedition
+ exit ;;
+ *:z/VM:*:*)
+ echo s390-ibm-zvmoe
+ exit ;;
+ *:OS400:*:*)
+ echo powerpc-ibm-os400
+ exit ;;
+ arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+ echo arm-acorn-riscix${UNAME_RELEASE}
+ exit ;;
+ arm:riscos:*:*|arm:RISCOS:*:*)
+ echo arm-unknown-riscos
+ exit ;;
+ SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+ echo hppa1.1-hitachi-hiuxmpp
+ exit ;;
+ Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+ # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+ if test "`(/bin/universe) 2>/dev/null`" = att ; then
+ echo pyramid-pyramid-sysv3
+ else
+ echo pyramid-pyramid-bsd
+ fi
+ exit ;;
+ NILE*:*:*:dcosx)
+ echo pyramid-pyramid-svr4
+ exit ;;
+ DRS?6000:unix:4.0:6*)
+ echo sparc-icl-nx6
+ exit ;;
+ DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
+ case `/usr/bin/uname -p` in
+ sparc) echo sparc-icl-nx7; exit ;;
+ esac ;;
+ s390x:SunOS:*:*)
+ echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4H:SunOS:5.*:*)
+ echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+ echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
+ echo i386-pc-auroraux${UNAME_RELEASE}
+ exit ;;
+ i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
+ eval $set_cc_for_build
+ SUN_ARCH="i386"
+ # If there is a compiler, see if it is configured for 64-bit objects.
+ # Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
+ # This test works for both compilers.
+ if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+ if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ SUN_ARCH="x86_64"
+ fi
+ fi
+ echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4*:SunOS:6*:*)
+ # According to config.sub, this is the proper way to canonicalize
+ # SunOS6. Hard to guess exactly what SunOS6 will be like, but
+ # it's likely to be more like Solaris than SunOS4.
+ echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4*:SunOS:*:*)
+ case "`/usr/bin/arch -k`" in
+ Series*|S4*)
+ UNAME_RELEASE=`uname -v`
+ ;;
+ esac
+ # Japanese Language versions have a version number like `4.1.3-JL'.
+ echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+ exit ;;
+ sun3*:SunOS:*:*)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ exit ;;
+ sun*:*:4.2BSD:*)
+ UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+ test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+ case "`/bin/arch`" in
+ sun3)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ ;;
+ sun4)
+ echo sparc-sun-sunos${UNAME_RELEASE}
+ ;;
+ esac
+ exit ;;
+ aushp:SunOS:*:*)
+ echo sparc-auspex-sunos${UNAME_RELEASE}
+ exit ;;
+ # The situation for MiNT is a little confusing. The machine name
+ # can be virtually everything (everything which is not
+ # "atarist" or "atariste" at least should have a processor
+ # > m68000). The system name ranges from "MiNT" over "FreeMiNT"
+ # to the lowercase version "mint" (or "freemint"). Finally
+ # the system name "TOS" denotes a system which is actually not
+ # MiNT. But MiNT is downward compatible to TOS, so this should
+ # be no problem.
+ atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit ;;
+ atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit ;;
+ *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit ;;
+ milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+ echo m68k-milan-mint${UNAME_RELEASE}
+ exit ;;
+ hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+ echo m68k-hades-mint${UNAME_RELEASE}
+ exit ;;
+ *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+ echo m68k-unknown-mint${UNAME_RELEASE}
+ exit ;;
+ m68k:machten:*:*)
+ echo m68k-apple-machten${UNAME_RELEASE}
+ exit ;;
+ powerpc:machten:*:*)
+ echo powerpc-apple-machten${UNAME_RELEASE}
+ exit ;;
+ RISC*:Mach:*:*)
+ echo mips-dec-mach_bsd4.3
+ exit ;;
+ RISC*:ULTRIX:*:*)
+ echo mips-dec-ultrix${UNAME_RELEASE}
+ exit ;;
+ VAX*:ULTRIX*:*:*)
+ echo vax-dec-ultrix${UNAME_RELEASE}
+ exit ;;
+ 2020:CLIX:*:* | 2430:CLIX:*:*)
+ echo clipper-intergraph-clix${UNAME_RELEASE}
+ exit ;;
+ mips:*:*:UMIPS | mips:*:*:RISCos)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+#ifdef __cplusplus
+#include <stdio.h> /* for printf() prototype */
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+ #if defined (host_mips) && defined (MIPSEB)
+ #if defined (SYSTYPE_SYSV)
+ printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_SVR4)
+ printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+ printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+ #endif
+ #endif
+ exit (-1);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c &&
+ dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` &&
+ SYSTEM_NAME=`$dummy $dummyarg` &&
+ { echo "$SYSTEM_NAME"; exit; }
+ echo mips-mips-riscos${UNAME_RELEASE}
+ exit ;;
+ Motorola:PowerMAX_OS:*:*)
+ echo powerpc-motorola-powermax
+ exit ;;
+ Motorola:*:4.3:PL8-*)
+ echo powerpc-harris-powermax
+ exit ;;
+ Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+ echo powerpc-harris-powermax
+ exit ;;
+ Night_Hawk:Power_UNIX:*:*)
+ echo powerpc-harris-powerunix
+ exit ;;
+ m88k:CX/UX:7*:*)
+ echo m88k-harris-cxux7
+ exit ;;
+ m88k:*:4*:R4*)
+ echo m88k-motorola-sysv4
+ exit ;;
+ m88k:*:3*:R3*)
+ echo m88k-motorola-sysv3
+ exit ;;
+ AViiON:dgux:*:*)
+ # DG/UX returns AViiON for all architectures
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+ then
+ if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+ [ ${TARGET_BINARY_INTERFACE}x = x ]
+ then
+ echo m88k-dg-dgux${UNAME_RELEASE}
+ else
+ echo m88k-dg-dguxbcs${UNAME_RELEASE}
+ fi
+ else
+ echo i586-dg-dgux${UNAME_RELEASE}
+ fi
+ exit ;;
+ M88*:DolphinOS:*:*) # DolphinOS (SVR3)
+ echo m88k-dolphin-sysv3
+ exit ;;
+ M88*:*:R3*:*)
+ # Delta 88k system running SVR3
+ echo m88k-motorola-sysv3
+ exit ;;
+ XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+ echo m88k-tektronix-sysv3
+ exit ;;
+ Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+ echo m68k-tektronix-bsd
+ exit ;;
+ *:IRIX*:*:*)
+ echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+ exit ;;
+ ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+ echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
+ exit ;; # Note that: echo "'`uname -s`'" gives 'AIX '
+ i*86:AIX:*:*)
+ echo i386-ibm-aix
+ exit ;;
+ ia64:AIX:*:*)
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+ exit ;;
+ *:AIX:2:3)
+ if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <sys/systemcfg.h>
+
+ main()
+ {
+ if (!__power_pc())
+ exit(1);
+ puts("powerpc-ibm-aix3.2.5");
+ exit(0);
+ }
+EOF
+ if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy`
+ then
+ echo "$SYSTEM_NAME"
+ else
+ echo rs6000-ibm-aix3.2.5
+ fi
+ elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+ echo rs6000-ibm-aix3.2.4
+ else
+ echo rs6000-ibm-aix3.2
+ fi
+ exit ;;
+ *:AIX:*:[456])
+ IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+ if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+ IBM_ARCH=rs6000
+ else
+ IBM_ARCH=powerpc
+ fi
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+ exit ;;
+ *:AIX:*:*)
+ echo rs6000-ibm-aix
+ exit ;;
+ ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+ echo romp-ibm-bsd4.4
+ exit ;;
+ ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and
+ echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to
+ exit ;; # report: romp-ibm BSD 4.3
+ *:BOSX:*:*)
+ echo rs6000-bull-bosx
+ exit ;;
+ DPX/2?00:B.O.S.:*:*)
+ echo m68k-bull-sysv3
+ exit ;;
+ 9000/[34]??:4.3bsd:1.*:*)
+ echo m68k-hp-bsd
+ exit ;;
+ hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+ echo m68k-hp-bsd4.4
+ exit ;;
+ 9000/[34678]??:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ case "${UNAME_MACHINE}" in
+ 9000/31? ) HP_ARCH=m68000 ;;
+ 9000/[34]?? ) HP_ARCH=m68k ;;
+ 9000/[678][0-9][0-9])
+ if [ -x /usr/bin/getconf ]; then
+ sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+ sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+ case "${sc_cpu_version}" in
+ 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+ 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+ 532) # CPU_PA_RISC2_0
+ case "${sc_kernel_bits}" in
+ 32) HP_ARCH="hppa2.0n" ;;
+ 64) HP_ARCH="hppa2.0w" ;;
+ '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20
+ esac ;;
+ esac
+ fi
+ if [ "${HP_ARCH}" = "" ]; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+
+ #define _HPUX_SOURCE
+ #include <stdlib.h>
+ #include <unistd.h>
+
+ int main ()
+ {
+ #if defined(_SC_KERNEL_BITS)
+ long bits = sysconf(_SC_KERNEL_BITS);
+ #endif
+ long cpu = sysconf (_SC_CPU_VERSION);
+
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+ case CPU_PA_RISC2_0:
+ #if defined(_SC_KERNEL_BITS)
+ switch (bits)
+ {
+ case 64: puts ("hppa2.0w"); break;
+ case 32: puts ("hppa2.0n"); break;
+ default: puts ("hppa2.0"); break;
+ } break;
+ #else /* !defined(_SC_KERNEL_BITS) */
+ puts ("hppa2.0"); break;
+ #endif
+ default: puts ("hppa1.0"); break;
+ }
+ exit (0);
+ }
+EOF
+ (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
+ test -z "$HP_ARCH" && HP_ARCH=hppa
+ fi ;;
+ esac
+ if [ ${HP_ARCH} = "hppa2.0w" ]
+ then
+ eval $set_cc_for_build
+
+ # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
+ # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler
+ # generating 64-bit code. GNU and HP use different nomenclature:
+ #
+ # $ CC_FOR_BUILD=cc ./config.guess
+ # => hppa2.0w-hp-hpux11.23
+ # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
+ # => hppa64-hp-hpux11.23
+
+ if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
+ grep -q __LP64__
+ then
+ HP_ARCH="hppa2.0w"
+ else
+ HP_ARCH="hppa64"
+ fi
+ fi
+ echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+ exit ;;
+ ia64:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ echo ia64-hp-hpux${HPUX_REV}
+ exit ;;
+ 3050*:HI-UX:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <unistd.h>
+ int
+ main ()
+ {
+ long cpu = sysconf (_SC_CPU_VERSION);
+ /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+ true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
+ results, however. */
+ if (CPU_IS_PA_RISC (cpu))
+ {
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+ default: puts ("hppa-hitachi-hiuxwe2"); break;
+ }
+ }
+ else if (CPU_IS_HP_MC68K (cpu))
+ puts ("m68k-hitachi-hiuxwe2");
+ else puts ("unknown-hitachi-hiuxwe2");
+ exit (0);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` &&
+ { echo "$SYSTEM_NAME"; exit; }
+ echo unknown-hitachi-hiuxwe2
+ exit ;;
+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+ echo hppa1.1-hp-bsd
+ exit ;;
+ 9000/8??:4.3bsd:*:*)
+ echo hppa1.0-hp-bsd
+ exit ;;
+ *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+ echo hppa1.0-hp-mpeix
+ exit ;;
+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+ echo hppa1.1-hp-osf
+ exit ;;
+ hp8??:OSF1:*:*)
+ echo hppa1.0-hp-osf
+ exit ;;
+ i*86:OSF1:*:*)
+ if [ -x /usr/sbin/sysversion ] ; then
+ echo ${UNAME_MACHINE}-unknown-osf1mk
+ else
+ echo ${UNAME_MACHINE}-unknown-osf1
+ fi
+ exit ;;
+ parisc*:Lites*:*:*)
+ echo hppa1.1-hp-lites
+ exit ;;
+ C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+ echo c1-convex-bsd
+ exit ;;
+ C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit ;;
+ C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+ echo c34-convex-bsd
+ exit ;;
+ C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+ echo c38-convex-bsd
+ exit ;;
+ C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+ echo c4-convex-bsd
+ exit ;;
+ CRAY*Y-MP:*:*:*)
+ echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*[A-Z]90:*:*:*)
+ echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+ -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*TS:*:*:*)
+ echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*T3E:*:*:*)
+ echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*SV1:*:*:*)
+ echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ *:UNICOS/mp:*:*)
+ echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+ FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+ echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit ;;
+ 5000:UNIX_System_V:4.*:*)
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
+ echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit ;;
+ i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+ echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+ exit ;;
+ sparc*:BSD/OS:*:*)
+ echo sparc-unknown-bsdi${UNAME_RELEASE}
+ exit ;;
+ *:BSD/OS:*:*)
+ echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+ exit ;;
+ *:FreeBSD:*:*)
+ case ${UNAME_MACHINE} in
+ pc98)
+ echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ amd64)
+ echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ *)
+ echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ esac
+ exit ;;
+ i*:CYGWIN*:*)
+ echo ${UNAME_MACHINE}-pc-cygwin
+ exit ;;
+ *:MINGW*:*)
+ echo ${UNAME_MACHINE}-pc-mingw32
+ exit ;;
+ i*:windows32*:*)
+ # uname -m includes "-pc" on this system.
+ echo ${UNAME_MACHINE}-mingw32
+ exit ;;
+ i*:PW*:*)
+ echo ${UNAME_MACHINE}-pc-pw32
+ exit ;;
+ *:Interix*:*)
+ case ${UNAME_MACHINE} in
+ x86)
+ echo i586-pc-interix${UNAME_RELEASE}
+ exit ;;
+ authenticamd | genuineintel | EM64T)
+ echo x86_64-unknown-interix${UNAME_RELEASE}
+ exit ;;
+ IA64)
+ echo ia64-unknown-interix${UNAME_RELEASE}
+ exit ;;
+ esac ;;
+ [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
+ echo i${UNAME_MACHINE}-pc-mks
+ exit ;;
+ 8664:Windows_NT:*)
+ echo x86_64-pc-mks
+ exit ;;
+ i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+ # UNAME_MACHINE based on the output of uname instead of i386?
+ echo i586-pc-interix
+ exit ;;
+ i*:UWIN*:*)
+ echo ${UNAME_MACHINE}-pc-uwin
+ exit ;;
+ amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
+ echo x86_64-unknown-cygwin
+ exit ;;
+ p*:CYGWIN*:*)
+ echo powerpcle-unknown-cygwin
+ exit ;;
+ prep*:SunOS:5.*:*)
+ echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ *:GNU:*:*)
+ # the GNU system
+ echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+ exit ;;
+ *:GNU/*:*:*)
+ # other systems with GNU libc and userland
+ echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
+ exit ;;
+ i*86:Minix:*:*)
+ echo ${UNAME_MACHINE}-pc-minix
+ exit ;;
+ alpha:Linux:*:*)
+ case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+ EV5) UNAME_MACHINE=alphaev5 ;;
+ EV56) UNAME_MACHINE=alphaev56 ;;
+ PCA56) UNAME_MACHINE=alphapca56 ;;
+ PCA57) UNAME_MACHINE=alphapca56 ;;
+ EV6) UNAME_MACHINE=alphaev6 ;;
+ EV67) UNAME_MACHINE=alphaev67 ;;
+ EV68*) UNAME_MACHINE=alphaev68 ;;
+ esac
+ objdump --private-headers /bin/sh | grep -q ld.so.1
+ if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+ echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+ exit ;;
+ arm*:Linux:*:*)
+ eval $set_cc_for_build
+ if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_EABI__
+ then
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ else
+ echo ${UNAME_MACHINE}-unknown-linux-gnueabi
+ fi
+ exit ;;
+ avr32*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ cris:Linux:*:*)
+ echo cris-axis-linux-gnu
+ exit ;;
+ crisv32:Linux:*:*)
+ echo crisv32-axis-linux-gnu
+ exit ;;
+ frv:Linux:*:*)
+ echo frv-unknown-linux-gnu
+ exit ;;
+ i*86:Linux:*:*)
+ LIBC=gnu
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #ifdef __dietlibc__
+ LIBC=dietlibc
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'`
+ echo "${UNAME_MACHINE}-pc-linux-${LIBC}"
+ exit ;;
+ ia64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ m32r*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ m68*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ mips:Linux:*:* | mips64:Linux:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #undef CPU
+ #undef ${UNAME_MACHINE}
+ #undef ${UNAME_MACHINE}el
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ CPU=${UNAME_MACHINE}el
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ CPU=${UNAME_MACHINE}
+ #else
+ CPU=
+ #endif
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
+ test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
+ ;;
+ or32:Linux:*:*)
+ echo or32-unknown-linux-gnu
+ exit ;;
+ padre:Linux:*:*)
+ echo sparc-unknown-linux-gnu
+ exit ;;
+ parisc64:Linux:*:* | hppa64:Linux:*:*)
+ echo hppa64-unknown-linux-gnu
+ exit ;;
+ parisc:Linux:*:* | hppa:Linux:*:*)
+ # Look for CPU level
+ case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+ PA7*) echo hppa1.1-unknown-linux-gnu ;;
+ PA8*) echo hppa2.0-unknown-linux-gnu ;;
+ *) echo hppa-unknown-linux-gnu ;;
+ esac
+ exit ;;
+ ppc64:Linux:*:*)
+ echo powerpc64-unknown-linux-gnu
+ exit ;;
+ ppc:Linux:*:*)
+ echo powerpc-unknown-linux-gnu
+ exit ;;
+ s390:Linux:*:* | s390x:Linux:*:*)
+ echo ${UNAME_MACHINE}-ibm-linux
+ exit ;;
+ sh64*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ sh*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ sparc:Linux:*:* | sparc64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ vax:Linux:*:*)
+ echo ${UNAME_MACHINE}-dec-linux-gnu
+ exit ;;
+ x86_64:Linux:*:*)
+ echo x86_64-unknown-linux-gnu
+ exit ;;
+ xtensa*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ i*86:DYNIX/ptx:4*:*)
+ # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+ # earlier versions are messed up and put the nodename in both
+ # sysname and nodename.
+ echo i386-sequent-sysv4
+ exit ;;
+ i*86:UNIX_SV:4.2MP:2.*)
+ # Unixware is an offshoot of SVR4, but it has its own version
+ # number series starting with 2...
+ # I am not positive that other SVR4 systems won't match this,
+ # I just have to hope. -- rms.
+ # Use sysv4.2uw... so that sysv4* matches it.
+ echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+ exit ;;
+ i*86:OS/2:*:*)
+ # If we were able to find `uname', then EMX Unix compatibility
+ # is probably installed.
+ echo ${UNAME_MACHINE}-pc-os2-emx
+ exit ;;
+ i*86:XTS-300:*:STOP)
+ echo ${UNAME_MACHINE}-unknown-stop
+ exit ;;
+ i*86:atheos:*:*)
+ echo ${UNAME_MACHINE}-unknown-atheos
+ exit ;;
+ i*86:syllable:*:*)
+ echo ${UNAME_MACHINE}-pc-syllable
+ exit ;;
+ i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
+ echo i386-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ i*86:*DOS:*:*)
+ echo ${UNAME_MACHINE}-pc-msdosdjgpp
+ exit ;;
+ i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+ UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+ if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+ echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+ else
+ echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+ fi
+ exit ;;
+ i*86:*:5:[678]*)
+ # UnixWare 7.x, OpenUNIX and OpenServer 6.
+ case `/bin/uname -X | grep "^Machine"` in
+ *486*) UNAME_MACHINE=i486 ;;
+ *Pentium) UNAME_MACHINE=i586 ;;
+ *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+ esac
+ echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+ exit ;;
+ i*86:*:3.2:*)
+ if test -f /usr/options/cb.name; then
+ UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+ echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+ elif /bin/uname -X 2>/dev/null >/dev/null ; then
+ UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+ (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+ (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+ && UNAME_MACHINE=i586
+ (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+ && UNAME_MACHINE=i686
+ (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+ && UNAME_MACHINE=i686
+ echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+ else
+ echo ${UNAME_MACHINE}-pc-sysv32
+ fi
+ exit ;;
+ pc:*:*:*)
+ # Left here for compatibility:
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i586.
+ # Note: whatever this is, it MUST be the same as what config.sub
+ # prints for the "djgpp" host, or else GDB configury will decide that
+ # this is a cross-build.
+ echo i586-pc-msdosdjgpp
+ exit ;;
+ Intel:Mach:3*:*)
+ echo i386-pc-mach3
+ exit ;;
+ paragon:*:*:*)
+ echo i860-intel-osf1
+ exit ;;
+ i860:*:4.*:*) # i860-SVR4
+ if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+ echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+ else # Add other i860-SVR4 vendors below as they are discovered.
+ echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4
+ fi
+ exit ;;
+ mini*:CTIX:SYS*5:*)
+ # "miniframe"
+ echo m68010-convergent-sysv
+ exit ;;
+ mc68k:UNIX:SYSTEM5:3.51m)
+ echo m68k-convergent-sysv
+ exit ;;
+ M680?0:D-NIX:5.3:*)
+ echo m68k-diab-dnix
+ exit ;;
+ M68*:*:R3V[5678]*:*)
+ test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
+ 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
+ OS_REL=''
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
+ 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4; exit; } ;;
+ NCR*:*:4.2:* | MPRAS*:*:4.2:*)
+ OS_REL='.3'
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && { echo i586-ncr-sysv4.3${OS_REL}; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
+ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
+ m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+ echo m68k-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ mc68030:UNIX_System_V:4.*:*)
+ echo m68k-atari-sysv4
+ exit ;;
+ TSUNAMI:LynxOS:2.*:*)
+ echo sparc-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ rs6000:LynxOS:2.*:*)
+ echo rs6000-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
+ echo powerpc-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ SM[BE]S:UNIX_SV:*:*)
+ echo mips-dde-sysv${UNAME_RELEASE}
+ exit ;;
+ RM*:ReliantUNIX-*:*:*)
+ echo mips-sni-sysv4
+ exit ;;
+ RM*:SINIX-*:*:*)
+ echo mips-sni-sysv4
+ exit ;;
+ *:SINIX-*:*:*)
+ if uname -p 2>/dev/null >/dev/null ; then
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ echo ${UNAME_MACHINE}-sni-sysv4
+ else
+ echo ns32k-sni-sysv
+ fi
+ exit ;;
+ PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+ # says <Richard.M.Bartel@ccMail.Census.GOV>
+ echo i586-unisys-sysv4
+ exit ;;
+ *:UNIX_System_V:4*:FTX*)
+ # From Gerald Hewes <hewes@openmarket.com>.
+ # How about differentiating between stratus architectures? -djm
+ echo hppa1.1-stratus-sysv4
+ exit ;;
+ *:*:*:FTX*)
+ # From seanf@swdc.stratus.com.
+ echo i860-stratus-sysv4
+ exit ;;
+ i*86:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo ${UNAME_MACHINE}-stratus-vos
+ exit ;;
+ *:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo hppa1.1-stratus-vos
+ exit ;;
+ mc68*:A/UX:*:*)
+ echo m68k-apple-aux${UNAME_RELEASE}
+ exit ;;
+ news*:NEWS-OS:6*:*)
+ echo mips-sony-newsos6
+ exit ;;
+ R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+ if [ -d /usr/nec ]; then
+ echo mips-nec-sysv${UNAME_RELEASE}
+ else
+ echo mips-unknown-sysv${UNAME_RELEASE}
+ fi
+ exit ;;
+ BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
+ echo powerpc-be-beos
+ exit ;;
+ BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
+ echo powerpc-apple-beos
+ exit ;;
+ BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
+ echo i586-pc-beos
+ exit ;;
+ BePC:Haiku:*:*) # Haiku running on Intel PC compatible.
+ echo i586-pc-haiku
+ exit ;;
+ SX-4:SUPER-UX:*:*)
+ echo sx4-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-5:SUPER-UX:*:*)
+ echo sx5-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-6:SUPER-UX:*:*)
+ echo sx6-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-7:SUPER-UX:*:*)
+ echo sx7-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-8:SUPER-UX:*:*)
+ echo sx8-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-8R:SUPER-UX:*:*)
+ echo sx8r-nec-superux${UNAME_RELEASE}
+ exit ;;
+ Power*:Rhapsody:*:*)
+ echo powerpc-apple-rhapsody${UNAME_RELEASE}
+ exit ;;
+ *:Rhapsody:*:*)
+ echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+ exit ;;
+ *:Darwin:*:*)
+ UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
+ case $UNAME_PROCESSOR in
+ i386)
+ eval $set_cc_for_build
+ if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+ if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ UNAME_PROCESSOR="x86_64"
+ fi
+ fi ;;
+ unknown) UNAME_PROCESSOR=powerpc ;;
+ esac
+ echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
+ exit ;;
+ *:procnto*:*:* | *:QNX:[0123456789]*:*)
+ UNAME_PROCESSOR=`uname -p`
+ if test "$UNAME_PROCESSOR" = "x86"; then
+ UNAME_PROCESSOR=i386
+ UNAME_MACHINE=pc
+ fi
+ echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
+ exit ;;
+ *:QNX:*:4*)
+ echo i386-pc-qnx
+ exit ;;
+ NSE-?:NONSTOP_KERNEL:*:*)
+ echo nse-tandem-nsk${UNAME_RELEASE}
+ exit ;;
+ NSR-?:NONSTOP_KERNEL:*:*)
+ echo nsr-tandem-nsk${UNAME_RELEASE}
+ exit ;;
+ *:NonStop-UX:*:*)
+ echo mips-compaq-nonstopux
+ exit ;;
+ BS2000:POSIX*:*:*)
+ echo bs2000-siemens-sysv
+ exit ;;
+ DS/*:UNIX_System_V:*:*)
+ echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+ exit ;;
+ *:Plan9:*:*)
+ # "uname -m" is not consistent, so use $cputype instead. 386
+ # is converted to i386 for consistency with other x86
+ # operating systems.
+ if test "$cputype" = "386"; then
+ UNAME_MACHINE=i386
+ else
+ UNAME_MACHINE="$cputype"
+ fi
+ echo ${UNAME_MACHINE}-unknown-plan9
+ exit ;;
+ *:TOPS-10:*:*)
+ echo pdp10-unknown-tops10
+ exit ;;
+ *:TENEX:*:*)
+ echo pdp10-unknown-tenex
+ exit ;;
+ KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+ echo pdp10-dec-tops20
+ exit ;;
+ XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+ echo pdp10-xkl-tops20
+ exit ;;
+ *:TOPS-20:*:*)
+ echo pdp10-unknown-tops20
+ exit ;;
+ *:ITS:*:*)
+ echo pdp10-unknown-its
+ exit ;;
+ SEI:*:*:SEIUX)
+ echo mips-sei-seiux${UNAME_RELEASE}
+ exit ;;
+ *:DragonFly:*:*)
+ echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+ exit ;;
+ *:*VMS:*:*)
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ case "${UNAME_MACHINE}" in
+ A*) echo alpha-dec-vms ; exit ;;
+ I*) echo ia64-dec-vms ; exit ;;
+ V*) echo vax-dec-vms ; exit ;;
+ esac ;;
+ *:XENIX:*:SysV)
+ echo i386-pc-xenix
+ exit ;;
+ i*86:skyos:*:*)
+ echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//'
+ exit ;;
+ i*86:rdos:*:*)
+ echo ${UNAME_MACHINE}-pc-rdos
+ exit ;;
+ i*86:AROS:*:*)
+ echo ${UNAME_MACHINE}-pc-aros
+ exit ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+eval $set_cc_for_build
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+ /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
+ I don't know.... */
+ printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+ printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+ "4"
+#else
+ ""
+#endif
+ ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+ printf ("arm-acorn-riscix\n"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+ printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+ int version;
+ version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+ if (version < 4)
+ printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+ else
+ printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+ exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+ printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+ printf ("ns32k-encore-mach\n"); exit (0);
+#else
+ printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+ printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+ printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+ printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+ struct utsname un;
+
+ uname(&un);
+
+ if (strncmp(un.version, "V2", 2) == 0) {
+ printf ("i386-sequent-ptx2\n"); exit (0);
+ }
+ if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+ printf ("i386-sequent-ptx1\n"); exit (0);
+ }
+ printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+# if !defined (ultrix)
+# include <sys/param.h>
+# if defined (BSD)
+# if BSD == 43
+ printf ("vax-dec-bsd4.3\n"); exit (0);
+# else
+# if BSD == 199006
+ printf ("vax-dec-bsd4.3reno\n"); exit (0);
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# endif
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# else
+ printf ("vax-dec-ultrix\n"); exit (0);
+# endif
+#endif
+
+#if defined (alliant) && defined (i860)
+ printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+ exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` &&
+ { echo "$SYSTEM_NAME"; exit; }
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+ case `getsysinfo -f cpu_type` in
+ c1*)
+ echo c1-convex-bsd
+ exit ;;
+ c2*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit ;;
+ c34*)
+ echo c34-convex-bsd
+ exit ;;
+ c38*)
+ echo c38-convex-bsd
+ exit ;;
+ c4*)
+ echo c4-convex-bsd
+ exit ;;
+ esac
+fi
+
+cat >&2 <<EOF
+$0: unable to guess system type
+
+This script, last modified $timestamp, has failed to recognize
+the operating system you are using. It is advised that you
+download the most up to date version of the config scripts from
+
+ http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
+and
+ http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
+
+If the version you run ($0) is already up to date, please
+send the following data and any information you think might be
+pertinent to <config-patches@gnu.org> in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo = `(hostinfo) 2>/dev/null`
+/bin/universe = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/config.h.in b/config.h.in
new file mode 100644
index 0000000..5b949bf
--- /dev/null
+++ b/config.h.in
@@ -0,0 +1,96 @@
+/* config.h.in. Generated from configure.ac by autoheader. */
+
+/* Directory for the configuration files */
+#undef CONFIGDIR
+
+/* Define to 1 if you have capabilities library. */
+#undef HAVE_CAPNG
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#undef HAVE_DLFCN_H
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have USB library. */
+#undef HAVE_LIBUSB
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+ */
+#undef LT_OBJDIR
+
+/* Define to 1 if you need the dbus_connection_can_send_type() function. */
+#undef NEED_DBUS_CONNECTION_CAN_SEND_TYPE
+
+/* Define to 1 if you need the dbus_watch_get_unix_fd() function. */
+#undef NEED_DBUS_WATCH_GET_UNIX_FD
+
+/* Define to 1 if you need the ppoll() function. */
+#undef NEED_PPOLL
+
+/* Define to 1 if you need the usb_get_busses() function. */
+#undef NEED_USB_GET_BUSSES
+
+/* Define to 1 if you need the usb_interrupt_read() function. */
+#undef NEED_USB_INTERRUPT_READ
+
+/* Define to 1 if your C compiler doesn't accept -c and -o together. */
+#undef NO_MINUS_C_MINUS_O
+
+/* Define the OUI file path */
+#undef OUIFILE
+
+/* Name of package */
+#undef PACKAGE
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Directory for the storage files */
+#undef STORAGEDIR
+
+/* Version number of package */
+#undef VERSION
+
+/* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a
+ `char[]'. */
+#undef YYTEXT_POINTER
diff --git a/config.sub b/config.sub
new file mode 100755
index 0000000..2a55a50
--- /dev/null
+++ b/config.sub
@@ -0,0 +1,1705 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+# Free Software Foundation, Inc.
+
+timestamp='2009-11-20'
+
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine. It does not imply ALL GNU software can.
+#
+# This file 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 Street - Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+
+# Please send patches to <config-patches@gnu.org>. Submit a context
+# diff and a properly formatted GNU ChangeLog entry.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# You can get the latest version of this script from:
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support. The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS
+ $0 [OPTION] ALIAS
+
+Canonicalize a configuration name.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit ;;
+ --version | -v )
+ echo "$version" ; exit ;;
+ --help | --h* | -h )
+ echo "$usage"; exit ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help"
+ exit 1 ;;
+
+ *local*)
+ # First pass through any local machine types.
+ echo $1
+ exit ;;
+
+ * )
+ break ;;
+ esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+ exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+ exit 1;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+ nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \
+ uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \
+ kopensolaris*-gnu* | \
+ storm-chaos* | os2-emx* | rtmk-nova*)
+ os=-$maybe_os
+ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+ ;;
+ *)
+ basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+ if [ $basic_machine != $1 ]
+ then os=`echo $1 | sed 's/.*-/-/'`
+ else os=; fi
+ ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work. We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+ -sun*os*)
+ # Prevent following clause from handling this invalid input.
+ ;;
+ -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+ -apple | -axis | -knuth | -cray | -microblaze)
+ os=
+ basic_machine=$1
+ ;;
+ -bluegene*)
+ os=-cnk
+ ;;
+ -sim | -cisco | -oki | -wec | -winbond)
+ os=
+ basic_machine=$1
+ ;;
+ -scout)
+ ;;
+ -wrs)
+ os=-vxworks
+ basic_machine=$1
+ ;;
+ -chorusos*)
+ os=-chorusos
+ basic_machine=$1
+ ;;
+ -chorusrdb)
+ os=-chorusrdb
+ basic_machine=$1
+ ;;
+ -hiux*)
+ os=-hiuxwe2
+ ;;
+ -sco6)
+ os=-sco5v6
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco5)
+ os=-sco3.2v5
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco4)
+ os=-sco3.2v4
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2.[4-9]*)
+ os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2v[4-9]*)
+ # Don't forget version if it is 3.2v4 or newer.
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco5v6*)
+ # Don't forget version if it is 3.2v4 or newer.
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco*)
+ os=-sco3.2v2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -udk*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -isc)
+ os=-isc2.2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -clix*)
+ basic_machine=clipper-intergraph
+ ;;
+ -isc*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -lynx*)
+ os=-lynxos
+ ;;
+ -ptx*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+ ;;
+ -windowsnt*)
+ os=`echo $os | sed -e 's/windowsnt/winnt/'`
+ ;;
+ -psos*)
+ os=-psos
+ ;;
+ -mint | -mint[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+ # Recognize the basic CPU types without company name.
+ # Some are omitted here because they have special meanings below.
+ 1750a | 580 \
+ | a29k \
+ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
+ | am33_2.0 \
+ | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \
+ | bfin \
+ | c4x | clipper \
+ | d10v | d30v | dlx | dsp16xx \
+ | fido | fr30 | frv \
+ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+ | i370 | i860 | i960 | ia64 \
+ | ip2k | iq2000 \
+ | lm32 \
+ | m32c | m32r | m32rle | m68000 | m68k | m88k \
+ | maxq | mb | microblaze | mcore | mep | metag \
+ | mips | mipsbe | mipseb | mipsel | mipsle \
+ | mips16 \
+ | mips64 | mips64el \
+ | mips64octeon | mips64octeonel \
+ | mips64orion | mips64orionel \
+ | mips64r5900 | mips64r5900el \
+ | mips64vr | mips64vrel \
+ | mips64vr4100 | mips64vr4100el \
+ | mips64vr4300 | mips64vr4300el \
+ | mips64vr5000 | mips64vr5000el \
+ | mips64vr5900 | mips64vr5900el \
+ | mipsisa32 | mipsisa32el \
+ | mipsisa32r2 | mipsisa32r2el \
+ | mipsisa64 | mipsisa64el \
+ | mipsisa64r2 | mipsisa64r2el \
+ | mipsisa64sb1 | mipsisa64sb1el \
+ | mipsisa64sr71k | mipsisa64sr71kel \
+ | mipstx39 | mipstx39el \
+ | mn10200 | mn10300 \
+ | moxie \
+ | mt \
+ | msp430 \
+ | nios | nios2 \
+ | ns16k | ns32k \
+ | or32 \
+ | pdp10 | pdp11 | pj | pjl \
+ | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+ | pyramid \
+ | rx \
+ | score \
+ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
+ | sh64 | sh64le \
+ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
+ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \
+ | spu | strongarm \
+ | tahoe | thumb | tic4x | tic80 | tron \
+ | ubicom32 \
+ | v850 | v850e \
+ | we32k \
+ | x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \
+ | z8k | z80)
+ basic_machine=$basic_machine-unknown
+ ;;
+ m6811 | m68hc11 | m6812 | m68hc12 | picochip)
+ # Motorola 68HC11/12.
+ basic_machine=$basic_machine-unknown
+ os=-none
+ ;;
+ m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+ ;;
+ ms1)
+ basic_machine=mt-unknown
+ ;;
+
+ # We use `pc' rather than `unknown'
+ # because (1) that's what they normally are, and
+ # (2) the word "unknown" tends to confuse beginning users.
+ i*86 | x86_64)
+ basic_machine=$basic_machine-pc
+ ;;
+ # Object if more than one company name word.
+ *-*-*)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+ # Recognize the basic CPU types with company name.
+ 580-* \
+ | a29k-* \
+ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
+ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
+ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \
+ | avr-* | avr32-* \
+ | bfin-* | bs2000-* \
+ | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
+ | clipper-* | craynv-* | cydra-* \
+ | d10v-* | d30v-* | dlx-* \
+ | elxsi-* \
+ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
+ | h8300-* | h8500-* \
+ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+ | i*86-* | i860-* | i960-* | ia64-* \
+ | ip2k-* | iq2000-* \
+ | lm32-* \
+ | m32c-* | m32r-* | m32rle-* \
+ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
+ | m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \
+ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
+ | mips16-* \
+ | mips64-* | mips64el-* \
+ | mips64octeon-* | mips64octeonel-* \
+ | mips64orion-* | mips64orionel-* \
+ | mips64r5900-* | mips64r5900el-* \
+ | mips64vr-* | mips64vrel-* \
+ | mips64vr4100-* | mips64vr4100el-* \
+ | mips64vr4300-* | mips64vr4300el-* \
+ | mips64vr5000-* | mips64vr5000el-* \
+ | mips64vr5900-* | mips64vr5900el-* \
+ | mipsisa32-* | mipsisa32el-* \
+ | mipsisa32r2-* | mipsisa32r2el-* \
+ | mipsisa64-* | mipsisa64el-* \
+ | mipsisa64r2-* | mipsisa64r2el-* \
+ | mipsisa64sb1-* | mipsisa64sb1el-* \
+ | mipsisa64sr71k-* | mipsisa64sr71kel-* \
+ | mipstx39-* | mipstx39el-* \
+ | mmix-* \
+ | mt-* \
+ | msp430-* \
+ | nios-* | nios2-* \
+ | none-* | np1-* | ns16k-* | ns32k-* \
+ | orion-* \
+ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+ | pyramid-* \
+ | romp-* | rs6000-* | rx-* \
+ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
+ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
+ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
+ | sparclite-* \
+ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \
+ | tahoe-* | thumb-* \
+ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* | tile-* \
+ | tron-* \
+ | ubicom32-* \
+ | v850-* | v850e-* | vax-* \
+ | we32k-* \
+ | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \
+ | xstormy16-* | xtensa*-* \
+ | ymp-* \
+ | z8k-* | z80-*)
+ ;;
+ # Recognize the basic CPU types without company name, with glob match.
+ xtensa*)
+ basic_machine=$basic_machine-unknown
+ ;;
+ # Recognize the various machine names and aliases which stand
+ # for a CPU type and a company and sometimes even an OS.
+ 386bsd)
+ basic_machine=i386-unknown
+ os=-bsd
+ ;;
+ 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+ basic_machine=m68000-att
+ ;;
+ 3b*)
+ basic_machine=we32k-att
+ ;;
+ a29khif)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ abacus)
+ basic_machine=abacus-unknown
+ ;;
+ adobe68k)
+ basic_machine=m68010-adobe
+ os=-scout
+ ;;
+ alliant | fx80)
+ basic_machine=fx80-alliant
+ ;;
+ altos | altos3068)
+ basic_machine=m68k-altos
+ ;;
+ am29k)
+ basic_machine=a29k-none
+ os=-bsd
+ ;;
+ amd64)
+ basic_machine=x86_64-pc
+ ;;
+ amd64-*)
+ basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ amdahl)
+ basic_machine=580-amdahl
+ os=-sysv
+ ;;
+ amiga | amiga-*)
+ basic_machine=m68k-unknown
+ ;;
+ amigaos | amigados)
+ basic_machine=m68k-unknown
+ os=-amigaos
+ ;;
+ amigaunix | amix)
+ basic_machine=m68k-unknown
+ os=-sysv4
+ ;;
+ apollo68)
+ basic_machine=m68k-apollo
+ os=-sysv
+ ;;
+ apollo68bsd)
+ basic_machine=m68k-apollo
+ os=-bsd
+ ;;
+ aros)
+ basic_machine=i386-pc
+ os=-aros
+ ;;
+ aux)
+ basic_machine=m68k-apple
+ os=-aux
+ ;;
+ balance)
+ basic_machine=ns32k-sequent
+ os=-dynix
+ ;;
+ blackfin)
+ basic_machine=bfin-unknown
+ os=-linux
+ ;;
+ blackfin-*)
+ basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'`
+ os=-linux
+ ;;
+ bluegene*)
+ basic_machine=powerpc-ibm
+ os=-cnk
+ ;;
+ c90)
+ basic_machine=c90-cray
+ os=-unicos
+ ;;
+ cegcc)
+ basic_machine=arm-unknown
+ os=-cegcc
+ ;;
+ convex-c1)
+ basic_machine=c1-convex
+ os=-bsd
+ ;;
+ convex-c2)
+ basic_machine=c2-convex
+ os=-bsd
+ ;;
+ convex-c32)
+ basic_machine=c32-convex
+ os=-bsd
+ ;;
+ convex-c34)
+ basic_machine=c34-convex
+ os=-bsd
+ ;;
+ convex-c38)
+ basic_machine=c38-convex
+ os=-bsd
+ ;;
+ cray | j90)
+ basic_machine=j90-cray
+ os=-unicos
+ ;;
+ craynv)
+ basic_machine=craynv-cray
+ os=-unicosmp
+ ;;
+ cr16)
+ basic_machine=cr16-unknown
+ os=-elf
+ ;;
+ crds | unos)
+ basic_machine=m68k-crds
+ ;;
+ crisv32 | crisv32-* | etraxfs*)
+ basic_machine=crisv32-axis
+ ;;
+ cris | cris-* | etrax*)
+ basic_machine=cris-axis
+ ;;
+ crx)
+ basic_machine=crx-unknown
+ os=-elf
+ ;;
+ da30 | da30-*)
+ basic_machine=m68k-da30
+ ;;
+ decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+ basic_machine=mips-dec
+ ;;
+ decsystem10* | dec10*)
+ basic_machine=pdp10-dec
+ os=-tops10
+ ;;
+ decsystem20* | dec20*)
+ basic_machine=pdp10-dec
+ os=-tops20
+ ;;
+ delta | 3300 | motorola-3300 | motorola-delta \
+ | 3300-motorola | delta-motorola)
+ basic_machine=m68k-motorola
+ ;;
+ delta88)
+ basic_machine=m88k-motorola
+ os=-sysv3
+ ;;
+ dicos)
+ basic_machine=i686-pc
+ os=-dicos
+ ;;
+ djgpp)
+ basic_machine=i586-pc
+ os=-msdosdjgpp
+ ;;
+ dpx20 | dpx20-*)
+ basic_machine=rs6000-bull
+ os=-bosx
+ ;;
+ dpx2* | dpx2*-bull)
+ basic_machine=m68k-bull
+ os=-sysv3
+ ;;
+ ebmon29k)
+ basic_machine=a29k-amd
+ os=-ebmon
+ ;;
+ elxsi)
+ basic_machine=elxsi-elxsi
+ os=-bsd
+ ;;
+ encore | umax | mmax)
+ basic_machine=ns32k-encore
+ ;;
+ es1800 | OSE68k | ose68k | ose | OSE)
+ basic_machine=m68k-ericsson
+ os=-ose
+ ;;
+ fx2800)
+ basic_machine=i860-alliant
+ ;;
+ genix)
+ basic_machine=ns32k-ns
+ ;;
+ gmicro)
+ basic_machine=tron-gmicro
+ os=-sysv
+ ;;
+ go32)
+ basic_machine=i386-pc
+ os=-go32
+ ;;
+ h3050r* | hiux*)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ h8300hms)
+ basic_machine=h8300-hitachi
+ os=-hms
+ ;;
+ h8300xray)
+ basic_machine=h8300-hitachi
+ os=-xray
+ ;;
+ h8500hms)
+ basic_machine=h8500-hitachi
+ os=-hms
+ ;;
+ harris)
+ basic_machine=m88k-harris
+ os=-sysv3
+ ;;
+ hp300-*)
+ basic_machine=m68k-hp
+ ;;
+ hp300bsd)
+ basic_machine=m68k-hp
+ os=-bsd
+ ;;
+ hp300hpux)
+ basic_machine=m68k-hp
+ os=-hpux
+ ;;
+ hp3k9[0-9][0-9] | hp9[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k2[0-9][0-9] | hp9k31[0-9])
+ basic_machine=m68000-hp
+ ;;
+ hp9k3[2-9][0-9])
+ basic_machine=m68k-hp
+ ;;
+ hp9k6[0-9][0-9] | hp6[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k7[0-79][0-9] | hp7[0-79][0-9])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k78[0-9] | hp78[0-9])
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][13679] | hp8[0-9][13679])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][0-9] | hp8[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hppa-next)
+ os=-nextstep3
+ ;;
+ hppaosf)
+ basic_machine=hppa1.1-hp
+ os=-osf
+ ;;
+ hppro)
+ basic_machine=hppa1.1-hp
+ os=-proelf
+ ;;
+ i370-ibm* | ibm*)
+ basic_machine=i370-ibm
+ ;;
+# I'm not sure what "Sysv32" means. Should this be sysv3.2?
+ i*86v32)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv32
+ ;;
+ i*86v4*)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv4
+ ;;
+ i*86v)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv
+ ;;
+ i*86sol2)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-solaris2
+ ;;
+ i386mach)
+ basic_machine=i386-mach
+ os=-mach
+ ;;
+ i386-vsta | vsta)
+ basic_machine=i386-unknown
+ os=-vsta
+ ;;
+ iris | iris4d)
+ basic_machine=mips-sgi
+ case $os in
+ -irix*)
+ ;;
+ *)
+ os=-irix4
+ ;;
+ esac
+ ;;
+ isi68 | isi)
+ basic_machine=m68k-isi
+ os=-sysv
+ ;;
+ m68knommu)
+ basic_machine=m68k-unknown
+ os=-linux
+ ;;
+ m68knommu-*)
+ basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'`
+ os=-linux
+ ;;
+ m88k-omron*)
+ basic_machine=m88k-omron
+ ;;
+ magnum | m3230)
+ basic_machine=mips-mips
+ os=-sysv
+ ;;
+ merlin)
+ basic_machine=ns32k-utek
+ os=-sysv
+ ;;
+ microblaze)
+ basic_machine=microblaze-xilinx
+ ;;
+ mingw32)
+ basic_machine=i386-pc
+ os=-mingw32
+ ;;
+ mingw32ce)
+ basic_machine=arm-unknown
+ os=-mingw32ce
+ ;;
+ miniframe)
+ basic_machine=m68000-convergent
+ ;;
+ *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+ mips3*-*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+ ;;
+ mips3*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+ ;;
+ monitor)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ morphos)
+ basic_machine=powerpc-unknown
+ os=-morphos
+ ;;
+ msdos)
+ basic_machine=i386-pc
+ os=-msdos
+ ;;
+ ms1-*)
+ basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
+ ;;
+ mvs)
+ basic_machine=i370-ibm
+ os=-mvs
+ ;;
+ ncr3000)
+ basic_machine=i486-ncr
+ os=-sysv4
+ ;;
+ netbsd386)
+ basic_machine=i386-unknown
+ os=-netbsd
+ ;;
+ netwinder)
+ basic_machine=armv4l-rebel
+ os=-linux
+ ;;
+ news | news700 | news800 | news900)
+ basic_machine=m68k-sony
+ os=-newsos
+ ;;
+ news1000)
+ basic_machine=m68030-sony
+ os=-newsos
+ ;;
+ news-3600 | risc-news)
+ basic_machine=mips-sony
+ os=-newsos
+ ;;
+ necv70)
+ basic_machine=v70-nec
+ os=-sysv
+ ;;
+ next | m*-next )
+ basic_machine=m68k-next
+ case $os in
+ -nextstep* )
+ ;;
+ -ns2*)
+ os=-nextstep2
+ ;;
+ *)
+ os=-nextstep3
+ ;;
+ esac
+ ;;
+ nh3000)
+ basic_machine=m68k-harris
+ os=-cxux
+ ;;
+ nh[45]000)
+ basic_machine=m88k-harris
+ os=-cxux
+ ;;
+ nindy960)
+ basic_machine=i960-intel
+ os=-nindy
+ ;;
+ mon960)
+ basic_machine=i960-intel
+ os=-mon960
+ ;;
+ nonstopux)
+ basic_machine=mips-compaq
+ os=-nonstopux
+ ;;
+ np1)
+ basic_machine=np1-gould
+ ;;
+ nsr-tandem)
+ basic_machine=nsr-tandem
+ ;;
+ op50n-* | op60c-*)
+ basic_machine=hppa1.1-oki
+ os=-proelf
+ ;;
+ openrisc | openrisc-*)
+ basic_machine=or32-unknown
+ ;;
+ os400)
+ basic_machine=powerpc-ibm
+ os=-os400
+ ;;
+ OSE68000 | ose68000)
+ basic_machine=m68000-ericsson
+ os=-ose
+ ;;
+ os68k)
+ basic_machine=m68k-none
+ os=-os68k
+ ;;
+ pa-hitachi)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ paragon)
+ basic_machine=i860-intel
+ os=-osf
+ ;;
+ parisc)
+ basic_machine=hppa-unknown
+ os=-linux
+ ;;
+ parisc-*)
+ basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'`
+ os=-linux
+ ;;
+ pbd)
+ basic_machine=sparc-tti
+ ;;
+ pbb)
+ basic_machine=m68k-tti
+ ;;
+ pc532 | pc532-*)
+ basic_machine=ns32k-pc532
+ ;;
+ pc98)
+ basic_machine=i386-pc
+ ;;
+ pc98-*)
+ basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentium | p5 | k5 | k6 | nexgen | viac3)
+ basic_machine=i586-pc
+ ;;
+ pentiumpro | p6 | 6x86 | athlon | athlon_*)
+ basic_machine=i686-pc
+ ;;
+ pentiumii | pentium2 | pentiumiii | pentium3)
+ basic_machine=i686-pc
+ ;;
+ pentium4)
+ basic_machine=i786-pc
+ ;;
+ pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+ basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumpro-* | p6-* | 6x86-* | athlon-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentium4-*)
+ basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pn)
+ basic_machine=pn-gould
+ ;;
+ power) basic_machine=power-ibm
+ ;;
+ ppc) basic_machine=powerpc-unknown
+ ;;
+ ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppcle | powerpclittle | ppc-le | powerpc-little)
+ basic_machine=powerpcle-unknown
+ ;;
+ ppcle-* | powerpclittle-*)
+ basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64) basic_machine=powerpc64-unknown
+ ;;
+ ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64le | powerpc64little | ppc64-le | powerpc64-little)
+ basic_machine=powerpc64le-unknown
+ ;;
+ ppc64le-* | powerpc64little-*)
+ basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ps2)
+ basic_machine=i386-ibm
+ ;;
+ pw32)
+ basic_machine=i586-unknown
+ os=-pw32
+ ;;
+ rdos)
+ basic_machine=i386-pc
+ os=-rdos
+ ;;
+ rom68k)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ rm[46]00)
+ basic_machine=mips-siemens
+ ;;
+ rtpc | rtpc-*)
+ basic_machine=romp-ibm
+ ;;
+ s390 | s390-*)
+ basic_machine=s390-ibm
+ ;;
+ s390x | s390x-*)
+ basic_machine=s390x-ibm
+ ;;
+ sa29200)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ sb1)
+ basic_machine=mipsisa64sb1-unknown
+ ;;
+ sb1el)
+ basic_machine=mipsisa64sb1el-unknown
+ ;;
+ sde)
+ basic_machine=mipsisa32-sde
+ os=-elf
+ ;;
+ sei)
+ basic_machine=mips-sei
+ os=-seiux
+ ;;
+ sequent)
+ basic_machine=i386-sequent
+ ;;
+ sh)
+ basic_machine=sh-hitachi
+ os=-hms
+ ;;
+ sh5el)
+ basic_machine=sh5le-unknown
+ ;;
+ sh64)
+ basic_machine=sh64-unknown
+ ;;
+ sparclite-wrs | simso-wrs)
+ basic_machine=sparclite-wrs
+ os=-vxworks
+ ;;
+ sps7)
+ basic_machine=m68k-bull
+ os=-sysv2
+ ;;
+ spur)
+ basic_machine=spur-unknown
+ ;;
+ st2000)
+ basic_machine=m68k-tandem
+ ;;
+ stratus)
+ basic_machine=i860-stratus
+ os=-sysv4
+ ;;
+ sun2)
+ basic_machine=m68000-sun
+ ;;
+ sun2os3)
+ basic_machine=m68000-sun
+ os=-sunos3
+ ;;
+ sun2os4)
+ basic_machine=m68000-sun
+ os=-sunos4
+ ;;
+ sun3os3)
+ basic_machine=m68k-sun
+ os=-sunos3
+ ;;
+ sun3os4)
+ basic_machine=m68k-sun
+ os=-sunos4
+ ;;
+ sun4os3)
+ basic_machine=sparc-sun
+ os=-sunos3
+ ;;
+ sun4os4)
+ basic_machine=sparc-sun
+ os=-sunos4
+ ;;
+ sun4sol2)
+ basic_machine=sparc-sun
+ os=-solaris2
+ ;;
+ sun3 | sun3-*)
+ basic_machine=m68k-sun
+ ;;
+ sun4)
+ basic_machine=sparc-sun
+ ;;
+ sun386 | sun386i | roadrunner)
+ basic_machine=i386-sun
+ ;;
+ sv1)
+ basic_machine=sv1-cray
+ os=-unicos
+ ;;
+ symmetry)
+ basic_machine=i386-sequent
+ os=-dynix
+ ;;
+ t3e)
+ basic_machine=alphaev5-cray
+ os=-unicos
+ ;;
+ t90)
+ basic_machine=t90-cray
+ os=-unicos
+ ;;
+ tic54x | c54x*)
+ basic_machine=tic54x-unknown
+ os=-coff
+ ;;
+ tic55x | c55x*)
+ basic_machine=tic55x-unknown
+ os=-coff
+ ;;
+ tic6x | c6x*)
+ basic_machine=tic6x-unknown
+ os=-coff
+ ;;
+ tile*)
+ basic_machine=tile-unknown
+ os=-linux-gnu
+ ;;
+ tx39)
+ basic_machine=mipstx39-unknown
+ ;;
+ tx39el)
+ basic_machine=mipstx39el-unknown
+ ;;
+ toad1)
+ basic_machine=pdp10-xkl
+ os=-tops20
+ ;;
+ tower | tower-32)
+ basic_machine=m68k-ncr
+ ;;
+ tpf)
+ basic_machine=s390x-ibm
+ os=-tpf
+ ;;
+ udi29k)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ ultra3)
+ basic_machine=a29k-nyu
+ os=-sym1
+ ;;
+ v810 | necv810)
+ basic_machine=v810-nec
+ os=-none
+ ;;
+ vaxv)
+ basic_machine=vax-dec
+ os=-sysv
+ ;;
+ vms)
+ basic_machine=vax-dec
+ os=-vms
+ ;;
+ vpp*|vx|vx-*)
+ basic_machine=f301-fujitsu
+ ;;
+ vxworks960)
+ basic_machine=i960-wrs
+ os=-vxworks
+ ;;
+ vxworks68)
+ basic_machine=m68k-wrs
+ os=-vxworks
+ ;;
+ vxworks29k)
+ basic_machine=a29k-wrs
+ os=-vxworks
+ ;;
+ w65*)
+ basic_machine=w65-wdc
+ os=-none
+ ;;
+ w89k-*)
+ basic_machine=hppa1.1-winbond
+ os=-proelf
+ ;;
+ xbox)
+ basic_machine=i686-pc
+ os=-mingw32
+ ;;
+ xps | xps100)
+ basic_machine=xps100-honeywell
+ ;;
+ ymp)
+ basic_machine=ymp-cray
+ os=-unicos
+ ;;
+ z8k-*-coff)
+ basic_machine=z8k-unknown
+ os=-sim
+ ;;
+ z80-*-coff)
+ basic_machine=z80-unknown
+ os=-sim
+ ;;
+ none)
+ basic_machine=none-none
+ os=-none
+ ;;
+
+# Here we handle the default manufacturer of certain CPU types. It is in
+# some cases the only manufacturer, in others, it is the most popular.
+ w89k)
+ basic_machine=hppa1.1-winbond
+ ;;
+ op50n)
+ basic_machine=hppa1.1-oki
+ ;;
+ op60c)
+ basic_machine=hppa1.1-oki
+ ;;
+ romp)
+ basic_machine=romp-ibm
+ ;;
+ mmix)
+ basic_machine=mmix-knuth
+ ;;
+ rs6000)
+ basic_machine=rs6000-ibm
+ ;;
+ vax)
+ basic_machine=vax-dec
+ ;;
+ pdp10)
+ # there are many clones, so DEC is not a safe bet
+ basic_machine=pdp10-unknown
+ ;;
+ pdp11)
+ basic_machine=pdp11-dec
+ ;;
+ we32k)
+ basic_machine=we32k-att
+ ;;
+ sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele)
+ basic_machine=sh-unknown
+ ;;
+ sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v)
+ basic_machine=sparc-sun
+ ;;
+ cydra)
+ basic_machine=cydra-cydrome
+ ;;
+ orion)
+ basic_machine=orion-highlevel
+ ;;
+ orion105)
+ basic_machine=clipper-highlevel
+ ;;
+ mac | mpw | mac-mpw)
+ basic_machine=m68k-apple
+ ;;
+ pmac | pmac-mpw)
+ basic_machine=powerpc-apple
+ ;;
+ *-unknown)
+ # Make sure to match an already-canonicalized machine name.
+ ;;
+ *)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+ *-digital*)
+ basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+ ;;
+ *-commodore*)
+ basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+ ;;
+ *)
+ ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+ # First match some system type aliases
+ # that might get confused with valid system types.
+ # -solaris* is a basic system type, with this one exception.
+ -auroraux)
+ os=-auroraux
+ ;;
+ -solaris1 | -solaris1.*)
+ os=`echo $os | sed -e 's|solaris1|sunos4|'`
+ ;;
+ -solaris)
+ os=-solaris2
+ ;;
+ -svr4*)
+ os=-sysv4
+ ;;
+ -unixware*)
+ os=-sysv4.2uw
+ ;;
+ -gnu/linux*)
+ os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+ ;;
+ # First accept the basic system types.
+ # The portable systems comes first.
+ # Each alternative MUST END IN A *, to match a version number.
+ # -sysv* is not here because it comes later, after sysvr4.
+ -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\
+ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
+ | -sym* | -kopensolaris* \
+ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+ | -aos* | -aros* \
+ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
+ | -openbsd* | -solidbsd* \
+ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
+ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+ | -chorusos* | -chorusrdb* | -cegcc* \
+ | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+ | -mingw32* | -linux-gnu* | -linux-newlib* | -linux-uclibc* \
+ | -uxpv* | -beos* | -mpeix* | -udk* \
+ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
+ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
+ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
+ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
+ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
+ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*)
+ # Remember, each alternative MUST END IN *, to match a version number.
+ ;;
+ -qnx*)
+ case $basic_machine in
+ x86-* | i*86-*)
+ ;;
+ *)
+ os=-nto$os
+ ;;
+ esac
+ ;;
+ -nto-qnx*)
+ ;;
+ -nto*)
+ os=`echo $os | sed -e 's|nto|nto-qnx|'`
+ ;;
+ -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+ | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \
+ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+ ;;
+ -mac*)
+ os=`echo $os | sed -e 's|mac|macos|'`
+ ;;
+ -linux-dietlibc)
+ os=-linux-dietlibc
+ ;;
+ -linux*)
+ os=`echo $os | sed -e 's|linux|linux-gnu|'`
+ ;;
+ -sunos5*)
+ os=`echo $os | sed -e 's|sunos5|solaris2|'`
+ ;;
+ -sunos6*)
+ os=`echo $os | sed -e 's|sunos6|solaris3|'`
+ ;;
+ -opened*)
+ os=-openedition
+ ;;
+ -os400*)
+ os=-os400
+ ;;
+ -wince*)
+ os=-wince
+ ;;
+ -osfrose*)
+ os=-osfrose
+ ;;
+ -osf*)
+ os=-osf
+ ;;
+ -utek*)
+ os=-bsd
+ ;;
+ -dynix*)
+ os=-bsd
+ ;;
+ -acis*)
+ os=-aos
+ ;;
+ -atheos*)
+ os=-atheos
+ ;;
+ -syllable*)
+ os=-syllable
+ ;;
+ -386bsd)
+ os=-bsd
+ ;;
+ -ctix* | -uts*)
+ os=-sysv
+ ;;
+ -nova*)
+ os=-rtmk-nova
+ ;;
+ -ns2 )
+ os=-nextstep2
+ ;;
+ -nsk*)
+ os=-nsk
+ ;;
+ # Preserve the version number of sinix5.
+ -sinix5.*)
+ os=`echo $os | sed -e 's|sinix|sysv|'`
+ ;;
+ -sinix*)
+ os=-sysv4
+ ;;
+ -tpf*)
+ os=-tpf
+ ;;
+ -triton*)
+ os=-sysv3
+ ;;
+ -oss*)
+ os=-sysv3
+ ;;
+ -svr4)
+ os=-sysv4
+ ;;
+ -svr3)
+ os=-sysv3
+ ;;
+ -sysvr4)
+ os=-sysv4
+ ;;
+ # This must come after -sysvr4.
+ -sysv*)
+ ;;
+ -ose*)
+ os=-ose
+ ;;
+ -es1800*)
+ os=-ose
+ ;;
+ -xenix)
+ os=-xenix
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ os=-mint
+ ;;
+ -aros*)
+ os=-aros
+ ;;
+ -kaos*)
+ os=-kaos
+ ;;
+ -zvmoe)
+ os=-zvmoe
+ ;;
+ -dicos*)
+ os=-dicos
+ ;;
+ -none)
+ ;;
+ *)
+ # Get rid of the `-' at the beginning of $os.
+ os=`echo $os | sed 's/[^-]*-//'`
+ echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system. Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+ score-*)
+ os=-elf
+ ;;
+ spu-*)
+ os=-elf
+ ;;
+ *-acorn)
+ os=-riscix1.2
+ ;;
+ arm*-rebel)
+ os=-linux
+ ;;
+ arm*-semi)
+ os=-aout
+ ;;
+ c4x-* | tic4x-*)
+ os=-coff
+ ;;
+ # This must come before the *-dec entry.
+ pdp10-*)
+ os=-tops20
+ ;;
+ pdp11-*)
+ os=-none
+ ;;
+ *-dec | vax-*)
+ os=-ultrix4.2
+ ;;
+ m68*-apollo)
+ os=-domain
+ ;;
+ i386-sun)
+ os=-sunos4.0.2
+ ;;
+ m68000-sun)
+ os=-sunos3
+ # This also exists in the configure program, but was not the
+ # default.
+ # os=-sunos4
+ ;;
+ m68*-cisco)
+ os=-aout
+ ;;
+ mep-*)
+ os=-elf
+ ;;
+ mips*-cisco)
+ os=-elf
+ ;;
+ mips*-*)
+ os=-elf
+ ;;
+ or32-*)
+ os=-coff
+ ;;
+ *-tti) # must be before sparc entry or we get the wrong os.
+ os=-sysv3
+ ;;
+ sparc-* | *-sun)
+ os=-sunos4.1.1
+ ;;
+ *-be)
+ os=-beos
+ ;;
+ *-haiku)
+ os=-haiku
+ ;;
+ *-ibm)
+ os=-aix
+ ;;
+ *-knuth)
+ os=-mmixware
+ ;;
+ *-wec)
+ os=-proelf
+ ;;
+ *-winbond)
+ os=-proelf
+ ;;
+ *-oki)
+ os=-proelf
+ ;;
+ *-hp)
+ os=-hpux
+ ;;
+ *-hitachi)
+ os=-hiux
+ ;;
+ i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+ os=-sysv
+ ;;
+ *-cbm)
+ os=-amigaos
+ ;;
+ *-dg)
+ os=-dgux
+ ;;
+ *-dolphin)
+ os=-sysv3
+ ;;
+ m68k-ccur)
+ os=-rtu
+ ;;
+ m88k-omron*)
+ os=-luna
+ ;;
+ *-next )
+ os=-nextstep
+ ;;
+ *-sequent)
+ os=-ptx
+ ;;
+ *-crds)
+ os=-unos
+ ;;
+ *-ns)
+ os=-genix
+ ;;
+ i370-*)
+ os=-mvs
+ ;;
+ *-next)
+ os=-nextstep3
+ ;;
+ *-gould)
+ os=-sysv
+ ;;
+ *-highlevel)
+ os=-bsd
+ ;;
+ *-encore)
+ os=-bsd
+ ;;
+ *-sgi)
+ os=-irix
+ ;;
+ *-siemens)
+ os=-sysv4
+ ;;
+ *-masscomp)
+ os=-rtu
+ ;;
+ f30[01]-fujitsu | f700-fujitsu)
+ os=-uxpv
+ ;;
+ *-rom68k)
+ os=-coff
+ ;;
+ *-*bug)
+ os=-coff
+ ;;
+ *-apple)
+ os=-macos
+ ;;
+ *-atari*)
+ os=-mint
+ ;;
+ *)
+ os=-none
+ ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer. We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+ *-unknown)
+ case $os in
+ -riscix*)
+ vendor=acorn
+ ;;
+ -sunos*)
+ vendor=sun
+ ;;
+ -cnk*|-aix*)
+ vendor=ibm
+ ;;
+ -beos*)
+ vendor=be
+ ;;
+ -hpux*)
+ vendor=hp
+ ;;
+ -mpeix*)
+ vendor=hp
+ ;;
+ -hiux*)
+ vendor=hitachi
+ ;;
+ -unos*)
+ vendor=crds
+ ;;
+ -dgux*)
+ vendor=dg
+ ;;
+ -luna*)
+ vendor=omron
+ ;;
+ -genix*)
+ vendor=ns
+ ;;
+ -mvs* | -opened*)
+ vendor=ibm
+ ;;
+ -os400*)
+ vendor=ibm
+ ;;
+ -ptx*)
+ vendor=sequent
+ ;;
+ -tpf*)
+ vendor=ibm
+ ;;
+ -vxsim* | -vxworks* | -windiss*)
+ vendor=wrs
+ ;;
+ -aux*)
+ vendor=apple
+ ;;
+ -hms*)
+ vendor=hitachi
+ ;;
+ -mpw* | -macos*)
+ vendor=apple
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ vendor=atari
+ ;;
+ -vos*)
+ vendor=stratus
+ ;;
+ esac
+ basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+ ;;
+esac
+
+echo $basic_machine$os
+exit
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/configure b/configure
new file mode 100755
index 0000000..e647ad8
--- /dev/null
+++ b/configure
@@ -0,0 +1,16522 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.63 for bluez 4.91.
+#
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
+# 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## --------------------- ##
+## M4sh Initialization. ##
+## --------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in
+ *posix*) set -o posix ;;
+esac
+
+fi
+
+
+
+
+# PATH needs CR
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+if (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='printf %s\n'
+ as_echo_n='printf %s'
+else
+ if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+ as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+ as_echo_n='/usr/ucb/echo -n'
+ else
+ as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+ as_echo_n_body='eval
+ arg=$1;
+ case $arg in
+ *"$as_nl"*)
+ expr "X$arg" : "X\\(.*\\)$as_nl";
+ arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+ esac;
+ expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+ '
+ export as_echo_n_body
+ as_echo_n='sh -c $as_echo_n_body as_echo'
+ fi
+ export as_echo_body
+ as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+ PATH_SEPARATOR=';'
+ }
+fi
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ as_unset=unset
+else
+ as_unset=false
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" "" $as_nl"
+
+# Find who we are. Look in the path if we contain no directory separator.
+case $0 in
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ { (exit 1); exit 1; }
+fi
+
+# Work around bugs in pre-3.0 UWIN ksh.
+for as_var in ENV MAIL MAILPATH
+do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# CDPATH.
+$as_unset CDPATH
+
+
+if test "x$CONFIG_SHELL" = x; then
+ if (eval ":") 2>/dev/null; then
+ as_have_required=yes
+else
+ as_have_required=no
+fi
+
+ if test $as_have_required = yes && (eval ":
+(as_func_return () {
+ (exit \$1)
+}
+as_func_success () {
+ as_func_return 0
+}
+as_func_failure () {
+ as_func_return 1
+}
+as_func_ret_success () {
+ return 0
+}
+as_func_ret_failure () {
+ return 1
+}
+
+exitcode=0
+if as_func_success; then
+ :
+else
+ exitcode=1
+ echo as_func_success failed.
+fi
+
+if as_func_failure; then
+ exitcode=1
+ echo as_func_failure succeeded.
+fi
+
+if as_func_ret_success; then
+ :
+else
+ exitcode=1
+ echo as_func_ret_success failed.
+fi
+
+if as_func_ret_failure; then
+ exitcode=1
+ echo as_func_ret_failure succeeded.
+fi
+
+if ( set x; as_func_ret_success y && test x = \"\$1\" ); then
+ :
+else
+ exitcode=1
+ echo positional parameters were not saved.
+fi
+
+test \$exitcode = 0) || { (exit 1); exit 1; }
+
+(
+ as_lineno_1=\$LINENO
+ as_lineno_2=\$LINENO
+ test \"x\$as_lineno_1\" != \"x\$as_lineno_2\" &&
+ test \"x\`expr \$as_lineno_1 + 1\`\" = \"x\$as_lineno_2\") || { (exit 1); exit 1; }
+") 2> /dev/null; then
+ :
+else
+ as_candidate_shells=
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ case $as_dir in
+ /*)
+ for as_base in sh bash ksh sh5; do
+ as_candidate_shells="$as_candidate_shells $as_dir/$as_base"
+ done;;
+ esac
+done
+IFS=$as_save_IFS
+
+
+ for as_shell in $as_candidate_shells $SHELL; do
+ # Try only shells that exist, to save several forks.
+ if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+ { ("$as_shell") 2> /dev/null <<\_ASEOF
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in
+ *posix*) set -o posix ;;
+esac
+
+fi
+
+
+:
+_ASEOF
+}; then
+ CONFIG_SHELL=$as_shell
+ as_have_required=yes
+ if { "$as_shell" 2> /dev/null <<\_ASEOF
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in
+ *posix*) set -o posix ;;
+esac
+
+fi
+
+
+:
+(as_func_return () {
+ (exit $1)
+}
+as_func_success () {
+ as_func_return 0
+}
+as_func_failure () {
+ as_func_return 1
+}
+as_func_ret_success () {
+ return 0
+}
+as_func_ret_failure () {
+ return 1
+}
+
+exitcode=0
+if as_func_success; then
+ :
+else
+ exitcode=1
+ echo as_func_success failed.
+fi
+
+if as_func_failure; then
+ exitcode=1
+ echo as_func_failure succeeded.
+fi
+
+if as_func_ret_success; then
+ :
+else
+ exitcode=1
+ echo as_func_ret_success failed.
+fi
+
+if as_func_ret_failure; then
+ exitcode=1
+ echo as_func_ret_failure succeeded.
+fi
+
+if ( set x; as_func_ret_success y && test x = "$1" ); then
+ :
+else
+ exitcode=1
+ echo positional parameters were not saved.
+fi
+
+test $exitcode = 0) || { (exit 1); exit 1; }
+
+(
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2") || { (exit 1); exit 1; }
+
+_ASEOF
+}; then
+ break
+fi
+
+fi
+
+ done
+
+ if test "x$CONFIG_SHELL" != x; then
+ for as_var in BASH_ENV ENV
+ do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+ done
+ export CONFIG_SHELL
+ exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"}
+fi
+
+
+ if test $as_have_required = no; then
+ echo This script requires a shell more modern than all the
+ echo shells that I found on your system. Please install a
+ echo modern shell, or manually run the script under such a
+ echo shell if you do have one.
+ { (exit 1); exit 1; }
+fi
+
+
+fi
+
+fi
+
+
+
+(eval "as_func_return () {
+ (exit \$1)
+}
+as_func_success () {
+ as_func_return 0
+}
+as_func_failure () {
+ as_func_return 1
+}
+as_func_ret_success () {
+ return 0
+}
+as_func_ret_failure () {
+ return 1
+}
+
+exitcode=0
+if as_func_success; then
+ :
+else
+ exitcode=1
+ echo as_func_success failed.
+fi
+
+if as_func_failure; then
+ exitcode=1
+ echo as_func_failure succeeded.
+fi
+
+if as_func_ret_success; then
+ :
+else
+ exitcode=1
+ echo as_func_ret_success failed.
+fi
+
+if as_func_ret_failure; then
+ exitcode=1
+ echo as_func_ret_failure succeeded.
+fi
+
+if ( set x; as_func_ret_success y && test x = \"\$1\" ); then
+ :
+else
+ exitcode=1
+ echo positional parameters were not saved.
+fi
+
+test \$exitcode = 0") || {
+ echo No shell found that supports shell functions.
+ echo Please tell bug-autoconf@gnu.org about your system,
+ echo including any error possibly output before this message.
+ echo This can help us improve future autoconf versions.
+ echo Configuration will now proceed without shell functions.
+}
+
+
+
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || {
+
+ # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+ # uniformly replaced by the line number. The first 'sed' inserts a
+ # line-number line after each line using $LINENO; the second 'sed'
+ # does the real work. The second script uses 'N' to pair each
+ # line-number line with the line containing $LINENO, and appends
+ # trailing '-' during substitution so that $LINENO is not a special
+ # case at line end.
+ # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+ # scripts with optimization help from Paolo Bonzini. Blame Lee
+ # E. McMahon (1931-1989) for sed's syntax. :-)
+ sed -n '
+ p
+ /[$]LINENO/=
+ ' <$as_myself |
+ sed '
+ s/[$]LINENO.*/&-/
+ t lineno
+ b
+ :lineno
+ N
+ :loop
+ s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+ t loop
+ s/-\n.*//
+ ' >$as_me.lineno &&
+ chmod +x "$as_me.lineno" ||
+ { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+ { (exit 1); exit 1; }; }
+
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensitive to this).
+ . "./$as_me.lineno"
+ # Exit status is that of the last command.
+ exit
+}
+
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in
+-n*)
+ case `echo 'x\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ *) ECHO_C='\c';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+ if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -p'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -p'
+ elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+ else
+ as_ln_s='cp -p'
+ fi
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p=:
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+if test -x / >/dev/null 2>&1; then
+ as_test_x='test -x'
+else
+ if ls -dL / >/dev/null 2>&1; then
+ as_ls_L_option=L
+ else
+ as_ls_L_option=
+ fi
+ as_test_x='
+ eval sh -c '\''
+ if test -d "$1"; then
+ test -d "$1/.";
+ else
+ case $1 in
+ -*)set "./$1";;
+ esac;
+ case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in
+ ???[sx]*):;;*)false;;esac;fi
+ '\'' sh
+ '
+fi
+as_executable_p=$as_test_x
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+
+
+# Check that we are running under the correct shell.
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+case X$lt_ECHO in
+X*--fallback-echo)
+ # Remove one level of quotation (which was required for Make).
+ ECHO=`echo "$lt_ECHO" | sed 's,\\\\\$\\$0,'$0','`
+ ;;
+esac
+
+ECHO=${lt_ECHO-echo}
+if test "X$1" = X--no-reexec; then
+ # Discard the --no-reexec flag, and continue.
+ shift
+elif test "X$1" = X--fallback-echo; then
+ # Avoid inline document here, it may be left over
+ :
+elif test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' ; then
+ # Yippee, $ECHO works!
+ :
+else
+ # Restart under the correct shell.
+ exec $SHELL "$0" --no-reexec ${1+"$@"}
+fi
+
+if test "X$1" = X--fallback-echo; then
+ # used as fallback echo
+ shift
+ cat <<_LT_EOF
+$*
+_LT_EOF
+ exit 0
+fi
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+if test -z "$lt_ECHO"; then
+ if test "X${echo_test_string+set}" != Xset; then
+ # find a string as large as possible, as long as the shell can cope with it
+ for cmd in 'sed 50q "$0"' 'sed 20q "$0"' 'sed 10q "$0"' 'sed 2q "$0"' 'echo test'; do
+ # expected sizes: less than 2Kb, 1Kb, 512 bytes, 16 bytes, ...
+ if { echo_test_string=`eval $cmd`; } 2>/dev/null &&
+ { test "X$echo_test_string" = "X$echo_test_string"; } 2>/dev/null
+ then
+ break
+ fi
+ done
+ fi
+
+ if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' &&
+ echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ :
+ else
+ # The Solaris, AIX, and Digital Unix default echo programs unquote
+ # backslashes. This makes it impossible to quote backslashes using
+ # echo "$something" | sed 's/\\/\\\\/g'
+ #
+ # So, first we look for a working echo in the user's PATH.
+
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ for dir in $PATH /usr/ucb; do
+ IFS="$lt_save_ifs"
+ if (test -f $dir/echo || test -f $dir/echo$ac_exeext) &&
+ test "X`($dir/echo '\t') 2>/dev/null`" = 'X\t' &&
+ echo_testing_string=`($dir/echo "$echo_test_string") 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ ECHO="$dir/echo"
+ break
+ fi
+ done
+ IFS="$lt_save_ifs"
+
+ if test "X$ECHO" = Xecho; then
+ # We didn't find a better echo, so look for alternatives.
+ if test "X`{ print -r '\t'; } 2>/dev/null`" = 'X\t' &&
+ echo_testing_string=`{ print -r "$echo_test_string"; } 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ # This shell has a builtin print -r that does the trick.
+ ECHO='print -r'
+ elif { test -f /bin/ksh || test -f /bin/ksh$ac_exeext; } &&
+ test "X$CONFIG_SHELL" != X/bin/ksh; then
+ # If we have ksh, try running configure again with it.
+ ORIGINAL_CONFIG_SHELL=${CONFIG_SHELL-/bin/sh}
+ export ORIGINAL_CONFIG_SHELL
+ CONFIG_SHELL=/bin/ksh
+ export CONFIG_SHELL
+ exec $CONFIG_SHELL "$0" --no-reexec ${1+"$@"}
+ else
+ # Try using printf.
+ ECHO='printf %s\n'
+ if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' &&
+ echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ # Cool, printf works
+ :
+ elif echo_testing_string=`($ORIGINAL_CONFIG_SHELL "$0" --fallback-echo '\t') 2>/dev/null` &&
+ test "X$echo_testing_string" = 'X\t' &&
+ echo_testing_string=`($ORIGINAL_CONFIG_SHELL "$0" --fallback-echo "$echo_test_string") 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ CONFIG_SHELL=$ORIGINAL_CONFIG_SHELL
+ export CONFIG_SHELL
+ SHELL="$CONFIG_SHELL"
+ export SHELL
+ ECHO="$CONFIG_SHELL $0 --fallback-echo"
+ elif echo_testing_string=`($CONFIG_SHELL "$0" --fallback-echo '\t') 2>/dev/null` &&
+ test "X$echo_testing_string" = 'X\t' &&
+ echo_testing_string=`($CONFIG_SHELL "$0" --fallback-echo "$echo_test_string") 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ ECHO="$CONFIG_SHELL $0 --fallback-echo"
+ else
+ # maybe with a smaller string...
+ prev=:
+
+ for cmd in 'echo test' 'sed 2q "$0"' 'sed 10q "$0"' 'sed 20q "$0"' 'sed 50q "$0"'; do
+ if { test "X$echo_test_string" = "X`eval $cmd`"; } 2>/dev/null
+ then
+ break
+ fi
+ prev="$cmd"
+ done
+
+ if test "$prev" != 'sed 50q "$0"'; then
+ echo_test_string=`eval $prev`
+ export echo_test_string
+ exec ${ORIGINAL_CONFIG_SHELL-${CONFIG_SHELL-/bin/sh}} "$0" ${1+"$@"}
+ else
+ # Oops. We lost completely, so just stick with echo.
+ ECHO=echo
+ fi
+ fi
+ fi
+ fi
+ fi
+fi
+
+# Copy echo and quote the copy suitably for passing to libtool from
+# the Makefile, instead of quoting the original, which is used later.
+lt_ECHO=$ECHO
+if test "X$lt_ECHO" = "X$CONFIG_SHELL $0 --fallback-echo"; then
+ lt_ECHO="$CONFIG_SHELL \\\$\$0 --fallback-echo"
+fi
+
+
+
+
+exec 7<&0 </dev/null 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIBOBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+# Identity of this package.
+PACKAGE_NAME='bluez'
+PACKAGE_TARNAME='bluez'
+PACKAGE_VERSION='4.91'
+PACKAGE_STRING='bluez 4.91'
+PACKAGE_BUGREPORT=''
+
+ac_default_prefix=/usr/local
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
+#endif
+#ifdef HAVE_STRING_H
+# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
+# include <memory.h>
+# endif
+# include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_subst_vars='am__EXEEXT_FALSE
+am__EXEEXT_TRUE
+LTLIBOBJS
+LIBOBJS
+CAPNG_LIBS
+CAPNG_CFLAGS
+DBUSOOBPLUGIN_FALSE
+DBUSOOBPLUGIN_TRUE
+MAEMO6PLUGIN_FALSE
+MAEMO6PLUGIN_TRUE
+CONFIGFILES_FALSE
+CONFIGFILES_TRUE
+UDEVRULES_FALSE
+UDEVRULES_TRUE
+DFUTOOL_FALSE
+DFUTOOL_TRUE
+HID2HCI_FALSE
+HID2HCI_TRUE
+PCMCIA_FALSE
+PCMCIA_TRUE
+BCCMD_FALSE
+BCCMD_TRUE
+TOOLS_FALSE
+TOOLS_TRUE
+TEST_FALSE
+TEST_TRUE
+CUPS_FALSE
+CUPS_TRUE
+DUND_FALSE
+DUND_TRUE
+PAND_FALSE
+PAND_TRUE
+HIDD_FALSE
+HIDD_TRUE
+TRACER_FALSE
+TRACER_TRUE
+PNATPLUGIN_FALSE
+PNATPLUGIN_TRUE
+ECHOPLUGIN_FALSE
+ECHOPLUGIN_TRUE
+ATTRIBPLUGIN_FALSE
+ATTRIBPLUGIN_TRUE
+READLINE_FALSE
+READLINE_TRUE
+HAL_FALSE
+HAL_TRUE
+MCAP_FALSE
+MCAP_TRUE
+HEALTHPLUGIN_FALSE
+HEALTHPLUGIN_TRUE
+SERVICEPLUGIN_FALSE
+SERVICEPLUGIN_TRUE
+SAPPLUGIN_FALSE
+SAPPLUGIN_TRUE
+NETWORKPLUGIN_FALSE
+NETWORKPLUGIN_TRUE
+SERIALPLUGIN_FALSE
+SERIALPLUGIN_TRUE
+INPUTPLUGIN_FALSE
+INPUTPLUGIN_TRUE
+AUDIOPLUGIN_FALSE
+AUDIOPLUGIN_TRUE
+GSTREAMER_FALSE
+GSTREAMER_TRUE
+ALSA_FALSE
+ALSA_TRUE
+SBC_FALSE
+SBC_TRUE
+USB_FALSE
+USB_TRUE
+SNDFILE_FALSE
+SNDFILE_TRUE
+TELEPHONY_DRIVER
+SAP_DRIVER
+READLINE_LIBS
+SNDFILE_LIBS
+SNDFILE_CFLAGS
+USB_LIBS
+USB_CFLAGS
+GSTREAMER_PLUGINSDIR
+GSTREAMER_LIBS
+GSTREAMER_CFLAGS
+ALSA_LIBS
+ALSA_CFLAGS
+GLIB_LIBS
+GLIB_CFLAGS
+DBUS_LIBS
+DBUS_CFLAGS
+CPP
+OTOOL64
+OTOOL
+LIPO
+NMEDIT
+DSYMUTIL
+lt_ECHO
+RANLIB
+AR
+OBJDUMP
+LN_S
+NM
+ac_ct_DUMPBIN
+DUMPBIN
+LD
+FGREP
+EGREP
+GREP
+SED
+host_os
+host_vendor
+host_cpu
+host
+build_os
+build_vendor
+build_cpu
+build
+LIBTOOL
+LEXLIB
+LEX_OUTPUT_ROOT
+LEX
+YFLAGS
+YACC
+am__fastdepCC_FALSE
+am__fastdepCC_TRUE
+CCDEPMODE
+AMDEPBACKSLASH
+AMDEP_FALSE
+AMDEP_TRUE
+am__quote
+am__include
+DEPDIR
+OBJEXT
+EXEEXT
+ac_ct_CC
+CPPFLAGS
+LDFLAGS
+CFLAGS
+CC
+UDEV_DATADIR
+STORAGEDIR
+CONFIGDIR
+PKG_CONFIG
+MAINT
+MAINTAINER_MODE_FALSE
+MAINTAINER_MODE_TRUE
+AM_BACKSLASH
+AM_DEFAULT_VERBOSITY
+am__untar
+am__tar
+AMTAR
+am__leading_dot
+SET_MAKE
+AWK
+mkdir_p
+MKDIR_P
+INSTALL_STRIP_PROGRAM
+STRIP
+install_sh
+MAKEINFO
+AUTOHEADER
+AUTOMAKE
+AUTOCONF
+ACLOCAL
+VERSION
+PACKAGE
+CYGPATH_W
+am__isrc
+INSTALL_DATA
+INSTALL_SCRIPT
+INSTALL_PROGRAM
+target_alias
+host_alias
+build_alias
+LIBS
+ECHO_T
+ECHO_N
+ECHO_C
+DEFS
+mandir
+localedir
+libdir
+psdir
+pdfdir
+dvidir
+htmldir
+infodir
+docdir
+oldincludedir
+includedir
+localstatedir
+sharedstatedir
+sysconfdir
+datadir
+datarootdir
+libexecdir
+sbindir
+bindir
+program_transform_name
+prefix
+exec_prefix
+PACKAGE_BUGREPORT
+PACKAGE_STRING
+PACKAGE_VERSION
+PACKAGE_TARNAME
+PACKAGE_NAME
+PATH_SEPARATOR
+SHELL'
+ac_subst_files=''
+ac_user_opts='
+enable_option_checking
+enable_silent_rules
+enable_maintainer_mode
+enable_dependency_tracking
+enable_static
+enable_shared
+with_pic
+enable_fast_install
+with_gnu_ld
+enable_libtool_lock
+with_ouifile
+enable_optimization
+enable_fortify
+enable_pie
+enable_network
+enable_sap
+with_sap
+enable_serial
+enable_input
+enable_audio
+enable_service
+enable_health
+enable_pnat
+enable_attrib
+enable_gstreamer
+enable_alsa
+enable_usb
+enable_tracer
+enable_tools
+enable_bccmd
+enable_pcmcia
+enable_hid2hci
+enable_dfutool
+enable_hidd
+enable_pand
+enable_dund
+enable_cups
+enable_test
+enable_udevrules
+enable_configfiles
+enable_debug
+with_telephony
+enable_maemo6
+enable_dbusoob
+enable_hal
+enable_capng
+'
+ ac_precious_vars='build_alias
+host_alias
+target_alias
+PKG_CONFIG
+CC
+CFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS
+YACC
+YFLAGS
+CPP
+DBUS_CFLAGS
+DBUS_LIBS
+GLIB_CFLAGS
+GLIB_LIBS
+ALSA_CFLAGS
+ALSA_LIBS
+GSTREAMER_CFLAGS
+GSTREAMER_LIBS
+USB_CFLAGS
+USB_LIBS
+SNDFILE_CFLAGS
+SNDFILE_LIBS
+CAPNG_CFLAGS
+CAPNG_LIBS'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+ac_unrecognized_opts=
+ac_unrecognized_sep=
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval $ac_prev=\$ac_option
+ ac_prev=
+ continue
+ fi
+
+ case $ac_option in
+ *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+ *) ac_optarg=yes ;;
+ esac
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case $ac_dashdash$ac_option in
+ --)
+ ac_dashdash=yes ;;
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir=$ac_optarg ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build_alias ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build_alias=$ac_optarg ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file=$ac_optarg ;;
+
+ --config-cache | -C)
+ cache_file=config.cache ;;
+
+ -datadir | --datadir | --datadi | --datad)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=*)
+ datadir=$ac_optarg ;;
+
+ -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+ | --dataroo | --dataro | --datar)
+ ac_prev=datarootdir ;;
+ -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+ datarootdir=$ac_optarg ;;
+
+ -disable-* | --disable-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ { $as_echo "$as_me: error: invalid feature name: $ac_useropt" >&2
+ { (exit 1); exit 1; }; }
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"enable_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval enable_$ac_useropt=no ;;
+
+ -docdir | --docdir | --docdi | --doc | --do)
+ ac_prev=docdir ;;
+ -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+ docdir=$ac_optarg ;;
+
+ -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+ ac_prev=dvidir ;;
+ -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+ dvidir=$ac_optarg ;;
+
+ -enable-* | --enable-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ { $as_echo "$as_me: error: invalid feature name: $ac_useropt" >&2
+ { (exit 1); exit 1; }; }
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"enable_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval enable_$ac_useropt=\$ac_optarg ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix=$ac_optarg ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he | -h)
+ ac_init_help=long ;;
+ -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+ ac_init_help=recursive ;;
+ -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+ ac_init_help=short ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host_alias ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host_alias=$ac_optarg ;;
+
+ -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+ ac_prev=htmldir ;;
+ -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+ | --ht=*)
+ htmldir=$ac_optarg ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir=$ac_optarg ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir=$ac_optarg ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir=$ac_optarg ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir=$ac_optarg ;;
+
+ -localedir | --localedir | --localedi | --localed | --locale)
+ ac_prev=localedir ;;
+ -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+ localedir=$ac_optarg ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst | --locals)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+ localstatedir=$ac_optarg ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir=$ac_optarg ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c | -n)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir=$ac_optarg ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix=$ac_optarg ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix=$ac_optarg ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix=$ac_optarg ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name=$ac_optarg ;;
+
+ -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+ ac_prev=pdfdir ;;
+ -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+ pdfdir=$ac_optarg ;;
+
+ -psdir | --psdir | --psdi | --psd | --ps)
+ ac_prev=psdir ;;
+ -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+ psdir=$ac_optarg ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir=$ac_optarg ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir=$ac_optarg ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site=$ac_optarg ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir=$ac_optarg ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir=$ac_optarg ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target_alias ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target_alias=$ac_optarg ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers | -V)
+ ac_init_version=: ;;
+
+ -with-* | --with-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ { $as_echo "$as_me: error: invalid package name: $ac_useropt" >&2
+ { (exit 1); exit 1; }; }
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"with_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval with_$ac_useropt=\$ac_optarg ;;
+
+ -without-* | --without-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ { $as_echo "$as_me: error: invalid package name: $ac_useropt" >&2
+ { (exit 1); exit 1; }; }
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"with_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval with_$ac_useropt=no ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes=$ac_optarg ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries=$ac_optarg ;;
+
+ -*) { $as_echo "$as_me: error: unrecognized option: $ac_option
+Try \`$0 --help' for more information." >&2
+ { (exit 1); exit 1; }; }
+ ;;
+
+ *=*)
+ ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&
+ { $as_echo "$as_me: error: invalid variable name: $ac_envvar" >&2
+ { (exit 1); exit 1; }; }
+ eval $ac_envvar=\$ac_optarg
+ export $ac_envvar ;;
+
+ *)
+ # FIXME: should be removed in autoconf 3.0.
+ $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+ expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+ : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+ { $as_echo "$as_me: error: missing argument to $ac_option" >&2
+ { (exit 1); exit 1; }; }
+fi
+
+if test -n "$ac_unrecognized_opts"; then
+ case $enable_option_checking in
+ no) ;;
+ fatal) { $as_echo "$as_me: error: unrecognized options: $ac_unrecognized_opts" >&2
+ { (exit 1); exit 1; }; } ;;
+ *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+ esac
+fi
+
+# Check all directory arguments for consistency.
+for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
+ datadir sysconfdir sharedstatedir localstatedir includedir \
+ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+ libdir localedir mandir
+do
+ eval ac_val=\$$ac_var
+ # Remove trailing slashes.
+ case $ac_val in
+ */ )
+ ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
+ eval $ac_var=\$ac_val;;
+ esac
+ # Be sure to have absolute directory names.
+ case $ac_val in
+ [\\/$]* | ?:[\\/]* ) continue;;
+ NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+ esac
+ { $as_echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+ { (exit 1); exit 1; }; }
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+ if test "x$build_alias" = x; then
+ cross_compiling=maybe
+ $as_echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
+ If a cross compiler is detected then cross compile mode will be used." >&2
+ elif test "x$build_alias" != "x$host_alias"; then
+ cross_compiling=yes
+ fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+ { $as_echo "$as_me: error: working directory cannot be determined" >&2
+ { (exit 1); exit 1; }; }
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+ { $as_echo "$as_me: error: pwd does not report name of working directory" >&2
+ { (exit 1); exit 1; }; }
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then the parent directory.
+ ac_confdir=`$as_dirname -- "$as_myself" ||
+$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_myself" : 'X\(//\)[^/]' \| \
+ X"$as_myself" : 'X\(//\)$' \| \
+ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_myself" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ srcdir=$ac_confdir
+ if test ! -r "$srcdir/$ac_unique_file"; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+ test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+ { $as_echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2
+ { (exit 1); exit 1; }; }
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+ cd "$srcdir" && test -r "./$ac_unique_file" || { $as_echo "$as_me: error: $ac_msg" >&2
+ { (exit 1); exit 1; }; }
+ pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+ srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+ eval ac_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_env_${ac_var}_value=\$${ac_var}
+ eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat <<_ACEOF
+\`configure' configures bluez 4.91 to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE. See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+ -h, --help display this help and exit
+ --help=short display options specific to this package
+ --help=recursive display the short help of all the included packages
+ -V, --version display version information and exit
+ -q, --quiet, --silent do not print \`checking...' messages
+ --cache-file=FILE cache test results in FILE [disabled]
+ -C, --config-cache alias for \`--cache-file=config.cache'
+ -n, --no-create do not create output files
+ --srcdir=DIR find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+ --bindir=DIR user executables [EPREFIX/bin]
+ --sbindir=DIR system admin executables [EPREFIX/sbin]
+ --libexecdir=DIR program executables [EPREFIX/libexec]
+ --sysconfdir=DIR read-only single-machine data [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --libdir=DIR object code libraries [EPREFIX/lib]
+ --includedir=DIR C header files [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc [/usr/include]
+ --datarootdir=DIR read-only arch.-independent data root [PREFIX/share]
+ --datadir=DIR read-only architecture-independent data [DATAROOTDIR]
+ --infodir=DIR info documentation [DATAROOTDIR/info]
+ --localedir=DIR locale-dependent data [DATAROOTDIR/locale]
+ --mandir=DIR man documentation [DATAROOTDIR/man]
+ --docdir=DIR documentation root [DATAROOTDIR/doc/bluez]
+ --htmldir=DIR html documentation [DOCDIR]
+ --dvidir=DIR dvi documentation [DOCDIR]
+ --pdfdir=DIR pdf documentation [DOCDIR]
+ --psdir=DIR ps documentation [DOCDIR]
+_ACEOF
+
+ cat <<\_ACEOF
+
+Program names:
+ --program-prefix=PREFIX prepend PREFIX to installed program names
+ --program-suffix=SUFFIX append SUFFIX to installed program names
+ --program-transform-name=PROGRAM run sed PROGRAM on installed program names
+
+System types:
+ --build=BUILD configure for building on BUILD [guessed]
+ --host=HOST cross-compile to build programs to run on HOST [BUILD]
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+ case $ac_init_help in
+ short | recursive ) echo "Configuration of bluez 4.91:";;
+ esac
+ cat <<\_ACEOF
+
+Optional Features:
+ --disable-option-checking ignore unrecognized --enable/--with options
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --enable-silent-rules less verbose build output (undo: `make V=1')
+ --disable-silent-rules verbose build output (undo: `make V=0')
+ --enable-maintainer-mode enable make rules and dependencies not useful
+ (and sometimes confusing) to the casual installer
+ --disable-dependency-tracking speeds up one-time build
+ --enable-dependency-tracking do not reject slow dependency extractors
+ --enable-static[=PKGS] build static libraries [default=no]
+ --enable-shared[=PKGS] build shared libraries [default=yes]
+ --enable-fast-install[=PKGS]
+ optimize for fast installation [default=yes]
+ --disable-libtool-lock avoid locking (might break parallel builds)
+ --disable-optimization disable code optimization
+ --disable-fortify disable compile time buffer checks
+ --disable-pie disable position independent executables flag
+ --disable-network disable network plugin
+ --enable-sap enable sap plugin
+ --disable-serial disable serial plugin
+ --disable-input disable input plugin
+ --disable-audio disable audio plugin
+ --disable-service disable service plugin
+ --enable-health enable health plugin
+ --enable-pnat enable pnat plugin
+ --enable-attrib enable attrib plugin
+ --enable-gstreamer enable GStreamer support
+ --enable-alsa enable ALSA support
+ --enable-usb enable USB support
+ --enable-tracer install Tracing daemon
+ --enable-tools install Bluetooth utilities
+ --enable-bccmd install BCCMD interface utility
+ --enable-pcmcia install PCMCIA serial script
+ --enable-hid2hci install HID mode switching utility
+ --enable-dfutool install DFU firmware upgrade utility
+ --enable-hidd install HID daemon
+ --enable-pand install PAN daemon
+ --enable-dund install DUN daemon
+ --enable-cups install CUPS backend support
+ --enable-test install test programs
+ --enable-udevrules install Bluetooth udev rules
+ --enable-configfiles install Bluetooth configuration files
+ --enable-debug enable compiling with debugging information
+ --enable-maemo6 compile with maemo6 plugin
+ --enable-dbusoob compile with D-Bus OOB plugin
+ --enable-hal Use HAL to determine adapter class
+ --enable-capng enable capabilities support
+
+Optional Packages:
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --with-pic try to use only PIC/non-PIC objects [default=use
+ both]
+ --with-gnu-ld assume the C compiler uses GNU ld [default=no]
+ --with-ouifile=PATH Path to the oui.txt file [auto]
+ --with-sap=DRIVER select SAP driver
+ --with-telephony=DRIVER select telephony driver
+
+Some influential environment variables:
+ PKG_CONFIG path to pkg-config utility
+ CC C compiler command
+ CFLAGS C compiler flags
+ LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
+ nonstandard directory <lib dir>
+ LIBS libraries to pass to the linker, e.g. -l<library>
+ CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I<include dir> if
+ you have headers in a nonstandard directory <include dir>
+ YACC The `Yet Another C Compiler' implementation to use. Defaults to
+ the first program found out of: `bison -y', `byacc', `yacc'.
+ YFLAGS The list of arguments that will be passed by default to $YACC.
+ This script will default YFLAGS to the empty string to avoid a
+ default value of `-d' given by some make applications.
+ CPP C preprocessor
+ DBUS_CFLAGS C compiler flags for DBUS, overriding pkg-config
+ DBUS_LIBS linker flags for DBUS, overriding pkg-config
+ GLIB_CFLAGS C compiler flags for GLIB, overriding pkg-config
+ GLIB_LIBS linker flags for GLIB, overriding pkg-config
+ ALSA_CFLAGS C compiler flags for ALSA, overriding pkg-config
+ ALSA_LIBS linker flags for ALSA, overriding pkg-config
+ GSTREAMER_CFLAGS
+ C compiler flags for GSTREAMER, overriding pkg-config
+ GSTREAMER_LIBS
+ linker flags for GSTREAMER, overriding pkg-config
+ USB_CFLAGS C compiler flags for USB, overriding pkg-config
+ USB_LIBS linker flags for USB, overriding pkg-config
+ SNDFILE_CFLAGS
+ C compiler flags for SNDFILE, overriding pkg-config
+ SNDFILE_LIBS
+ linker flags for SNDFILE, overriding pkg-config
+ CAPNG_CFLAGS
+ C compiler flags for CAPNG, overriding pkg-config
+ CAPNG_LIBS linker flags for CAPNG, overriding pkg-config
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+ # If there are subdirs, report their specific --help.
+ for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+ test -d "$ac_dir" ||
+ { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
+ continue
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+ cd "$ac_dir" || { ac_status=$?; continue; }
+ # Check for guested configure.
+ if test -f "$ac_srcdir/configure.gnu"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+ elif test -f "$ac_srcdir/configure"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure" --help=recursive
+ else
+ $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+ fi || ac_status=$?
+ cd "$ac_pwd" || { ac_status=$?; break; }
+ done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+ cat <<\_ACEOF
+bluez configure 4.91
+generated by GNU Autoconf 2.63
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
+2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+ exit
+fi
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by bluez $as_me 4.91, which was
+generated by GNU Autoconf 2.63. Invocation command line was
+
+ $ $0 $@
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown`
+
+/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown`
+/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown`
+/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ $as_echo "PATH: $as_dir"
+done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+ for ac_arg
+ do
+ case $ac_arg in
+ -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ continue ;;
+ *\'*)
+ ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ case $ac_pass in
+ 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;;
+ 2)
+ ac_configure_args1="$ac_configure_args1 '$ac_arg'"
+ if test $ac_must_keep_next = true; then
+ ac_must_keep_next=false # Got value, back to normal.
+ else
+ case $ac_arg in
+ *=* | --config-cache | -C | -disable-* | --disable-* \
+ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+ | -with-* | --with-* | -without-* | --without-* | --x)
+ case "$ac_configure_args0 " in
+ "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+ esac
+ ;;
+ -* ) ac_must_keep_next=true ;;
+ esac
+ fi
+ ac_configure_args="$ac_configure_args '$ac_arg'"
+ ;;
+ esac
+ done
+done
+$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; }
+$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; }
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log. We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+ # Save into config.log some information that might help in debugging.
+ {
+ echo
+
+ cat <<\_ASBOX
+## ---------------- ##
+## Cache variables. ##
+## ---------------- ##
+_ASBOX
+ echo
+ # The following way of writing the cache mishandles newlines in values,
+(
+ for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { $as_echo "$as_me:$LINENO: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+ *) $as_unset $ac_var ;;
+ esac ;;
+ esac
+ done
+ (set) 2>&1 |
+ case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ sed -n \
+ "s/'\''/'\''\\\\'\'''\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+ ;; #(
+ *)
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+)
+ echo
+
+ cat <<\_ASBOX
+## ----------------- ##
+## Output variables. ##
+## ----------------- ##
+_ASBOX
+ echo
+ for ac_var in $ac_subst_vars
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ $as_echo "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+
+ if test -n "$ac_subst_files"; then
+ cat <<\_ASBOX
+## ------------------- ##
+## File substitutions. ##
+## ------------------- ##
+_ASBOX
+ echo
+ for ac_var in $ac_subst_files
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ $as_echo "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+ fi
+
+ if test -s confdefs.h; then
+ cat <<\_ASBOX
+## ----------- ##
+## confdefs.h. ##
+## ----------- ##
+_ASBOX
+ echo
+ cat confdefs.h
+ echo
+ fi
+ test "$ac_signal" != 0 &&
+ $as_echo "$as_me: caught signal $ac_signal"
+ $as_echo "$as_me: exit $exit_status"
+ } >&5
+ rm -f core *.core core.conftest.* &&
+ rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+ exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+ trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer an explicitly selected file to automatically selected ones.
+ac_site_file1=NONE
+ac_site_file2=NONE
+if test -n "$CONFIG_SITE"; then
+ ac_site_file1=$CONFIG_SITE
+elif test "x$prefix" != xNONE; then
+ ac_site_file1=$prefix/share/config.site
+ ac_site_file2=$prefix/etc/config.site
+else
+ ac_site_file1=$ac_default_prefix/share/config.site
+ ac_site_file2=$ac_default_prefix/etc/config.site
+fi
+for ac_site_file in "$ac_site_file1" "$ac_site_file2"
+do
+ test "x$ac_site_file" = xNONE && continue
+ if test -r "$ac_site_file"; then
+ { $as_echo "$as_me:$LINENO: loading site script $ac_site_file" >&5
+$as_echo "$as_me: loading site script $ac_site_file" >&6;}
+ sed 's/^/| /' "$ac_site_file" >&5
+ . "$ac_site_file"
+ fi
+done
+
+if test -r "$cache_file"; then
+ # Some versions of bash will fail to source /dev/null (special
+ # files actually), so we avoid doing that.
+ if test -f "$cache_file"; then
+ { $as_echo "$as_me:$LINENO: loading cache $cache_file" >&5
+$as_echo "$as_me: loading cache $cache_file" >&6;}
+ case $cache_file in
+ [\\/]* | ?:[\\/]* ) . "$cache_file";;
+ *) . "./$cache_file";;
+ esac
+ fi
+else
+ { $as_echo "$as_me:$LINENO: creating cache $cache_file" >&5
+$as_echo "$as_me: creating cache $cache_file" >&6;}
+ >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+ eval ac_old_set=\$ac_cv_env_${ac_var}_set
+ eval ac_new_set=\$ac_env_${ac_var}_set
+ eval ac_old_val=\$ac_cv_env_${ac_var}_value
+ eval ac_new_val=\$ac_env_${ac_var}_value
+ case $ac_old_set,$ac_new_set in
+ set,)
+ { $as_echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,set)
+ { $as_echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,);;
+ *)
+ if test "x$ac_old_val" != "x$ac_new_val"; then
+ # differences in whitespace do not lead to failure.
+ ac_old_val_w=`echo x $ac_old_val`
+ ac_new_val_w=`echo x $ac_new_val`
+ if test "$ac_old_val_w" != "$ac_new_val_w"; then
+ { $as_echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5
+$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+ ac_cache_corrupted=:
+ else
+ { $as_echo "$as_me:$LINENO: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
+$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+ eval $ac_var=\$ac_old_val
+ fi
+ { $as_echo "$as_me:$LINENO: former value: \`$ac_old_val'" >&5
+$as_echo "$as_me: former value: \`$ac_old_val'" >&2;}
+ { $as_echo "$as_me:$LINENO: current value: \`$ac_new_val'" >&5
+$as_echo "$as_me: current value: \`$ac_new_val'" >&2;}
+ fi;;
+ esac
+ # Pass precious variables to config.status.
+ if test "$ac_new_set" = set; then
+ case $ac_new_val in
+ *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+ *) ac_arg=$ac_var=$ac_new_val ;;
+ esac
+ case " $ac_configure_args " in
+ *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.
+ *) ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+ esac
+ fi
+done
+if $ac_cache_corrupted; then
+ { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+ { $as_echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5
+$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+ { { $as_echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5
+$as_echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+am__api_version='1.11'
+
+ac_aux_dir=
+for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
+ if test -f "$ac_dir/install-sh"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f "$ac_dir/install.sh"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ elif test -f "$ac_dir/shtool"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/shtool install -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ { { $as_echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" >&5
+$as_echo "$as_me: error: cannot find install-sh or install.sh in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+# These three variables are undocumented and unsupported,
+# and are intended to be withdrawn in a future Autoconf release.
+# They can cause serious problems if a builder's source tree is in a directory
+# whose full name contains unusual characters.
+ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var.
+ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var.
+ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var.
+
+
+# Find a good install program. We prefer a C program (faster),
+# so one script is as good as another. But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+# Reject install programs that cannot install multiple files.
+{ $as_echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5
+$as_echo_n "checking for a BSD-compatible install... " >&6; }
+if test -z "$INSTALL"; then
+if test "${ac_cv_path_install+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in
+ ./ | .// | /cC/* | \
+ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+ ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \
+ /usr/ucb/* ) ;;
+ *)
+ # OSF1 and SCO ODT 3.0 have their own names for install.
+ # Don't use installbsd from OSF since it installs stuff as root
+ # by default.
+ for ac_prog in ginstall scoinst install; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then
+ if test $ac_prog = install &&
+ grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # AIX install. It has an incompatible calling convention.
+ :
+ elif test $ac_prog = install &&
+ grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # program-specific install script used by HP pwplus--don't use.
+ :
+ else
+ rm -rf conftest.one conftest.two conftest.dir
+ echo one > conftest.one
+ echo two > conftest.two
+ mkdir conftest.dir
+ if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" &&
+ test -s conftest.one && test -s conftest.two &&
+ test -s conftest.dir/conftest.one &&
+ test -s conftest.dir/conftest.two
+ then
+ ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+ break 3
+ fi
+ fi
+ fi
+ done
+ done
+ ;;
+esac
+
+done
+IFS=$as_save_IFS
+
+rm -rf conftest.one conftest.two conftest.dir
+
+fi
+ if test "${ac_cv_path_install+set}" = set; then
+ INSTALL=$ac_cv_path_install
+ else
+ # As a last resort, use the slow shell script. Don't cache a
+ # value for INSTALL within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the value is a relative name.
+ INSTALL=$ac_install_sh
+ fi
+fi
+{ $as_echo "$as_me:$LINENO: result: $INSTALL" >&5
+$as_echo "$INSTALL" >&6; }
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+{ $as_echo "$as_me:$LINENO: checking whether build environment is sane" >&5
+$as_echo_n "checking whether build environment is sane... " >&6; }
+# Just in case
+sleep 1
+echo timestamp > conftest.file
+# Reject unsafe characters in $srcdir or the absolute working directory
+# name. Accept space and tab only in the latter.
+am_lf='
+'
+case `pwd` in
+ *[\\\"\#\$\&\'\`$am_lf]*)
+ { { $as_echo "$as_me:$LINENO: error: unsafe absolute working directory name" >&5
+$as_echo "$as_me: error: unsafe absolute working directory name" >&2;}
+ { (exit 1); exit 1; }; };;
+esac
+case $srcdir in
+ *[\\\"\#\$\&\'\`$am_lf\ \ ]*)
+ { { $as_echo "$as_me:$LINENO: error: unsafe srcdir value: \`$srcdir'" >&5
+$as_echo "$as_me: error: unsafe srcdir value: \`$srcdir'" >&2;}
+ { (exit 1); exit 1; }; };;
+esac
+
+# Do `set' in a subshell so we don't clobber the current shell's
+# arguments. Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+ set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
+ if test "$*" = "X"; then
+ # -L didn't work.
+ set X `ls -t "$srcdir/configure" conftest.file`
+ fi
+ rm -f conftest.file
+ if test "$*" != "X $srcdir/configure conftest.file" \
+ && test "$*" != "X conftest.file $srcdir/configure"; then
+
+ # If neither matched, then we have a broken ls. This can happen
+ # if, for instance, CONFIG_SHELL is bash and it inherits a
+ # broken ls alias from the environment. This has actually
+ # happened. Such a system could not be considered "sane".
+ { { $as_echo "$as_me:$LINENO: error: ls -t appears to fail. Make sure there is not a broken
+alias in your environment" >&5
+$as_echo "$as_me: error: ls -t appears to fail. Make sure there is not a broken
+alias in your environment" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+
+ test "$2" = conftest.file
+ )
+then
+ # Ok.
+ :
+else
+ { { $as_echo "$as_me:$LINENO: error: newly created file is older than distributed files!
+Check your system clock" >&5
+$as_echo "$as_me: error: newly created file is older than distributed files!
+Check your system clock" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+{ $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+test "$program_prefix" != NONE &&
+ program_transform_name="s&^&$program_prefix&;$program_transform_name"
+# Use a double $ so make ignores it.
+test "$program_suffix" != NONE &&
+ program_transform_name="s&\$&$program_suffix&;$program_transform_name"
+# Double any \ or $.
+# By default was `s,x,x', remove it if useless.
+ac_script='s/[\\$]/&&/g;s/;s,x,x,$//'
+program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"`
+
+# expand $ac_aux_dir to an absolute path
+am_aux_dir=`cd $ac_aux_dir && pwd`
+
+if test x"${MISSING+set}" != xset; then
+ case $am_aux_dir in
+ *\ * | *\ *)
+ MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
+ *)
+ MISSING="\${SHELL} $am_aux_dir/missing" ;;
+ esac
+fi
+# Use eval to expand $SHELL
+if eval "$MISSING --run true"; then
+ am_missing_run="$MISSING --run "
+else
+ am_missing_run=
+ { $as_echo "$as_me:$LINENO: WARNING: \`missing' script is too old or missing" >&5
+$as_echo "$as_me: WARNING: \`missing' script is too old or missing" >&2;}
+fi
+
+if test x"${install_sh}" != xset; then
+ case $am_aux_dir in
+ *\ * | *\ *)
+ install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
+ *)
+ install_sh="\${SHELL} $am_aux_dir/install-sh"
+ esac
+fi
+
+# Installed binaries are usually stripped using `strip' when the user
+# run `make install-strip'. However `strip' might not be the right
+# tool to use in cross-compilation environments, therefore Automake
+# will honor the `STRIP' environment variable to overrule this program.
+if test "$cross_compiling" != no; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
+set dummy ${ac_tool_prefix}strip; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_STRIP+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$STRIP"; then
+ ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_STRIP="${ac_tool_prefix}strip"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+STRIP=$ac_cv_prog_STRIP
+if test -n "$STRIP"; then
+ { $as_echo "$as_me:$LINENO: result: $STRIP" >&5
+$as_echo "$STRIP" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_STRIP"; then
+ ac_ct_STRIP=$STRIP
+ # Extract the first word of "strip", so it can be a program name with args.
+set dummy strip; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_STRIP"; then
+ ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_STRIP="strip"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
+if test -n "$ac_ct_STRIP"; then
+ { $as_echo "$as_me:$LINENO: result: $ac_ct_STRIP" >&5
+$as_echo "$ac_ct_STRIP" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_STRIP" = x; then
+ STRIP=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ STRIP=$ac_ct_STRIP
+ fi
+else
+ STRIP="$ac_cv_prog_STRIP"
+fi
+
+fi
+INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
+
+{ $as_echo "$as_me:$LINENO: checking for a thread-safe mkdir -p" >&5
+$as_echo_n "checking for a thread-safe mkdir -p... " >&6; }
+if test -z "$MKDIR_P"; then
+ if test "${ac_cv_path_mkdir+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in mkdir gmkdir; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; } || continue
+ case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #(
+ 'mkdir (GNU coreutils) '* | \
+ 'mkdir (coreutils) '* | \
+ 'mkdir (fileutils) '4.1*)
+ ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext
+ break 3;;
+ esac
+ done
+ done
+done
+IFS=$as_save_IFS
+
+fi
+
+ if test "${ac_cv_path_mkdir+set}" = set; then
+ MKDIR_P="$ac_cv_path_mkdir -p"
+ else
+ # As a last resort, use the slow shell script. Don't cache a
+ # value for MKDIR_P within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the value is a relative name.
+ test -d ./--version && rmdir ./--version
+ MKDIR_P="$ac_install_sh -d"
+ fi
+fi
+{ $as_echo "$as_me:$LINENO: result: $MKDIR_P" >&5
+$as_echo "$MKDIR_P" >&6; }
+
+mkdir_p="$MKDIR_P"
+case $mkdir_p in
+ [\\/$]* | ?:[\\/]*) ;;
+ */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;;
+esac
+
+for ac_prog in gawk mawk nawk awk
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_AWK+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$AWK"; then
+ ac_cv_prog_AWK="$AWK" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_AWK="$ac_prog"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+AWK=$ac_cv_prog_AWK
+if test -n "$AWK"; then
+ { $as_echo "$as_me:$LINENO: result: $AWK" >&5
+$as_echo "$AWK" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$AWK" && break
+done
+
+{ $as_echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5
+$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; }
+set x ${MAKE-make}
+ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'`
+if { as_var=ac_cv_prog_make_${ac_make}_set; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.make <<\_ACEOF
+SHELL = /bin/sh
+all:
+ @echo '@@@%%%=$(MAKE)=@@@%%%'
+_ACEOF
+# GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+case `${MAKE-make} -f conftest.make 2>/dev/null` in
+ *@@@%%%=?*=@@@%%%*)
+ eval ac_cv_prog_make_${ac_make}_set=yes;;
+ *)
+ eval ac_cv_prog_make_${ac_make}_set=no;;
+esac
+rm -f conftest.make
+fi
+if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then
+ { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+ SET_MAKE=
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+ SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+rm -rf .tst 2>/dev/null
+mkdir .tst 2>/dev/null
+if test -d .tst; then
+ am__leading_dot=.
+else
+ am__leading_dot=_
+fi
+rmdir .tst 2>/dev/null
+
+if test "`cd $srcdir && pwd`" != "`pwd`"; then
+ # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
+ # is not polluted with repeated "-I."
+ am__isrc=' -I$(srcdir)'
+ # test to see if srcdir already configured
+ if test -f $srcdir/config.status; then
+ { { $as_echo "$as_me:$LINENO: error: source directory already configured; run \"make distclean\" there first" >&5
+$as_echo "$as_me: error: source directory already configured; run \"make distclean\" there first" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+fi
+
+# test whether we have cygpath
+if test -z "$CYGPATH_W"; then
+ if (cygpath --version) >/dev/null 2>/dev/null; then
+ CYGPATH_W='cygpath -w'
+ else
+ CYGPATH_W=echo
+ fi
+fi
+
+
+# Define the identity of the package.
+ PACKAGE='bluez'
+ VERSION='4.91'
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE "$PACKAGE"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define VERSION "$VERSION"
+_ACEOF
+
+# Some tools Automake needs.
+
+ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"}
+
+
+AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"}
+
+
+AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"}
+
+
+AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"}
+
+
+MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"}
+
+# We need awk for the "check" target. The system "awk" is bad on
+# some platforms.
+# Always define AMTAR for backward compatibility.
+
+AMTAR=${AMTAR-"${am_missing_run}tar"}
+
+am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'
+
+
+
+
+
+ac_config_headers="$ac_config_headers config.h"
+
+
+# Check whether --enable-silent-rules was given.
+if test "${enable_silent_rules+set}" = set; then
+ enableval=$enable_silent_rules;
+fi
+
+case $enable_silent_rules in
+yes) AM_DEFAULT_VERBOSITY=0;;
+no) AM_DEFAULT_VERBOSITY=1;;
+*) AM_DEFAULT_VERBOSITY=0;;
+esac
+AM_BACKSLASH='\'
+
+
+
+{ $as_echo "$as_me:$LINENO: checking whether to enable maintainer-specific portions of Makefiles" >&5
+$as_echo_n "checking whether to enable maintainer-specific portions of Makefiles... " >&6; }
+ # Check whether --enable-maintainer-mode was given.
+if test "${enable_maintainer_mode+set}" = set; then
+ enableval=$enable_maintainer_mode; USE_MAINTAINER_MODE=$enableval
+else
+ USE_MAINTAINER_MODE=no
+fi
+
+ { $as_echo "$as_me:$LINENO: result: $USE_MAINTAINER_MODE" >&5
+$as_echo "$USE_MAINTAINER_MODE" >&6; }
+ if test $USE_MAINTAINER_MODE = yes; then
+ MAINTAINER_MODE_TRUE=
+ MAINTAINER_MODE_FALSE='#'
+else
+ MAINTAINER_MODE_TRUE='#'
+ MAINTAINER_MODE_FALSE=
+fi
+
+ MAINT=$MAINTAINER_MODE_TRUE
+
+
+
+
+
+if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args.
+set dummy ${ac_tool_prefix}pkg-config; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_path_PKG_CONFIG+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ case $PKG_CONFIG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+PKG_CONFIG=$ac_cv_path_PKG_CONFIG
+if test -n "$PKG_CONFIG"; then
+ { $as_echo "$as_me:$LINENO: result: $PKG_CONFIG" >&5
+$as_echo "$PKG_CONFIG" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_path_PKG_CONFIG"; then
+ ac_pt_PKG_CONFIG=$PKG_CONFIG
+ # Extract the first word of "pkg-config", so it can be a program name with args.
+set dummy pkg-config; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_path_ac_pt_PKG_CONFIG+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ case $ac_pt_PKG_CONFIG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG
+if test -n "$ac_pt_PKG_CONFIG"; then
+ { $as_echo "$as_me:$LINENO: result: $ac_pt_PKG_CONFIG" >&5
+$as_echo "$ac_pt_PKG_CONFIG" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_pt_PKG_CONFIG" = x; then
+ PKG_CONFIG=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ PKG_CONFIG=$ac_pt_PKG_CONFIG
+ fi
+else
+ PKG_CONFIG="$ac_cv_path_PKG_CONFIG"
+fi
+
+fi
+if test -n "$PKG_CONFIG"; then
+ _pkg_min_version=0.9.0
+ { $as_echo "$as_me:$LINENO: checking pkg-config is at least version $_pkg_min_version" >&5
+$as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; }
+ if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
+ { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+ else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+ PKG_CONFIG=""
+ fi
+
+fi
+
+
+
+
+ if (test "${prefix}" = "NONE"); then
+ if (test "$sysconfdir" = '${prefix}/etc'); then
+ sysconfdir='/etc'
+
+ fi
+
+ if (test "$localstatedir" = '${prefix}/var'); then
+ localstatedir='/var'
+
+ fi
+
+ if (test "$libexecdir" = '${exec_prefix}/libexec'); then
+ libexecdir='/lib'
+
+ fi
+
+ if (test "$mandir" = '${prefix}/man'); then
+ mandir='${prefix}/share/man'
+
+ fi
+
+ prefix="${ac_default_prefix}"
+ fi
+
+ if (test "${libdir}" = '${exec_prefix}/lib'); then
+ libdir="${prefix}/lib"
+ fi
+
+ plugindir="${libdir}/bluetooth/plugins"
+
+ if (test "$sysconfdir" = '${prefix}/etc'); then
+ configdir="${prefix}/etc/bluetooth"
+ else
+ configdir="${sysconfdir}/bluetooth"
+ fi
+
+ if (test "$localstatedir" = '${prefix}/var'); then
+ storagedir="${prefix}/var/lib/bluetooth"
+ else
+ storagedir="${localstatedir}/lib/bluetooth"
+ fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define CONFIGDIR "${configdir}"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define STORAGEDIR "${storagedir}"
+_ACEOF
+
+
+ CONFIGDIR="${configdir}"
+
+ STORAGEDIR="${storagedir}"
+
+
+ UDEV_DATADIR="`$PKG_CONFIG --variable=udevdir udev`"
+ if (test -z "${UDEV_DATADIR}"); then
+ UDEV_DATADIR="${sysconfdir}/udev/rules.d"
+ else
+ UDEV_DATADIR="${UDEV_DATADIR}/rules.d"
+ fi
+
+
+
+
+ if (test "${CFLAGS}" = ""); then
+ CFLAGS="-Wall -O2"
+ fi
+ if (test "$USE_MAINTAINER_MODE" = "yes"); then
+ CFLAGS="$CFLAGS -Werror -Wextra"
+ CFLAGS="$CFLAGS -Wno-unused-parameter"
+ CFLAGS="$CFLAGS -Wno-missing-field-initializers"
+ CFLAGS="$CFLAGS -Wdeclaration-after-statement"
+ CFLAGS="$CFLAGS -Wmissing-declarations"
+ CFLAGS="$CFLAGS -Wredundant-decls"
+ CFLAGS="$CFLAGS -Wcast-align"
+ CFLAGS="$CFLAGS -DG_DISABLE_DEPRECATED"
+ fi
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="${ac_tool_prefix}gcc"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:$LINENO: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_CC="gcc"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="${ac_tool_prefix}cc"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:$LINENO: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ fi
+fi
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# != 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+ fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:$LINENO: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in cl.exe
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:$LINENO: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$CC" && break
+ done
+fi
+if test -z "$CC"; then
+ ac_ct_CC=$CC
+ for ac_prog in cl.exe
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_CC="$ac_prog"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$ac_ct_CC" && break
+done
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+fi
+
+fi
+
+
+test -z "$CC" && { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { $as_echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&5
+$as_echo "$as_me: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }; }
+
+# Provide some information about the compiler.
+$as_echo "$as_me:$LINENO: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+{ (ac_try="$ac_compiler --version >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compiler --version >&5") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+{ (ac_try="$ac_compiler -v >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compiler -v >&5") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+{ (ac_try="$ac_compiler -V >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compiler -V >&5") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ $as_echo "$as_me:$LINENO: checking for C compiler default output file name" >&5
+$as_echo_n "checking for C compiler default output file name... " >&6; }
+ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+
+# The possible output files:
+ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
+
+ac_rmfiles=
+for ac_file in $ac_files
+do
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+ * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+ esac
+done
+rm -f $ac_rmfiles
+
+if { (ac_try="$ac_link_default"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link_default") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile. We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
+ ;;
+ [ab].out )
+ # We found the default executable, but exeext='' is most
+ # certainly right.
+ break;;
+ *.* )
+ if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+ then :; else
+ ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ fi
+ # We set ac_cv_exeext here because the later test for it is not
+ # safe: cross compilers may not add the suffix if given an `-o'
+ # argument, so we may need to know it at that point already.
+ # Even if this section looks crufty: it has the advantage of
+ # actually working.
+ break;;
+ * )
+ break;;
+ esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else
+ ac_file=''
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $ac_file" >&5
+$as_echo "$ac_file" >&6; }
+if test -z "$ac_file"; then
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { $as_echo "$as_me:$LINENO: error: C compiler cannot create executables
+See \`config.log' for more details." >&5
+$as_echo "$as_me: error: C compiler cannot create executables
+See \`config.log' for more details." >&2;}
+ { (exit 77); exit 77; }; }; }
+fi
+
+ac_exeext=$ac_cv_exeext
+
+# Check that the compiler produces executables we can run. If not, either
+# the compiler is broken, or we cross compile.
+{ $as_echo "$as_me:$LINENO: checking whether the C compiler works" >&5
+$as_echo_n "checking whether the C compiler works... " >&6; }
+# FIXME: These cross compiler hacks should be removed for Autoconf 3.0
+# If not cross compiling, check that we can run a simple program.
+if test "$cross_compiling" != yes; then
+ if { ac_try='./$ac_file'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ cross_compiling=no
+ else
+ if test "$cross_compiling" = maybe; then
+ cross_compiling=yes
+ else
+ { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { $as_echo "$as_me:$LINENO: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&5
+$as_echo "$as_me: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }; }
+ fi
+ fi
+fi
+{ $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+
+rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+# Check that the compiler produces executables we can run. If not, either
+# the compiler is broken, or we cross compile.
+{ $as_echo "$as_me:$LINENO: checking whether we are cross compiling" >&5
+$as_echo_n "checking whether we are cross compiling... " >&6; }
+{ $as_echo "$as_me:$LINENO: result: $cross_compiling" >&5
+$as_echo "$cross_compiling" >&6; }
+
+{ $as_echo "$as_me:$LINENO: checking for suffix of executables" >&5
+$as_echo_n "checking for suffix of executables... " >&6; }
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+ *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ break;;
+ * ) break;;
+ esac
+done
+else
+ { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { $as_echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&5
+$as_echo "$as_me: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }; }
+fi
+
+rm -f conftest$ac_cv_exeext
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5
+$as_echo "$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+{ $as_echo "$as_me:$LINENO: checking for suffix of object files" >&5
+$as_echo_n "checking for suffix of object files... " >&6; }
+if test "${ac_cv_objext+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ for ac_file in conftest.o conftest.obj conftest.*; do
+ test -f "$ac_file" || continue;
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
+ *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+ break;;
+ esac
+done
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { $as_echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&5
+$as_echo "$as_me: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }; }
+fi
+
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_objext" >&5
+$as_echo "$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ $as_echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5
+$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
+if test "${ac_cv_c_compiler_gnu+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+#ifndef __GNUC__
+ choke me
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_compiler_gnu=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_compiler_gnu=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5
+$as_echo "$ac_cv_c_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+ GCC=yes
+else
+ GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ $as_echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5
+$as_echo_n "checking whether $CC accepts -g... " >&6; }
+if test "${ac_cv_prog_cc_g+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_save_c_werror_flag=$ac_c_werror_flag
+ ac_c_werror_flag=yes
+ ac_cv_prog_cc_g=no
+ CFLAGS="-g"
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_prog_cc_g=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ CFLAGS=""
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ :
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_c_werror_flag=$ac_save_c_werror_flag
+ CFLAGS="-g"
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_prog_cc_g=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5
+$as_echo "$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+ CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+{ $as_echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if test "${ac_cv_prog_cc_c89+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+ char **p;
+ int i;
+{
+ return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+ char *s;
+ va_list v;
+ va_start (v,p);
+ s = g (p, va_arg (v,int));
+ va_end (v);
+ return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
+ function prototypes and stuff, but not '\xHH' hex character constants.
+ These don't provoke an error unfortunately, instead are silently treated
+ as 'x'. The following induces an error, until -std is added to get
+ proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
+ array size at least. It's necessary to write '\x00'==0 to get something
+ that's true only with -std. */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+ inside strings and character constants. */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
+ ;
+ return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+ CC="$ac_save_CC $ac_arg"
+ rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_prog_cc_c89=$ac_arg
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext
+ test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+ x)
+ { $as_echo "$as_me:$LINENO: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+ xno)
+ { $as_echo "$as_me:$LINENO: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+ *)
+ CC="$CC $ac_cv_prog_cc_c89"
+ { $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+DEPDIR="${am__leading_dot}deps"
+
+ac_config_commands="$ac_config_commands depfiles"
+
+
+am_make=${MAKE-make}
+cat > confinc << 'END'
+am__doit:
+ @echo this is the am__doit target
+.PHONY: am__doit
+END
+# If we don't find an include directive, just comment out the code.
+{ $as_echo "$as_me:$LINENO: checking for style of include used by $am_make" >&5
+$as_echo_n "checking for style of include used by $am_make... " >&6; }
+am__include="#"
+am__quote=
+_am_result=none
+# First try GNU make style include.
+echo "include confinc" > confmf
+# Ignore all kinds of additional output from `make'.
+case `$am_make -s -f confmf 2> /dev/null` in #(
+*the\ am__doit\ target*)
+ am__include=include
+ am__quote=
+ _am_result=GNU
+ ;;
+esac
+# Now try BSD make style include.
+if test "$am__include" = "#"; then
+ echo '.include "confinc"' > confmf
+ case `$am_make -s -f confmf 2> /dev/null` in #(
+ *the\ am__doit\ target*)
+ am__include=.include
+ am__quote="\""
+ _am_result=BSD
+ ;;
+ esac
+fi
+
+
+{ $as_echo "$as_me:$LINENO: result: $_am_result" >&5
+$as_echo "$_am_result" >&6; }
+rm -f confinc confmf
+
+# Check whether --enable-dependency-tracking was given.
+if test "${enable_dependency_tracking+set}" = set; then
+ enableval=$enable_dependency_tracking;
+fi
+
+if test "x$enable_dependency_tracking" != xno; then
+ am_depcomp="$ac_aux_dir/depcomp"
+ AMDEPBACKSLASH='\'
+fi
+ if test "x$enable_dependency_tracking" != xno; then
+ AMDEP_TRUE=
+ AMDEP_FALSE='#'
+else
+ AMDEP_TRUE='#'
+ AMDEP_FALSE=
+fi
+
+
+
+depcc="$CC" am_compiler_list=
+
+{ $as_echo "$as_me:$LINENO: checking dependency style of $depcc" >&5
+$as_echo_n "checking dependency style of $depcc... " >&6; }
+if test "${am_cv_CC_dependencies_compiler_type+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+ # We make a subdir and do the tests there. Otherwise we can end up
+ # making bogus files that we don't know about and never remove. For
+ # instance it was reported that on HP-UX the gcc test will end up
+ # making a dummy file named `D' -- because `-MD' means `put the output
+ # in D'.
+ mkdir conftest.dir
+ # Copy depcomp to subdir because otherwise we won't find it if we're
+ # using a relative directory.
+ cp "$am_depcomp" conftest.dir
+ cd conftest.dir
+ # We will build objects and dependencies in a subdirectory because
+ # it helps to detect inapplicable dependency modes. For instance
+ # both Tru64's cc and ICC support -MD to output dependencies as a
+ # side effect of compilation, but ICC will put the dependencies in
+ # the current directory while Tru64 will put them in the object
+ # directory.
+ mkdir sub
+
+ am_cv_CC_dependencies_compiler_type=none
+ if test "$am_compiler_list" = ""; then
+ am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp`
+ fi
+ am__universal=false
+ case " $depcc " in #(
+ *\ -arch\ *\ -arch\ *) am__universal=true ;;
+ esac
+
+ for depmode in $am_compiler_list; do
+ # Setup a source with many dependencies, because some compilers
+ # like to wrap large dependency lists on column 80 (with \), and
+ # we should not choose a depcomp mode which is confused by this.
+ #
+ # We need to recreate these files for each test, as the compiler may
+ # overwrite some of them when testing with obscure command lines.
+ # This happens at least with the AIX C compiler.
+ : > sub/conftest.c
+ for i in 1 2 3 4 5 6; do
+ echo '#include "conftst'$i'.h"' >> sub/conftest.c
+ # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
+ # Solaris 8's {/usr,}/bin/sh.
+ touch sub/conftst$i.h
+ done
+ echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+ # We check with `-c' and `-o' for the sake of the "dashmstdout"
+ # mode. It turns out that the SunPro C++ compiler does not properly
+ # handle `-M -o', and we need to detect this. Also, some Intel
+ # versions had trouble with output in subdirs
+ am__obj=sub/conftest.${OBJEXT-o}
+ am__minus_obj="-o $am__obj"
+ case $depmode in
+ gcc)
+ # This depmode causes a compiler race in universal mode.
+ test "$am__universal" = false || continue
+ ;;
+ nosideeffect)
+ # after this tag, mechanisms are not by side-effect, so they'll
+ # only be used when explicitly requested
+ if test "x$enable_dependency_tracking" = xyes; then
+ continue
+ else
+ break
+ fi
+ ;;
+ msvisualcpp | msvcmsys)
+ # This compiler won't grok `-c -o', but also, the minuso test has
+ # not run yet. These depmodes are late enough in the game, and
+ # so weak that their functioning should not be impacted.
+ am__obj=conftest.${OBJEXT-o}
+ am__minus_obj=
+ ;;
+ none) break ;;
+ esac
+ if depmode=$depmode \
+ source=sub/conftest.c object=$am__obj \
+ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
+ >/dev/null 2>conftest.err &&
+ grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
+ ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+ # icc doesn't choke on unknown options, it will just issue warnings
+ # or remarks (even with -Werror). So we grep stderr for any message
+ # that says an option was ignored or not supported.
+ # When given -MP, icc 7.0 and 7.1 complain thusly:
+ # icc: Command line warning: ignoring option '-M'; no argument required
+ # The diagnosis changed in icc 8.0:
+ # icc: Command line remark: option '-MP' not supported
+ if (grep 'ignoring option' conftest.err ||
+ grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+ am_cv_CC_dependencies_compiler_type=$depmode
+ break
+ fi
+ fi
+ done
+
+ cd ..
+ rm -rf conftest.dir
+else
+ am_cv_CC_dependencies_compiler_type=none
+fi
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $am_cv_CC_dependencies_compiler_type" >&5
+$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; }
+CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type
+
+ if
+ test "x$enable_dependency_tracking" != xno \
+ && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then
+ am__fastdepCC_TRUE=
+ am__fastdepCC_FALSE='#'
+else
+ am__fastdepCC_TRUE='#'
+ am__fastdepCC_FALSE=
+fi
+
+
+if test "x$CC" != xcc; then
+ { $as_echo "$as_me:$LINENO: checking whether $CC and cc understand -c and -o together" >&5
+$as_echo_n "checking whether $CC and cc understand -c and -o together... " >&6; }
+else
+ { $as_echo "$as_me:$LINENO: checking whether cc understands -c and -o together" >&5
+$as_echo_n "checking whether cc understands -c and -o together... " >&6; }
+fi
+set dummy $CC; ac_cc=`$as_echo "$2" |
+ sed 's/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/'`
+if { as_var=ac_cv_prog_cc_${ac_cc}_c_o; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+# Make sure it works both with $CC and with simple cc.
+# We do the test twice because some compilers refuse to overwrite an
+# existing .o file with -o, though they will create one.
+ac_try='$CC -c conftest.$ac_ext -o conftest2.$ac_objext >&5'
+rm -f conftest2.*
+if { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ test -f conftest2.$ac_objext && { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); };
+then
+ eval ac_cv_prog_cc_${ac_cc}_c_o=yes
+ if test "x$CC" != xcc; then
+ # Test first that cc exists at all.
+ if { ac_try='cc -c conftest.$ac_ext >&5'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_try='cc -c conftest.$ac_ext -o conftest2.$ac_objext >&5'
+ rm -f conftest2.*
+ if { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ test -f conftest2.$ac_objext && { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); };
+ then
+ # cc works too.
+ :
+ else
+ # cc exists but doesn't like -o.
+ eval ac_cv_prog_cc_${ac_cc}_c_o=no
+ fi
+ fi
+ fi
+else
+ eval ac_cv_prog_cc_${ac_cc}_c_o=no
+fi
+rm -f core conftest*
+
+fi
+if eval test \$ac_cv_prog_cc_${ac_cc}_c_o = yes; then
+ { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+
+cat >>confdefs.h <<\_ACEOF
+#define NO_MINUS_C_MINUS_O 1
+_ACEOF
+
+fi
+
+# FIXME: we rely on the cache variable name because
+# there is no other way.
+set dummy $CC
+am_cc=`echo $2 | sed 's/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/'`
+eval am_t=\$ac_cv_prog_cc_${am_cc}_c_o
+if test "$am_t" != yes; then
+ # Losing compiler, so override with the script.
+ # FIXME: It is wrong to rewrite CC.
+ # But if we don't then we get into trouble of one sort or another.
+ # A longer-term fix would be to have automake use am__CC in this case,
+ # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)"
+ CC="$am_aux_dir/compile $CC"
+fi
+
+
+
+ { $as_echo "$as_me:$LINENO: checking whether ${CC-cc} accepts -fPIE" >&5
+$as_echo_n "checking whether ${CC-cc} accepts -fPIE... " >&6; }
+if test "${ac_cv_prog_cc_pie+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+
+ echo 'void f(){}' > conftest.c
+ if test -z "`${CC-cc} -fPIE -pie -c conftest.c 2>&1`"; then
+ ac_cv_prog_cc_pie=yes
+ else
+ ac_cv_prog_cc_pie=no
+ fi
+ rm -rf conftest*
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_pie" >&5
+$as_echo "$ac_cv_prog_cc_pie" >&6; }
+
+# Find a good install program. We prefer a C program (faster),
+# so one script is as good as another. But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+# Reject install programs that cannot install multiple files.
+{ $as_echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5
+$as_echo_n "checking for a BSD-compatible install... " >&6; }
+if test -z "$INSTALL"; then
+if test "${ac_cv_path_install+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in
+ ./ | .// | /cC/* | \
+ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+ ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \
+ /usr/ucb/* ) ;;
+ *)
+ # OSF1 and SCO ODT 3.0 have their own names for install.
+ # Don't use installbsd from OSF since it installs stuff as root
+ # by default.
+ for ac_prog in ginstall scoinst install; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then
+ if test $ac_prog = install &&
+ grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # AIX install. It has an incompatible calling convention.
+ :
+ elif test $ac_prog = install &&
+ grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # program-specific install script used by HP pwplus--don't use.
+ :
+ else
+ rm -rf conftest.one conftest.two conftest.dir
+ echo one > conftest.one
+ echo two > conftest.two
+ mkdir conftest.dir
+ if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" &&
+ test -s conftest.one && test -s conftest.two &&
+ test -s conftest.dir/conftest.one &&
+ test -s conftest.dir/conftest.two
+ then
+ ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+ break 3
+ fi
+ fi
+ fi
+ done
+ done
+ ;;
+esac
+
+done
+IFS=$as_save_IFS
+
+rm -rf conftest.one conftest.two conftest.dir
+
+fi
+ if test "${ac_cv_path_install+set}" = set; then
+ INSTALL=$ac_cv_path_install
+ else
+ # As a last resort, use the slow shell script. Don't cache a
+ # value for INSTALL within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the value is a relative name.
+ INSTALL=$ac_install_sh
+ fi
+fi
+{ $as_echo "$as_me:$LINENO: result: $INSTALL" >&5
+$as_echo "$INSTALL" >&6; }
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+for ac_prog in 'bison -y' byacc
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_YACC+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$YACC"; then
+ ac_cv_prog_YACC="$YACC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_YACC="$ac_prog"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+YACC=$ac_cv_prog_YACC
+if test -n "$YACC"; then
+ { $as_echo "$as_me:$LINENO: result: $YACC" >&5
+$as_echo "$YACC" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$YACC" && break
+done
+test -n "$YACC" || YACC="yacc"
+
+
+for ac_prog in flex lex
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_LEX+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$LEX"; then
+ ac_cv_prog_LEX="$LEX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_LEX="$ac_prog"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+LEX=$ac_cv_prog_LEX
+if test -n "$LEX"; then
+ { $as_echo "$as_me:$LINENO: result: $LEX" >&5
+$as_echo "$LEX" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$LEX" && break
+done
+test -n "$LEX" || LEX=":"
+
+if test "x$LEX" != "x:"; then
+ cat >conftest.l <<_ACEOF
+%%
+a { ECHO; }
+b { REJECT; }
+c { yymore (); }
+d { yyless (1); }
+e { yyless (input () != 0); }
+f { unput (yytext[0]); }
+. { BEGIN INITIAL; }
+%%
+#ifdef YYTEXT_POINTER
+extern char *yytext;
+#endif
+int
+main (void)
+{
+ return ! yylex () + ! yywrap ();
+}
+_ACEOF
+{ (ac_try="$LEX conftest.l"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$LEX conftest.l") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+{ $as_echo "$as_me:$LINENO: checking lex output file root" >&5
+$as_echo_n "checking lex output file root... " >&6; }
+if test "${ac_cv_prog_lex_root+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+
+if test -f lex.yy.c; then
+ ac_cv_prog_lex_root=lex.yy
+elif test -f lexyy.c; then
+ ac_cv_prog_lex_root=lexyy
+else
+ { { $as_echo "$as_me:$LINENO: error: cannot find output from $LEX; giving up" >&5
+$as_echo "$as_me: error: cannot find output from $LEX; giving up" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_prog_lex_root" >&5
+$as_echo "$ac_cv_prog_lex_root" >&6; }
+LEX_OUTPUT_ROOT=$ac_cv_prog_lex_root
+
+if test -z "${LEXLIB+set}"; then
+ { $as_echo "$as_me:$LINENO: checking lex library" >&5
+$as_echo_n "checking lex library... " >&6; }
+if test "${ac_cv_lib_lex+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+
+ ac_save_LIBS=$LIBS
+ ac_cv_lib_lex='none needed'
+ for ac_lib in '' -lfl -ll; do
+ LIBS="$ac_lib $ac_save_LIBS"
+ cat >conftest.$ac_ext <<_ACEOF
+`cat $LEX_OUTPUT_ROOT.c`
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_lib_lex=$ac_lib
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+ test "$ac_cv_lib_lex" != 'none needed' && break
+ done
+ LIBS=$ac_save_LIBS
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_lex" >&5
+$as_echo "$ac_cv_lib_lex" >&6; }
+ test "$ac_cv_lib_lex" != 'none needed' && LEXLIB=$ac_cv_lib_lex
+fi
+
+
+{ $as_echo "$as_me:$LINENO: checking whether yytext is a pointer" >&5
+$as_echo_n "checking whether yytext is a pointer... " >&6; }
+if test "${ac_cv_prog_lex_yytext_pointer+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ # POSIX says lex can declare yytext either as a pointer or an array; the
+# default is implementation-dependent. Figure out which it is, since
+# not all implementations provide the %pointer and %array declarations.
+ac_cv_prog_lex_yytext_pointer=no
+ac_save_LIBS=$LIBS
+LIBS="$LEXLIB $ac_save_LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+#define YYTEXT_POINTER 1
+`cat $LEX_OUTPUT_ROOT.c`
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_prog_lex_yytext_pointer=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_save_LIBS
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_prog_lex_yytext_pointer" >&5
+$as_echo "$ac_cv_prog_lex_yytext_pointer" >&6; }
+if test $ac_cv_prog_lex_yytext_pointer = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define YYTEXT_POINTER 1
+_ACEOF
+
+fi
+rm -f conftest.l $LEX_OUTPUT_ROOT.c
+
+fi
+if test "$LEX" = :; then
+ LEX=${am_missing_run}flex
+fi
+mkdir_p="$MKDIR_P"
+case $mkdir_p in
+ [\\/$]* | ?:[\\/]*) ;;
+ */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;;
+esac
+
+
+
+
+
+# Check whether --enable-static was given.
+if test "${enable_static+set}" = set; then
+ enableval=$enable_static; p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_static=yes ;;
+ no) enable_static=no ;;
+ *)
+ enable_static=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ for pkg in $enableval; do
+ IFS="$lt_save_ifs"
+ if test "X$pkg" = "X$p"; then
+ enable_static=yes
+ fi
+ done
+ IFS="$lt_save_ifs"
+ ;;
+ esac
+else
+ enable_static=no
+fi
+
+
+
+
+
+
+
+
+
+case `pwd` in
+ *\ * | *\ *)
+ { $as_echo "$as_me:$LINENO: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5
+$as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;;
+esac
+
+
+
+macro_version='2.2.6b'
+macro_revision='1.3017'
+
+
+
+
+
+
+
+
+
+
+
+
+
+ltmain="$ac_aux_dir/ltmain.sh"
+
+# Make sure we can run config.sub.
+$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 ||
+ { { $as_echo "$as_me:$LINENO: error: cannot run $SHELL $ac_aux_dir/config.sub" >&5
+$as_echo "$as_me: error: cannot run $SHELL $ac_aux_dir/config.sub" >&2;}
+ { (exit 1); exit 1; }; }
+
+{ $as_echo "$as_me:$LINENO: checking build system type" >&5
+$as_echo_n "checking build system type... " >&6; }
+if test "${ac_cv_build+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_build_alias=$build_alias
+test "x$ac_build_alias" = x &&
+ ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"`
+test "x$ac_build_alias" = x &&
+ { { $as_echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5
+$as_echo "$as_me: error: cannot guess build type; you must specify one" >&2;}
+ { (exit 1); exit 1; }; }
+ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` ||
+ { { $as_echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $ac_build_alias failed" >&5
+$as_echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $ac_build_alias failed" >&2;}
+ { (exit 1); exit 1; }; }
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_build" >&5
+$as_echo "$ac_cv_build" >&6; }
+case $ac_cv_build in
+*-*-*) ;;
+*) { { $as_echo "$as_me:$LINENO: error: invalid value of canonical build" >&5
+$as_echo "$as_me: error: invalid value of canonical build" >&2;}
+ { (exit 1); exit 1; }; };;
+esac
+build=$ac_cv_build
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_build
+shift
+build_cpu=$1
+build_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+build_os=$*
+IFS=$ac_save_IFS
+case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac
+
+
+{ $as_echo "$as_me:$LINENO: checking host system type" >&5
+$as_echo_n "checking host system type... " >&6; }
+if test "${ac_cv_host+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test "x$host_alias" = x; then
+ ac_cv_host=$ac_cv_build
+else
+ ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` ||
+ { { $as_echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $host_alias failed" >&5
+$as_echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $host_alias failed" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_host" >&5
+$as_echo "$ac_cv_host" >&6; }
+case $ac_cv_host in
+*-*-*) ;;
+*) { { $as_echo "$as_me:$LINENO: error: invalid value of canonical host" >&5
+$as_echo "$as_me: error: invalid value of canonical host" >&2;}
+ { (exit 1); exit 1; }; };;
+esac
+host=$ac_cv_host
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_host
+shift
+host_cpu=$1
+host_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+host_os=$*
+IFS=$ac_save_IFS
+case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
+
+
+{ $as_echo "$as_me:$LINENO: checking for a sed that does not truncate output" >&5
+$as_echo_n "checking for a sed that does not truncate output... " >&6; }
+if test "${ac_cv_path_SED+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/
+ for ac_i in 1 2 3 4 5 6 7; do
+ ac_script="$ac_script$as_nl$ac_script"
+ done
+ echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed
+ $as_unset ac_script || ac_script=
+ if test -z "$SED"; then
+ ac_path_SED_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in sed gsed; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_SED="$as_dir/$ac_prog$ac_exec_ext"
+ { test -f "$ac_path_SED" && $as_test_x "$ac_path_SED"; } || continue
+# Check for GNU ac_path_SED and select it if it is found.
+ # Check for GNU $ac_path_SED
+case `"$ac_path_SED" --version 2>&1` in
+*GNU*)
+ ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo '' >> "conftest.nl"
+ "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ ac_count=`expr $ac_count + 1`
+ if test $ac_count -gt ${ac_path_SED_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_SED="$ac_path_SED"
+ ac_path_SED_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_SED_found && break 3
+ done
+ done
+done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_SED"; then
+ { { $as_echo "$as_me:$LINENO: error: no acceptable sed could be found in \$PATH" >&5
+$as_echo "$as_me: error: no acceptable sed could be found in \$PATH" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+else
+ ac_cv_path_SED=$SED
+fi
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_path_SED" >&5
+$as_echo "$ac_cv_path_SED" >&6; }
+ SED="$ac_cv_path_SED"
+ rm -f conftest.sed
+
+test -z "$SED" && SED=sed
+Xsed="$SED -e 1s/^X//"
+
+
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:$LINENO: checking for grep that handles long lines and -e" >&5
+$as_echo_n "checking for grep that handles long lines and -e... " >&6; }
+if test "${ac_cv_path_GREP+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -z "$GREP"; then
+ ac_path_GREP_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in grep ggrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
+ { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue
+# Check for GNU ac_path_GREP and select it if it is found.
+ # Check for GNU $ac_path_GREP
+case `"$ac_path_GREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo 'GREP' >> "conftest.nl"
+ "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ ac_count=`expr $ac_count + 1`
+ if test $ac_count -gt ${ac_path_GREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_GREP="$ac_path_GREP"
+ ac_path_GREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_GREP_found && break 3
+ done
+ done
+done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_GREP"; then
+ { { $as_echo "$as_me:$LINENO: error: no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5
+$as_echo "$as_me: error: no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+else
+ ac_cv_path_GREP=$GREP
+fi
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_path_GREP" >&5
+$as_echo "$ac_cv_path_GREP" >&6; }
+ GREP="$ac_cv_path_GREP"
+
+
+{ $as_echo "$as_me:$LINENO: checking for egrep" >&5
+$as_echo_n "checking for egrep... " >&6; }
+if test "${ac_cv_path_EGREP+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
+ then ac_cv_path_EGREP="$GREP -E"
+ else
+ if test -z "$EGREP"; then
+ ac_path_EGREP_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in egrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
+ { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue
+# Check for GNU ac_path_EGREP and select it if it is found.
+ # Check for GNU $ac_path_EGREP
+case `"$ac_path_EGREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo 'EGREP' >> "conftest.nl"
+ "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ ac_count=`expr $ac_count + 1`
+ if test $ac_count -gt ${ac_path_EGREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_EGREP="$ac_path_EGREP"
+ ac_path_EGREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_EGREP_found && break 3
+ done
+ done
+done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_EGREP"; then
+ { { $as_echo "$as_me:$LINENO: error: no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5
+$as_echo "$as_me: error: no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+else
+ ac_cv_path_EGREP=$EGREP
+fi
+
+ fi
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_path_EGREP" >&5
+$as_echo "$ac_cv_path_EGREP" >&6; }
+ EGREP="$ac_cv_path_EGREP"
+
+
+{ $as_echo "$as_me:$LINENO: checking for fgrep" >&5
+$as_echo_n "checking for fgrep... " >&6; }
+if test "${ac_cv_path_FGREP+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1
+ then ac_cv_path_FGREP="$GREP -F"
+ else
+ if test -z "$FGREP"; then
+ ac_path_FGREP_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in fgrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext"
+ { test -f "$ac_path_FGREP" && $as_test_x "$ac_path_FGREP"; } || continue
+# Check for GNU ac_path_FGREP and select it if it is found.
+ # Check for GNU $ac_path_FGREP
+case `"$ac_path_FGREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo 'FGREP' >> "conftest.nl"
+ "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ ac_count=`expr $ac_count + 1`
+ if test $ac_count -gt ${ac_path_FGREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_FGREP="$ac_path_FGREP"
+ ac_path_FGREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_FGREP_found && break 3
+ done
+ done
+done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_FGREP"; then
+ { { $as_echo "$as_me:$LINENO: error: no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5
+$as_echo "$as_me: error: no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+else
+ ac_cv_path_FGREP=$FGREP
+fi
+
+ fi
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_path_FGREP" >&5
+$as_echo "$ac_cv_path_FGREP" >&6; }
+ FGREP="$ac_cv_path_FGREP"
+
+
+test -z "$GREP" && GREP=grep
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# Check whether --with-gnu-ld was given.
+if test "${with_gnu_ld+set}" = set; then
+ withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes
+else
+ with_gnu_ld=no
+fi
+
+ac_prog=ld
+if test "$GCC" = yes; then
+ # Check if gcc -print-prog-name=ld gives a path.
+ { $as_echo "$as_me:$LINENO: checking for ld used by $CC" >&5
+$as_echo_n "checking for ld used by $CC... " >&6; }
+ case $host in
+ *-*-mingw*)
+ # gcc leaves a trailing carriage return which upsets mingw
+ ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+ *)
+ ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+ esac
+ case $ac_prog in
+ # Accept absolute paths.
+ [\\/]* | ?:[\\/]*)
+ re_direlt='/[^/][^/]*/\.\./'
+ # Canonicalize the pathname of ld
+ ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'`
+ while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do
+ ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"`
+ done
+ test -z "$LD" && LD="$ac_prog"
+ ;;
+ "")
+ # If it fails, then pretend we aren't using GCC.
+ ac_prog=ld
+ ;;
+ *)
+ # If it is relative, then search for the first ld in PATH.
+ with_gnu_ld=unknown
+ ;;
+ esac
+elif test "$with_gnu_ld" = yes; then
+ { $as_echo "$as_me:$LINENO: checking for GNU ld" >&5
+$as_echo_n "checking for GNU ld... " >&6; }
+else
+ { $as_echo "$as_me:$LINENO: checking for non-GNU ld" >&5
+$as_echo_n "checking for non-GNU ld... " >&6; }
+fi
+if test "${lt_cv_path_LD+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -z "$LD"; then
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+ lt_cv_path_LD="$ac_dir/$ac_prog"
+ # Check to see if the program is GNU ld. I'd rather use --version,
+ # but apparently some variants of GNU ld only accept -v.
+ # Break only if it was the GNU/non-GNU ld that we prefer.
+ case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
+ *GNU* | *'with BFD'*)
+ test "$with_gnu_ld" != no && break
+ ;;
+ *)
+ test "$with_gnu_ld" != yes && break
+ ;;
+ esac
+ fi
+ done
+ IFS="$lt_save_ifs"
+else
+ lt_cv_path_LD="$LD" # Let the user override the test with a path.
+fi
+fi
+
+LD="$lt_cv_path_LD"
+if test -n "$LD"; then
+ { $as_echo "$as_me:$LINENO: result: $LD" >&5
+$as_echo "$LD" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+test -z "$LD" && { { $as_echo "$as_me:$LINENO: error: no acceptable ld found in \$PATH" >&5
+$as_echo "$as_me: error: no acceptable ld found in \$PATH" >&2;}
+ { (exit 1); exit 1; }; }
+{ $as_echo "$as_me:$LINENO: checking if the linker ($LD) is GNU ld" >&5
+$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; }
+if test "${lt_cv_prog_gnu_ld+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ # I'd rather use --version here, but apparently some GNU lds only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+ lt_cv_prog_gnu_ld=yes
+ ;;
+*)
+ lt_cv_prog_gnu_ld=no
+ ;;
+esac
+fi
+{ $as_echo "$as_me:$LINENO: result: $lt_cv_prog_gnu_ld" >&5
+$as_echo "$lt_cv_prog_gnu_ld" >&6; }
+with_gnu_ld=$lt_cv_prog_gnu_ld
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:$LINENO: checking for BSD- or MS-compatible name lister (nm)" >&5
+$as_echo_n "checking for BSD- or MS-compatible name lister (nm)... " >&6; }
+if test "${lt_cv_path_NM+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$NM"; then
+ # Let the user override the test.
+ lt_cv_path_NM="$NM"
+else
+ lt_nm_to_check="${ac_tool_prefix}nm"
+ if test -n "$ac_tool_prefix" && test "$build" = "$host"; then
+ lt_nm_to_check="$lt_nm_to_check nm"
+ fi
+ for lt_tmp_nm in $lt_nm_to_check; do
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ tmp_nm="$ac_dir/$lt_tmp_nm"
+ if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then
+ # Check to see if the nm accepts a BSD-compat flag.
+ # Adding the `sed 1q' prevents false positives on HP-UX, which says:
+ # nm: unknown option "B" ignored
+ # Tru64's nm complains that /dev/null is an invalid object file
+ case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in
+ */dev/null* | *'Invalid file or object type'*)
+ lt_cv_path_NM="$tmp_nm -B"
+ break
+ ;;
+ *)
+ case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in
+ */dev/null*)
+ lt_cv_path_NM="$tmp_nm -p"
+ break
+ ;;
+ *)
+ lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but
+ continue # so that we can try to find one that supports BSD flags
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ done
+ IFS="$lt_save_ifs"
+ done
+ : ${lt_cv_path_NM=no}
+fi
+fi
+{ $as_echo "$as_me:$LINENO: result: $lt_cv_path_NM" >&5
+$as_echo "$lt_cv_path_NM" >&6; }
+if test "$lt_cv_path_NM" != "no"; then
+ NM="$lt_cv_path_NM"
+else
+ # Didn't find any BSD compatible name lister, look for dumpbin.
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in "dumpbin -symbols" "link -dump -symbols"
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_DUMPBIN+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$DUMPBIN"; then
+ ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+DUMPBIN=$ac_cv_prog_DUMPBIN
+if test -n "$DUMPBIN"; then
+ { $as_echo "$as_me:$LINENO: result: $DUMPBIN" >&5
+$as_echo "$DUMPBIN" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$DUMPBIN" && break
+ done
+fi
+if test -z "$DUMPBIN"; then
+ ac_ct_DUMPBIN=$DUMPBIN
+ for ac_prog in "dumpbin -symbols" "link -dump -symbols"
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_DUMPBIN+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_DUMPBIN"; then
+ ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_DUMPBIN="$ac_prog"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN
+if test -n "$ac_ct_DUMPBIN"; then
+ { $as_echo "$as_me:$LINENO: result: $ac_ct_DUMPBIN" >&5
+$as_echo "$ac_ct_DUMPBIN" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$ac_ct_DUMPBIN" && break
+done
+
+ if test "x$ac_ct_DUMPBIN" = x; then
+ DUMPBIN=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ DUMPBIN=$ac_ct_DUMPBIN
+ fi
+fi
+
+
+ if test "$DUMPBIN" != ":"; then
+ NM="$DUMPBIN"
+ fi
+fi
+test -z "$NM" && NM=nm
+
+
+
+
+
+
+{ $as_echo "$as_me:$LINENO: checking the name lister ($NM) interface" >&5
+$as_echo_n "checking the name lister ($NM) interface... " >&6; }
+if test "${lt_cv_nm_interface+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_nm_interface="BSD nm"
+ echo "int some_variable = 0;" > conftest.$ac_ext
+ (eval echo "\"\$as_me:5376: $ac_compile\"" >&5)
+ (eval "$ac_compile" 2>conftest.err)
+ cat conftest.err >&5
+ (eval echo "\"\$as_me:5379: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
+ (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
+ cat conftest.err >&5
+ (eval echo "\"\$as_me:5382: output\"" >&5)
+ cat conftest.out >&5
+ if $GREP 'External.*some_variable' conftest.out > /dev/null; then
+ lt_cv_nm_interface="MS dumpbin"
+ fi
+ rm -f conftest*
+fi
+{ $as_echo "$as_me:$LINENO: result: $lt_cv_nm_interface" >&5
+$as_echo "$lt_cv_nm_interface" >&6; }
+
+{ $as_echo "$as_me:$LINENO: checking whether ln -s works" >&5
+$as_echo_n "checking whether ln -s works... " >&6; }
+LN_S=$as_ln_s
+if test "$LN_S" = "ln -s"; then
+ { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no, using $LN_S" >&5
+$as_echo "no, using $LN_S" >&6; }
+fi
+
+# find the maximum length of command line arguments
+{ $as_echo "$as_me:$LINENO: checking the maximum length of command line arguments" >&5
+$as_echo_n "checking the maximum length of command line arguments... " >&6; }
+if test "${lt_cv_sys_max_cmd_len+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ i=0
+ teststring="ABCD"
+
+ case $build_os in
+ msdosdjgpp*)
+ # On DJGPP, this test can blow up pretty badly due to problems in libc
+ # (any single argument exceeding 2000 bytes causes a buffer overrun
+ # during glob expansion). Even if it were fixed, the result of this
+ # check would be larger than it should be.
+ lt_cv_sys_max_cmd_len=12288; # 12K is about right
+ ;;
+
+ gnu*)
+ # Under GNU Hurd, this test is not required because there is
+ # no limit to the length of command line arguments.
+ # Libtool will interpret -1 as no limit whatsoever
+ lt_cv_sys_max_cmd_len=-1;
+ ;;
+
+ cygwin* | mingw* | cegcc*)
+ # On Win9x/ME, this test blows up -- it succeeds, but takes
+ # about 5 minutes as the teststring grows exponentially.
+ # Worse, since 9x/ME are not pre-emptively multitasking,
+ # you end up with a "frozen" computer, even though with patience
+ # the test eventually succeeds (with a max line length of 256k).
+ # Instead, let's just punt: use the minimum linelength reported by
+ # all of the supported platforms: 8192 (on NT/2K/XP).
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ amigaos*)
+ # On AmigaOS with pdksh, this test takes hours, literally.
+ # So we just punt and use a minimum line length of 8192.
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ netbsd* | freebsd* | openbsd* | darwin* | dragonfly*)
+ # This has been around since 386BSD, at least. Likely further.
+ if test -x /sbin/sysctl; then
+ lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax`
+ elif test -x /usr/sbin/sysctl; then
+ lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax`
+ else
+ lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs
+ fi
+ # And add a safety zone
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+ ;;
+
+ interix*)
+ # We know the value 262144 and hardcode it with a safety zone (like BSD)
+ lt_cv_sys_max_cmd_len=196608
+ ;;
+
+ osf*)
+ # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure
+ # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not
+ # nice to cause kernel panics so lets avoid the loop below.
+ # First set a reasonable default.
+ lt_cv_sys_max_cmd_len=16384
+ #
+ if test -x /sbin/sysconfig; then
+ case `/sbin/sysconfig -q proc exec_disable_arg_limit` in
+ *1*) lt_cv_sys_max_cmd_len=-1 ;;
+ esac
+ fi
+ ;;
+ sco3.2v5*)
+ lt_cv_sys_max_cmd_len=102400
+ ;;
+ sysv5* | sco5v6* | sysv4.2uw2*)
+ kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null`
+ if test -n "$kargmax"; then
+ lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[ ]//'`
+ else
+ lt_cv_sys_max_cmd_len=32768
+ fi
+ ;;
+ *)
+ lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null`
+ if test -n "$lt_cv_sys_max_cmd_len"; then
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+ else
+ # Make teststring a little bigger before we do anything with it.
+ # a 1K string should be a reasonable start.
+ for i in 1 2 3 4 5 6 7 8 ; do
+ teststring=$teststring$teststring
+ done
+ SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}}
+ # If test is not a shell built-in, we'll probably end up computing a
+ # maximum length that is only half of the actual maximum length, but
+ # we can't tell.
+ while { test "X"`$SHELL $0 --fallback-echo "X$teststring$teststring" 2>/dev/null` \
+ = "XX$teststring$teststring"; } >/dev/null 2>&1 &&
+ test $i != 17 # 1/2 MB should be enough
+ do
+ i=`expr $i + 1`
+ teststring=$teststring$teststring
+ done
+ # Only check the string length outside the loop.
+ lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1`
+ teststring=
+ # Add a significant safety factor because C++ compilers can tack on
+ # massive amounts of additional arguments before passing them to the
+ # linker. It appears as though 1/2 is a usable value.
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2`
+ fi
+ ;;
+ esac
+
+fi
+
+if test -n $lt_cv_sys_max_cmd_len ; then
+ { $as_echo "$as_me:$LINENO: result: $lt_cv_sys_max_cmd_len" >&5
+$as_echo "$lt_cv_sys_max_cmd_len" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: none" >&5
+$as_echo "none" >&6; }
+fi
+max_cmd_len=$lt_cv_sys_max_cmd_len
+
+
+
+
+
+
+: ${CP="cp -f"}
+: ${MV="mv -f"}
+: ${RM="rm -f"}
+
+{ $as_echo "$as_me:$LINENO: checking whether the shell understands some XSI constructs" >&5
+$as_echo_n "checking whether the shell understands some XSI constructs... " >&6; }
+# Try some XSI features
+xsi_shell=no
+( _lt_dummy="a/b/c"
+ test "${_lt_dummy##*/},${_lt_dummy%/*},"${_lt_dummy%"$_lt_dummy"}, \
+ = c,a/b,, \
+ && eval 'test $(( 1 + 1 )) -eq 2 \
+ && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \
+ && xsi_shell=yes
+{ $as_echo "$as_me:$LINENO: result: $xsi_shell" >&5
+$as_echo "$xsi_shell" >&6; }
+
+
+{ $as_echo "$as_me:$LINENO: checking whether the shell understands \"+=\"" >&5
+$as_echo_n "checking whether the shell understands \"+=\"... " >&6; }
+lt_shell_append=no
+( foo=bar; set foo baz; eval "$1+=\$2" && test "$foo" = barbaz ) \
+ >/dev/null 2>&1 \
+ && lt_shell_append=yes
+{ $as_echo "$as_me:$LINENO: result: $lt_shell_append" >&5
+$as_echo "$lt_shell_append" >&6; }
+
+
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ lt_unset=unset
+else
+ lt_unset=false
+fi
+
+
+
+
+
+# test EBCDIC or ASCII
+case `echo X|tr X '\101'` in
+ A) # ASCII based system
+ # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr
+ lt_SP2NL='tr \040 \012'
+ lt_NL2SP='tr \015\012 \040\040'
+ ;;
+ *) # EBCDIC based system
+ lt_SP2NL='tr \100 \n'
+ lt_NL2SP='tr \r\n \100\100'
+ ;;
+esac
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:$LINENO: checking for $LD option to reload object files" >&5
+$as_echo_n "checking for $LD option to reload object files... " >&6; }
+if test "${lt_cv_ld_reload_flag+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_ld_reload_flag='-r'
+fi
+{ $as_echo "$as_me:$LINENO: result: $lt_cv_ld_reload_flag" >&5
+$as_echo "$lt_cv_ld_reload_flag" >&6; }
+reload_flag=$lt_cv_ld_reload_flag
+case $reload_flag in
+"" | " "*) ;;
+*) reload_flag=" $reload_flag" ;;
+esac
+reload_cmds='$LD$reload_flag -o $output$reload_objs'
+case $host_os in
+ darwin*)
+ if test "$GCC" = yes; then
+ reload_cmds='$LTCC $LTCFLAGS -nostdlib ${wl}-r -o $output$reload_objs'
+ else
+ reload_cmds='$LD$reload_flag -o $output$reload_objs'
+ fi
+ ;;
+esac
+
+
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args.
+set dummy ${ac_tool_prefix}objdump; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_OBJDUMP+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$OBJDUMP"; then
+ ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+OBJDUMP=$ac_cv_prog_OBJDUMP
+if test -n "$OBJDUMP"; then
+ { $as_echo "$as_me:$LINENO: result: $OBJDUMP" >&5
+$as_echo "$OBJDUMP" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OBJDUMP"; then
+ ac_ct_OBJDUMP=$OBJDUMP
+ # Extract the first word of "objdump", so it can be a program name with args.
+set dummy objdump; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_OBJDUMP+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_OBJDUMP"; then
+ ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_OBJDUMP="objdump"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP
+if test -n "$ac_ct_OBJDUMP"; then
+ { $as_echo "$as_me:$LINENO: result: $ac_ct_OBJDUMP" >&5
+$as_echo "$ac_ct_OBJDUMP" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_OBJDUMP" = x; then
+ OBJDUMP="false"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ OBJDUMP=$ac_ct_OBJDUMP
+ fi
+else
+ OBJDUMP="$ac_cv_prog_OBJDUMP"
+fi
+
+test -z "$OBJDUMP" && OBJDUMP=objdump
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:$LINENO: checking how to recognize dependent libraries" >&5
+$as_echo_n "checking how to recognize dependent libraries... " >&6; }
+if test "${lt_cv_deplibs_check_method+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_file_magic_cmd='$MAGIC_CMD'
+lt_cv_file_magic_test_file=
+lt_cv_deplibs_check_method='unknown'
+# Need to set the preceding variable on all platforms that support
+# interlibrary dependencies.
+# 'none' -- dependencies not supported.
+# `unknown' -- same as none, but documents that we really don't know.
+# 'pass_all' -- all dependencies passed with no checks.
+# 'test_compile' -- check by making test program.
+# 'file_magic [[regex]]' -- check by looking for files in library path
+# which responds to the $file_magic_cmd with a given extended regex.
+# If you have `file' or equivalent on your system and you're not sure
+# whether `pass_all' will *always* work, you probably want this one.
+
+case $host_os in
+aix[4-9]*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+beos*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+bsdi[45]*)
+ lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)'
+ lt_cv_file_magic_cmd='/usr/bin/file -L'
+ lt_cv_file_magic_test_file=/shlib/libc.so
+ ;;
+
+cygwin*)
+ # func_win32_libid is a shell function defined in ltmain.sh
+ lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+ lt_cv_file_magic_cmd='func_win32_libid'
+ ;;
+
+mingw* | pw32*)
+ # Base MSYS/MinGW do not provide the 'file' command needed by
+ # func_win32_libid shell function, so use a weaker test based on 'objdump',
+ # unless we find 'file', for example because we are cross-compiling.
+ if ( file / ) >/dev/null 2>&1; then
+ lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+ lt_cv_file_magic_cmd='func_win32_libid'
+ else
+ lt_cv_deplibs_check_method='file_magic file format pei*-i386(.*architecture: i386)?'
+ lt_cv_file_magic_cmd='$OBJDUMP -f'
+ fi
+ ;;
+
+cegcc)
+ # use the weaker test based on 'objdump'. See mingw*.
+ lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?'
+ lt_cv_file_magic_cmd='$OBJDUMP -f'
+ ;;
+
+darwin* | rhapsody*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+freebsd* | dragonfly*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+ case $host_cpu in
+ i*86 )
+ # Not sure whether the presence of OpenBSD here was a mistake.
+ # Let's accept both of them until this is cleared up.
+ lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library'
+ lt_cv_file_magic_cmd=/usr/bin/file
+ lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
+ ;;
+ esac
+ else
+ lt_cv_deplibs_check_method=pass_all
+ fi
+ ;;
+
+gnu*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+hpux10.20* | hpux11*)
+ lt_cv_file_magic_cmd=/usr/bin/file
+ case $host_cpu in
+ ia64*)
+ lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64'
+ lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so
+ ;;
+ hppa*64*)
+ lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - PA-RISC [0-9].[0-9]'
+ lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl
+ ;;
+ *)
+ lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9].[0-9]) shared library'
+ lt_cv_file_magic_test_file=/usr/lib/libc.sl
+ ;;
+ esac
+ ;;
+
+interix[3-9]*)
+ # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$'
+ ;;
+
+irix5* | irix6* | nonstopux*)
+ case $LD in
+ *-32|*"-32 ") libmagic=32-bit;;
+ *-n32|*"-n32 ") libmagic=N32;;
+ *-64|*"-64 ") libmagic=64-bit;;
+ *) libmagic=never-match;;
+ esac
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+# This must be Linux ELF.
+linux* | k*bsd*-gnu)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
+ else
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$'
+ fi
+ ;;
+
+newos6*)
+ lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)'
+ lt_cv_file_magic_cmd=/usr/bin/file
+ lt_cv_file_magic_test_file=/usr/lib/libnls.so
+ ;;
+
+*nto* | *qnx*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+openbsd*)
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$'
+ else
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
+ fi
+ ;;
+
+osf3* | osf4* | osf5*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+rdos*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+solaris*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+sysv4 | sysv4.3*)
+ case $host_vendor in
+ motorola)
+ lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]'
+ lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*`
+ ;;
+ ncr)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ sequent)
+ lt_cv_file_magic_cmd='/bin/file'
+ lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )'
+ ;;
+ sni)
+ lt_cv_file_magic_cmd='/bin/file'
+ lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib"
+ lt_cv_file_magic_test_file=/lib/libc.so
+ ;;
+ siemens)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ pc)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ esac
+ ;;
+
+tpf*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+esac
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $lt_cv_deplibs_check_method" >&5
+$as_echo "$lt_cv_deplibs_check_method" >&6; }
+file_magic_cmd=$lt_cv_file_magic_cmd
+deplibs_check_method=$lt_cv_deplibs_check_method
+test -z "$deplibs_check_method" && deplibs_check_method=unknown
+
+
+
+
+
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ar; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_AR+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$AR"; then
+ ac_cv_prog_AR="$AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_AR="${ac_tool_prefix}ar"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+AR=$ac_cv_prog_AR
+if test -n "$AR"; then
+ { $as_echo "$as_me:$LINENO: result: $AR" >&5
+$as_echo "$AR" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_AR"; then
+ ac_ct_AR=$AR
+ # Extract the first word of "ar", so it can be a program name with args.
+set dummy ar; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_AR+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_AR"; then
+ ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_AR="ar"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_AR=$ac_cv_prog_ac_ct_AR
+if test -n "$ac_ct_AR"; then
+ { $as_echo "$as_me:$LINENO: result: $ac_ct_AR" >&5
+$as_echo "$ac_ct_AR" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_AR" = x; then
+ AR="false"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ AR=$ac_ct_AR
+ fi
+else
+ AR="$ac_cv_prog_AR"
+fi
+
+test -z "$AR" && AR=ar
+test -z "$AR_FLAGS" && AR_FLAGS=cru
+
+
+
+
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
+set dummy ${ac_tool_prefix}strip; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_STRIP+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$STRIP"; then
+ ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_STRIP="${ac_tool_prefix}strip"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+STRIP=$ac_cv_prog_STRIP
+if test -n "$STRIP"; then
+ { $as_echo "$as_me:$LINENO: result: $STRIP" >&5
+$as_echo "$STRIP" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_STRIP"; then
+ ac_ct_STRIP=$STRIP
+ # Extract the first word of "strip", so it can be a program name with args.
+set dummy strip; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_STRIP"; then
+ ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_STRIP="strip"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
+if test -n "$ac_ct_STRIP"; then
+ { $as_echo "$as_me:$LINENO: result: $ac_ct_STRIP" >&5
+$as_echo "$ac_ct_STRIP" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_STRIP" = x; then
+ STRIP=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ STRIP=$ac_ct_STRIP
+ fi
+else
+ STRIP="$ac_cv_prog_STRIP"
+fi
+
+test -z "$STRIP" && STRIP=:
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_RANLIB+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$RANLIB"; then
+ ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+ { $as_echo "$as_me:$LINENO: result: $RANLIB" >&5
+$as_echo "$RANLIB" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+ ac_ct_RANLIB=$RANLIB
+ # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_RANLIB"; then
+ ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_RANLIB="ranlib"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+ { $as_echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5
+$as_echo "$ac_ct_RANLIB" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_RANLIB" = x; then
+ RANLIB=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ RANLIB=$ac_ct_RANLIB
+ fi
+else
+ RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+test -z "$RANLIB" && RANLIB=:
+
+
+
+
+
+
+# Determine commands to create old-style static archives.
+old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs'
+old_postinstall_cmds='chmod 644 $oldlib'
+old_postuninstall_cmds=
+
+if test -n "$RANLIB"; then
+ case $host_os in
+ openbsd*)
+ old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$oldlib"
+ ;;
+ *)
+ old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$oldlib"
+ ;;
+ esac
+ old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib"
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+
+
+# Check for command to grab the raw symbol name followed by C symbol from nm.
+{ $as_echo "$as_me:$LINENO: checking command to parse $NM output from $compiler object" >&5
+$as_echo_n "checking command to parse $NM output from $compiler object... " >&6; }
+if test "${lt_cv_sys_global_symbol_pipe+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+
+# These are sane defaults that work on at least a few old systems.
+# [They come from Ultrix. What could be older than Ultrix?!! ;)]
+
+# Character class describing NM global symbol codes.
+symcode='[BCDEGRST]'
+
+# Regexp to match symbols that can be accessed directly from C.
+sympat='\([_A-Za-z][_A-Za-z0-9]*\)'
+
+# Define system-specific variables.
+case $host_os in
+aix*)
+ symcode='[BCDT]'
+ ;;
+cygwin* | mingw* | pw32* | cegcc*)
+ symcode='[ABCDGISTW]'
+ ;;
+hpux*)
+ if test "$host_cpu" = ia64; then
+ symcode='[ABCDEGRST]'
+ fi
+ ;;
+irix* | nonstopux*)
+ symcode='[BCDEGRST]'
+ ;;
+osf*)
+ symcode='[BCDEGQRST]'
+ ;;
+solaris*)
+ symcode='[BDRT]'
+ ;;
+sco3.2v5*)
+ symcode='[DT]'
+ ;;
+sysv4.2uw2*)
+ symcode='[DT]'
+ ;;
+sysv5* | sco5v6* | unixware* | OpenUNIX*)
+ symcode='[ABDT]'
+ ;;
+sysv4)
+ symcode='[DFNSTU]'
+ ;;
+esac
+
+# If we're using GNU nm, then use its standard symbol codes.
+case `$NM -V 2>&1` in
+*GNU* | *'with BFD'*)
+ symcode='[ABCDGIRSTW]' ;;
+esac
+
+# Transform an extracted symbol line into a proper C declaration.
+# Some systems (esp. on ia64) link data and code symbols differently,
+# so use this general approach.
+lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'"
+
+# Transform an extracted symbol line into symbol name and symbol address
+lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([^ ]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"\2\", (void *) \&\2},/p'"
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([^ ]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \(lib[^ ]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"lib\2\", (void *) \&\2},/p'"
+
+# Handle CRLF in mingw tool chain
+opt_cr=
+case $build_os in
+mingw*)
+ opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp
+ ;;
+esac
+
+# Try without a prefix underscore, then with it.
+for ac_symprfx in "" "_"; do
+
+ # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol.
+ symxfrm="\\1 $ac_symprfx\\2 \\2"
+
+ # Write the raw and C identifiers.
+ if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+ # Fake it for dumpbin and say T for any non-static function
+ # and D for any global variable.
+ # Also find C++ and __fastcall symbols from MSVC++,
+ # which start with @ or ?.
+ lt_cv_sys_global_symbol_pipe="$AWK '"\
+" {last_section=section; section=\$ 3};"\
+" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\
+" \$ 0!~/External *\|/{next};"\
+" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\
+" {if(hide[section]) next};"\
+" {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\
+" {split(\$ 0, a, /\||\r/); split(a[2], s)};"\
+" s[1]~/^[@?]/{print s[1], s[1]; next};"\
+" s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\
+" ' prfx=^$ac_symprfx"
+ else
+ lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'"
+ fi
+
+ # Check to see that the pipe works correctly.
+ pipe_works=no
+
+ rm -f conftest*
+ cat > conftest.$ac_ext <<_LT_EOF
+#ifdef __cplusplus
+extern "C" {
+#endif
+char nm_test_var;
+void nm_test_func(void);
+void nm_test_func(void){}
+#ifdef __cplusplus
+}
+#endif
+int main(){nm_test_var='a';nm_test_func();return(0);}
+_LT_EOF
+
+ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ # Now try to grab the symbols.
+ nlist=conftest.nm
+ if { (eval echo "$as_me:$LINENO: \"$NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist\"") >&5
+ (eval $NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && test -s "$nlist"; then
+ # Try sorting and uniquifying the output.
+ if sort "$nlist" | uniq > "$nlist"T; then
+ mv -f "$nlist"T "$nlist"
+ else
+ rm -f "$nlist"T
+ fi
+
+ # Make sure that we snagged all the symbols we need.
+ if $GREP ' nm_test_var$' "$nlist" >/dev/null; then
+ if $GREP ' nm_test_func$' "$nlist" >/dev/null; then
+ cat <<_LT_EOF > conftest.$ac_ext
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+_LT_EOF
+ # Now generate the symbol file.
+ eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext'
+
+ cat <<_LT_EOF >> conftest.$ac_ext
+
+/* The mapping between symbol names and symbols. */
+const struct {
+ const char *name;
+ void *address;
+}
+lt__PROGRAM__LTX_preloaded_symbols[] =
+{
+ { "@PROGRAM@", (void *) 0 },
+_LT_EOF
+ $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext
+ cat <<\_LT_EOF >> conftest.$ac_ext
+ {0, (void *) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+ return lt__PROGRAM__LTX_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+_LT_EOF
+ # Now try linking the two files.
+ mv conftest.$ac_objext conftstm.$ac_objext
+ lt_save_LIBS="$LIBS"
+ lt_save_CFLAGS="$CFLAGS"
+ LIBS="conftstm.$ac_objext"
+ CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag"
+ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && test -s conftest${ac_exeext}; then
+ pipe_works=yes
+ fi
+ LIBS="$lt_save_LIBS"
+ CFLAGS="$lt_save_CFLAGS"
+ else
+ echo "cannot find nm_test_func in $nlist" >&5
+ fi
+ else
+ echo "cannot find nm_test_var in $nlist" >&5
+ fi
+ else
+ echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5
+ fi
+ else
+ echo "$progname: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ fi
+ rm -rf conftest* conftst*
+
+ # Do not use the global_symbol_pipe unless it works.
+ if test "$pipe_works" = yes; then
+ break
+ else
+ lt_cv_sys_global_symbol_pipe=
+ fi
+done
+
+fi
+
+if test -z "$lt_cv_sys_global_symbol_pipe"; then
+ lt_cv_sys_global_symbol_to_cdecl=
+fi
+if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then
+ { $as_echo "$as_me:$LINENO: result: failed" >&5
+$as_echo "failed" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: ok" >&5
+$as_echo "ok" >&6; }
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# Check whether --enable-libtool-lock was given.
+if test "${enable_libtool_lock+set}" = set; then
+ enableval=$enable_libtool_lock;
+fi
+
+test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes
+
+# Some flags need to be propagated to the compiler or linker for good
+# libtool support.
+case $host in
+ia64-*-hpux*)
+ # Find out which ABI we are using.
+ echo 'int i;' > conftest.$ac_ext
+ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ case `/usr/bin/file conftest.$ac_objext` in
+ *ELF-32*)
+ HPUX_IA64_MODE="32"
+ ;;
+ *ELF-64*)
+ HPUX_IA64_MODE="64"
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+*-*-irix6*)
+ # Find out which ABI we are using.
+ echo '#line 6587 "configure"' > conftest.$ac_ext
+ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ if test "$lt_cv_prog_gnu_ld" = yes; then
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ LD="${LD-ld} -melf32bsmip"
+ ;;
+ *N32*)
+ LD="${LD-ld} -melf32bmipn32"
+ ;;
+ *64-bit*)
+ LD="${LD-ld} -melf64bmip"
+ ;;
+ esac
+ else
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ LD="${LD-ld} -32"
+ ;;
+ *N32*)
+ LD="${LD-ld} -n32"
+ ;;
+ *64-bit*)
+ LD="${LD-ld} -64"
+ ;;
+ esac
+ fi
+ fi
+ rm -rf conftest*
+ ;;
+
+x86_64-*kfreebsd*-gnu|x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*| \
+s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
+ # Find out which ABI we are using.
+ echo 'int i;' > conftest.$ac_ext
+ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ case `/usr/bin/file conftest.o` in
+ *32-bit*)
+ case $host in
+ x86_64-*kfreebsd*-gnu)
+ LD="${LD-ld} -m elf_i386_fbsd"
+ ;;
+ x86_64-*linux*)
+ LD="${LD-ld} -m elf_i386"
+ ;;
+ ppc64-*linux*|powerpc64-*linux*)
+ LD="${LD-ld} -m elf32ppclinux"
+ ;;
+ s390x-*linux*)
+ LD="${LD-ld} -m elf_s390"
+ ;;
+ sparc64-*linux*)
+ LD="${LD-ld} -m elf32_sparc"
+ ;;
+ esac
+ ;;
+ *64-bit*)
+ case $host in
+ x86_64-*kfreebsd*-gnu)
+ LD="${LD-ld} -m elf_x86_64_fbsd"
+ ;;
+ x86_64-*linux*)
+ LD="${LD-ld} -m elf_x86_64"
+ ;;
+ ppc*-*linux*|powerpc*-*linux*)
+ LD="${LD-ld} -m elf64ppc"
+ ;;
+ s390*-*linux*|s390*-*tpf*)
+ LD="${LD-ld} -m elf64_s390"
+ ;;
+ sparc*-*linux*)
+ LD="${LD-ld} -m elf64_sparc"
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+
+*-*-sco3.2v5*)
+ # On SCO OpenServer 5, we need -belf to get full-featured binaries.
+ SAVE_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS -belf"
+ { $as_echo "$as_me:$LINENO: checking whether the C compiler needs -belf" >&5
+$as_echo_n "checking whether the C compiler needs -belf... " >&6; }
+if test "${lt_cv_cc_needs_belf+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ lt_cv_cc_needs_belf=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ lt_cv_cc_needs_belf=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+ ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $lt_cv_cc_needs_belf" >&5
+$as_echo "$lt_cv_cc_needs_belf" >&6; }
+ if test x"$lt_cv_cc_needs_belf" != x"yes"; then
+ # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf
+ CFLAGS="$SAVE_CFLAGS"
+ fi
+ ;;
+sparc*-*solaris*)
+ # Find out which ABI we are using.
+ echo 'int i;' > conftest.$ac_ext
+ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ case `/usr/bin/file conftest.o` in
+ *64-bit*)
+ case $lt_cv_prog_gnu_ld in
+ yes*) LD="${LD-ld} -m elf64_sparc" ;;
+ *)
+ if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then
+ LD="${LD-ld} -64"
+ fi
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+esac
+
+need_locks="$enable_libtool_lock"
+
+
+ case $host_os in
+ rhapsody* | darwin*)
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args.
+set dummy ${ac_tool_prefix}dsymutil; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_DSYMUTIL+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$DSYMUTIL"; then
+ ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+DSYMUTIL=$ac_cv_prog_DSYMUTIL
+if test -n "$DSYMUTIL"; then
+ { $as_echo "$as_me:$LINENO: result: $DSYMUTIL" >&5
+$as_echo "$DSYMUTIL" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_DSYMUTIL"; then
+ ac_ct_DSYMUTIL=$DSYMUTIL
+ # Extract the first word of "dsymutil", so it can be a program name with args.
+set dummy dsymutil; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_DSYMUTIL+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_DSYMUTIL"; then
+ ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_DSYMUTIL="dsymutil"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL
+if test -n "$ac_ct_DSYMUTIL"; then
+ { $as_echo "$as_me:$LINENO: result: $ac_ct_DSYMUTIL" >&5
+$as_echo "$ac_ct_DSYMUTIL" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_DSYMUTIL" = x; then
+ DSYMUTIL=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ DSYMUTIL=$ac_ct_DSYMUTIL
+ fi
+else
+ DSYMUTIL="$ac_cv_prog_DSYMUTIL"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args.
+set dummy ${ac_tool_prefix}nmedit; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_NMEDIT+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$NMEDIT"; then
+ ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+NMEDIT=$ac_cv_prog_NMEDIT
+if test -n "$NMEDIT"; then
+ { $as_echo "$as_me:$LINENO: result: $NMEDIT" >&5
+$as_echo "$NMEDIT" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_NMEDIT"; then
+ ac_ct_NMEDIT=$NMEDIT
+ # Extract the first word of "nmedit", so it can be a program name with args.
+set dummy nmedit; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_NMEDIT+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_NMEDIT"; then
+ ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_NMEDIT="nmedit"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT
+if test -n "$ac_ct_NMEDIT"; then
+ { $as_echo "$as_me:$LINENO: result: $ac_ct_NMEDIT" >&5
+$as_echo "$ac_ct_NMEDIT" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_NMEDIT" = x; then
+ NMEDIT=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ NMEDIT=$ac_ct_NMEDIT
+ fi
+else
+ NMEDIT="$ac_cv_prog_NMEDIT"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args.
+set dummy ${ac_tool_prefix}lipo; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_LIPO+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$LIPO"; then
+ ac_cv_prog_LIPO="$LIPO" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_LIPO="${ac_tool_prefix}lipo"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+LIPO=$ac_cv_prog_LIPO
+if test -n "$LIPO"; then
+ { $as_echo "$as_me:$LINENO: result: $LIPO" >&5
+$as_echo "$LIPO" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_LIPO"; then
+ ac_ct_LIPO=$LIPO
+ # Extract the first word of "lipo", so it can be a program name with args.
+set dummy lipo; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_LIPO+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_LIPO"; then
+ ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_LIPO="lipo"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO
+if test -n "$ac_ct_LIPO"; then
+ { $as_echo "$as_me:$LINENO: result: $ac_ct_LIPO" >&5
+$as_echo "$ac_ct_LIPO" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_LIPO" = x; then
+ LIPO=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ LIPO=$ac_ct_LIPO
+ fi
+else
+ LIPO="$ac_cv_prog_LIPO"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args.
+set dummy ${ac_tool_prefix}otool; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_OTOOL+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$OTOOL"; then
+ ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_OTOOL="${ac_tool_prefix}otool"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+OTOOL=$ac_cv_prog_OTOOL
+if test -n "$OTOOL"; then
+ { $as_echo "$as_me:$LINENO: result: $OTOOL" >&5
+$as_echo "$OTOOL" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OTOOL"; then
+ ac_ct_OTOOL=$OTOOL
+ # Extract the first word of "otool", so it can be a program name with args.
+set dummy otool; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_OTOOL+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_OTOOL"; then
+ ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_OTOOL="otool"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL
+if test -n "$ac_ct_OTOOL"; then
+ { $as_echo "$as_me:$LINENO: result: $ac_ct_OTOOL" >&5
+$as_echo "$ac_ct_OTOOL" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_OTOOL" = x; then
+ OTOOL=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ OTOOL=$ac_ct_OTOOL
+ fi
+else
+ OTOOL="$ac_cv_prog_OTOOL"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args.
+set dummy ${ac_tool_prefix}otool64; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_OTOOL64+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$OTOOL64"; then
+ ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+OTOOL64=$ac_cv_prog_OTOOL64
+if test -n "$OTOOL64"; then
+ { $as_echo "$as_me:$LINENO: result: $OTOOL64" >&5
+$as_echo "$OTOOL64" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OTOOL64"; then
+ ac_ct_OTOOL64=$OTOOL64
+ # Extract the first word of "otool64", so it can be a program name with args.
+set dummy otool64; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_OTOOL64+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_OTOOL64"; then
+ ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_OTOOL64="otool64"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64
+if test -n "$ac_ct_OTOOL64"; then
+ { $as_echo "$as_me:$LINENO: result: $ac_ct_OTOOL64" >&5
+$as_echo "$ac_ct_OTOOL64" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_OTOOL64" = x; then
+ OTOOL64=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ OTOOL64=$ac_ct_OTOOL64
+ fi
+else
+ OTOOL64="$ac_cv_prog_OTOOL64"
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ { $as_echo "$as_me:$LINENO: checking for -single_module linker flag" >&5
+$as_echo_n "checking for -single_module linker flag... " >&6; }
+if test "${lt_cv_apple_cc_single_mod+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_apple_cc_single_mod=no
+ if test -z "${LT_MULTI_MODULE}"; then
+ # By default we will add the -single_module flag. You can override
+ # by either setting the environment variable LT_MULTI_MODULE
+ # non-empty at configure time, or by adding -multi_module to the
+ # link flags.
+ rm -rf libconftest.dylib*
+ echo "int foo(void){return 1;}" > conftest.c
+ echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+-dynamiclib -Wl,-single_module conftest.c" >&5
+ $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err
+ _lt_result=$?
+ if test -f libconftest.dylib && test ! -s conftest.err && test $_lt_result = 0; then
+ lt_cv_apple_cc_single_mod=yes
+ else
+ cat conftest.err >&5
+ fi
+ rm -rf libconftest.dylib*
+ rm -f conftest.*
+ fi
+fi
+{ $as_echo "$as_me:$LINENO: result: $lt_cv_apple_cc_single_mod" >&5
+$as_echo "$lt_cv_apple_cc_single_mod" >&6; }
+ { $as_echo "$as_me:$LINENO: checking for -exported_symbols_list linker flag" >&5
+$as_echo_n "checking for -exported_symbols_list linker flag... " >&6; }
+if test "${lt_cv_ld_exported_symbols_list+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_ld_exported_symbols_list=no
+ save_LDFLAGS=$LDFLAGS
+ echo "_main" > conftest.sym
+ LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym"
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ lt_cv_ld_exported_symbols_list=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ lt_cv_ld_exported_symbols_list=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+ LDFLAGS="$save_LDFLAGS"
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $lt_cv_ld_exported_symbols_list" >&5
+$as_echo "$lt_cv_ld_exported_symbols_list" >&6; }
+ case $host_os in
+ rhapsody* | darwin1.[012])
+ _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;;
+ darwin1.*)
+ _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+ darwin*) # darwin 5.x on
+ # if running on 10.5 or later, the deployment target defaults
+ # to the OS version, if on x86, and 10.4, the deployment
+ # target defaults to 10.4. Don't you love it?
+ case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
+ 10.0,*86*-darwin8*|10.0,*-darwin[91]*)
+ _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
+ 10.[012]*)
+ _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+ 10.*)
+ _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
+ esac
+ ;;
+ esac
+ if test "$lt_cv_apple_cc_single_mod" = "yes"; then
+ _lt_dar_single_mod='$single_module'
+ fi
+ if test "$lt_cv_ld_exported_symbols_list" = "yes"; then
+ _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym'
+ else
+ _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}'
+ fi
+ if test "$DSYMUTIL" != ":"; then
+ _lt_dsymutil='~$DSYMUTIL $lib || :'
+ else
+ _lt_dsymutil=
+ fi
+ ;;
+ esac
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ $as_echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5
+$as_echo_n "checking how to run the C preprocessor... " >&6; }
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+ if test "${ac_cv_prog_CPP+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ # Double quotes because CPP needs to be expanded
+ for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+ do
+ ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ :
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Broken: fails on valid input.
+continue
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ # Broken: success on invalid input.
+continue
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+ break
+fi
+
+ done
+ ac_cv_prog_CPP=$CPP
+
+fi
+ CPP=$ac_cv_prog_CPP
+else
+ ac_cv_prog_CPP=$CPP
+fi
+{ $as_echo "$as_me:$LINENO: result: $CPP" >&5
+$as_echo "$CPP" >&6; }
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ :
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Broken: fails on valid input.
+continue
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ # Broken: success on invalid input.
+continue
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+ :
+else
+ { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { $as_echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&5
+$as_echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+{ $as_echo "$as_me:$LINENO: checking for ANSI C header files" >&5
+$as_echo_n "checking for ANSI C header files... " >&6; }
+if test "${ac_cv_header_stdc+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_header_stdc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_header_stdc=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "memchr" >/dev/null 2>&1; then
+ :
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "free" >/dev/null 2>&1; then
+ :
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+ if test "$cross_compiling" = yes; then
+ :
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+ (('a' <= (c) && (c) <= 'i') \
+ || ('j' <= (c) && (c) <= 'r') \
+ || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ if (XOR (islower (i), ISLOWER (i))
+ || toupper (i) != TOUPPER (i))
+ return 2;
+ return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ :
+else
+ $as_echo "$as_me: program exited with status $ac_status" >&5
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_header_stdc=no
+fi
+rm -rf conftest.dSYM
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5
+$as_echo "$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define STDC_HEADERS 1
+_ACEOF
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+
+
+
+
+
+
+
+
+
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+ inttypes.h stdint.h unistd.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ eval "$as_ac_Header=yes"
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Header=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+
+for ac_header in dlfcn.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ eval "$as_ac_Header=yes"
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Header=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+
+# Set options
+
+
+
+ enable_dlopen=no
+
+
+ enable_win32_dll=no
+
+
+ # Check whether --enable-shared was given.
+if test "${enable_shared+set}" = set; then
+ enableval=$enable_shared; p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_shared=yes ;;
+ no) enable_shared=no ;;
+ *)
+ enable_shared=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ for pkg in $enableval; do
+ IFS="$lt_save_ifs"
+ if test "X$pkg" = "X$p"; then
+ enable_shared=yes
+ fi
+ done
+ IFS="$lt_save_ifs"
+ ;;
+ esac
+else
+ enable_shared=yes
+fi
+
+
+
+
+
+
+
+
+
+
+
+# Check whether --with-pic was given.
+if test "${with_pic+set}" = set; then
+ withval=$with_pic; pic_mode="$withval"
+else
+ pic_mode=default
+fi
+
+
+test -z "$pic_mode" && pic_mode=default
+
+
+
+
+
+
+
+ # Check whether --enable-fast-install was given.
+if test "${enable_fast_install+set}" = set; then
+ enableval=$enable_fast_install; p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_fast_install=yes ;;
+ no) enable_fast_install=no ;;
+ *)
+ enable_fast_install=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ for pkg in $enableval; do
+ IFS="$lt_save_ifs"
+ if test "X$pkg" = "X$p"; then
+ enable_fast_install=yes
+ fi
+ done
+ IFS="$lt_save_ifs"
+ ;;
+ esac
+else
+ enable_fast_install=yes
+fi
+
+
+
+
+
+
+
+
+
+
+
+# This can be used to rebuild libtool when needed
+LIBTOOL_DEPS="$ltmain"
+
+# Always use our own libtool.
+LIBTOOL='$(SHELL) $(top_builddir)/libtool'
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+test -z "$LN_S" && LN_S="ln -s"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+if test -n "${ZSH_VERSION+set}" ; then
+ setopt NO_GLOB_SUBST
+fi
+
+{ $as_echo "$as_me:$LINENO: checking for objdir" >&5
+$as_echo_n "checking for objdir... " >&6; }
+if test "${lt_cv_objdir+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ rm -f .libs 2>/dev/null
+mkdir .libs 2>/dev/null
+if test -d .libs; then
+ lt_cv_objdir=.libs
+else
+ # MS-DOS does not allow filenames that begin with a dot.
+ lt_cv_objdir=_libs
+fi
+rmdir .libs 2>/dev/null
+fi
+{ $as_echo "$as_me:$LINENO: result: $lt_cv_objdir" >&5
+$as_echo "$lt_cv_objdir" >&6; }
+objdir=$lt_cv_objdir
+
+
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define LT_OBJDIR "$lt_cv_objdir/"
+_ACEOF
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+case $host_os in
+aix3*)
+ # AIX sometimes has problems with the GCC collect2 program. For some
+ # reason, if we set the COLLECT_NAMES environment variable, the problems
+ # vanish in a puff of smoke.
+ if test "X${COLLECT_NAMES+set}" != Xset; then
+ COLLECT_NAMES=
+ export COLLECT_NAMES
+ fi
+ ;;
+esac
+
+# Sed substitution that helps us do robust quoting. It backslashifies
+# metacharacters that are still active within double-quoted strings.
+sed_quote_subst='s/\(["`$\\]\)/\\\1/g'
+
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\(["`\\]\)/\\\1/g'
+
+# Sed substitution to delay expansion of an escaped shell variable in a
+# double_quote_subst'ed string.
+delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
+
+# Sed substitution to delay expansion of an escaped single quote.
+delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g'
+
+# Sed substitution to avoid accidental globbing in evaled expressions
+no_glob_subst='s/\*/\\\*/g'
+
+# Global variables:
+ofile=libtool
+can_build_shared=yes
+
+# All known linkers require a `.a' archive for static linking (except MSVC,
+# which needs '.lib').
+libext=a
+
+with_gnu_ld="$lt_cv_prog_gnu_ld"
+
+old_CC="$CC"
+old_CFLAGS="$CFLAGS"
+
+# Set sane defaults for various variables
+test -z "$CC" && CC=cc
+test -z "$LTCC" && LTCC=$CC
+test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS
+test -z "$LD" && LD=ld
+test -z "$ac_objext" && ac_objext=o
+
+for cc_temp in $compiler""; do
+ case $cc_temp in
+ compile | *[\\/]compile | ccache | *[\\/]ccache ) ;;
+ distcc | *[\\/]distcc | purify | *[\\/]purify ) ;;
+ \-*) ;;
+ *) break;;
+ esac
+done
+cc_basename=`$ECHO "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"`
+
+
+# Only perform the check for file, if the check method requires it
+test -z "$MAGIC_CMD" && MAGIC_CMD=file
+case $deplibs_check_method in
+file_magic*)
+ if test "$file_magic_cmd" = '$MAGIC_CMD'; then
+ { $as_echo "$as_me:$LINENO: checking for ${ac_tool_prefix}file" >&5
+$as_echo_n "checking for ${ac_tool_prefix}file... " >&6; }
+if test "${lt_cv_path_MAGIC_CMD+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ case $MAGIC_CMD in
+[\\/*] | ?:[\\/]*)
+ lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+ ;;
+*)
+ lt_save_MAGIC_CMD="$MAGIC_CMD"
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ ac_dummy="/usr/bin$PATH_SEPARATOR$PATH"
+ for ac_dir in $ac_dummy; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/${ac_tool_prefix}file; then
+ lt_cv_path_MAGIC_CMD="$ac_dir/${ac_tool_prefix}file"
+ if test -n "$file_magic_test_file"; then
+ case $deplibs_check_method in
+ "file_magic "*)
+ file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+ MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+ if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+ $EGREP "$file_magic_regex" > /dev/null; then
+ :
+ else
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such. This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem. Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+ fi ;;
+ esac
+ fi
+ break
+ fi
+ done
+ IFS="$lt_save_ifs"
+ MAGIC_CMD="$lt_save_MAGIC_CMD"
+ ;;
+esac
+fi
+
+MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+if test -n "$MAGIC_CMD"; then
+ { $as_echo "$as_me:$LINENO: result: $MAGIC_CMD" >&5
+$as_echo "$MAGIC_CMD" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+
+
+if test -z "$lt_cv_path_MAGIC_CMD"; then
+ if test -n "$ac_tool_prefix"; then
+ { $as_echo "$as_me:$LINENO: checking for file" >&5
+$as_echo_n "checking for file... " >&6; }
+if test "${lt_cv_path_MAGIC_CMD+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ case $MAGIC_CMD in
+[\\/*] | ?:[\\/]*)
+ lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+ ;;
+*)
+ lt_save_MAGIC_CMD="$MAGIC_CMD"
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ ac_dummy="/usr/bin$PATH_SEPARATOR$PATH"
+ for ac_dir in $ac_dummy; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/file; then
+ lt_cv_path_MAGIC_CMD="$ac_dir/file"
+ if test -n "$file_magic_test_file"; then
+ case $deplibs_check_method in
+ "file_magic "*)
+ file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+ MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+ if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+ $EGREP "$file_magic_regex" > /dev/null; then
+ :
+ else
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such. This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem. Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+ fi ;;
+ esac
+ fi
+ break
+ fi
+ done
+ IFS="$lt_save_ifs"
+ MAGIC_CMD="$lt_save_MAGIC_CMD"
+ ;;
+esac
+fi
+
+MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+if test -n "$MAGIC_CMD"; then
+ { $as_echo "$as_me:$LINENO: result: $MAGIC_CMD" >&5
+$as_echo "$MAGIC_CMD" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ else
+ MAGIC_CMD=:
+ fi
+fi
+
+ fi
+ ;;
+esac
+
+# Use C for the default configuration in the libtool script
+
+lt_save_CC="$CC"
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+# Source file extension for C test sources.
+ac_ext=c
+
+# Object file extension for compiled C test sources.
+objext=o
+objext=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="int some_variable = 0;"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='int main(){return(0);}'
+
+
+
+
+
+
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+
+# Save the default compiler, since it gets overwritten when the other
+# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP.
+compiler_DEFAULT=$CC
+
+# save warnings/boilerplate of simple test code
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_compile_test_code" >conftest.$ac_ext
+eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_compiler_boilerplate=`cat conftest.err`
+$RM conftest*
+
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_link_test_code" >conftest.$ac_ext
+eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_linker_boilerplate=`cat conftest.err`
+$RM -r conftest*
+
+
+if test -n "$compiler"; then
+
+lt_prog_compiler_no_builtin_flag=
+
+if test "$GCC" = yes; then
+ lt_prog_compiler_no_builtin_flag=' -fno-builtin'
+
+ { $as_echo "$as_me:$LINENO: checking if $compiler supports -fno-rtti -fno-exceptions" >&5
+$as_echo_n "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; }
+if test "${lt_cv_prog_compiler_rtti_exceptions+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_rtti_exceptions=no
+ ac_outfile=conftest.$ac_objext
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+ lt_compiler_flag="-fno-rtti -fno-exceptions"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ # The option is referenced via a variable to avoid confusing sed.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:8410: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>conftest.err)
+ ac_status=$?
+ cat conftest.err >&5
+ echo "$as_me:8414: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s "$ac_outfile"; then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings other than the usual output.
+ $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_rtti_exceptions=yes
+ fi
+ fi
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_rtti_exceptions" >&5
+$as_echo "$lt_cv_prog_compiler_rtti_exceptions" >&6; }
+
+if test x"$lt_cv_prog_compiler_rtti_exceptions" = xyes; then
+ lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions"
+else
+ :
+fi
+
+fi
+
+
+
+
+
+
+ lt_prog_compiler_wl=
+lt_prog_compiler_pic=
+lt_prog_compiler_static=
+
+{ $as_echo "$as_me:$LINENO: checking for $compiler option to produce PIC" >&5
+$as_echo_n "checking for $compiler option to produce PIC... " >&6; }
+
+ if test "$GCC" = yes; then
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_static='-static'
+
+ case $host_os in
+ aix*)
+ # All AIX code is PIC.
+ if test "$host_cpu" = ia64; then
+ # AIX 5 now supports IA64 processor
+ lt_prog_compiler_static='-Bstatic'
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ lt_prog_compiler_pic='-fPIC'
+ ;;
+ m68k)
+ # FIXME: we need at least 68020 code to build shared libraries, but
+ # adding the `-m68020' flag to GCC prevents building anything better,
+ # like `-m68040'.
+ lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4'
+ ;;
+ esac
+ ;;
+
+ beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+ # PIC is the default for these OSes.
+ ;;
+
+ mingw* | cygwin* | pw32* | os2* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ # Although the cygwin gcc ignores -fPIC, still need this for old-style
+ # (--disable-auto-import) libraries
+ lt_prog_compiler_pic='-DDLL_EXPORT'
+ ;;
+
+ darwin* | rhapsody*)
+ # PIC is the default on this platform
+ # Common symbols not allowed in MH_DYLIB files
+ lt_prog_compiler_pic='-fno-common'
+ ;;
+
+ hpux*)
+ # PIC is the default for 64-bit PA HP-UX, but not for 32-bit
+ # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag
+ # sets the default TLS model and affects inlining.
+ case $host_cpu in
+ hppa*64*)
+ # +Z the default
+ ;;
+ *)
+ lt_prog_compiler_pic='-fPIC'
+ ;;
+ esac
+ ;;
+
+ interix[3-9]*)
+ # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+ # Instead, we relocate shared libraries at runtime.
+ ;;
+
+ msdosdjgpp*)
+ # Just because we use GCC doesn't mean we suddenly get shared libraries
+ # on systems that don't support them.
+ lt_prog_compiler_can_build_shared=no
+ enable_shared=no
+ ;;
+
+ *nto* | *qnx*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ lt_prog_compiler_pic='-fPIC -shared'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ lt_prog_compiler_pic=-Kconform_pic
+ fi
+ ;;
+
+ *)
+ lt_prog_compiler_pic='-fPIC'
+ ;;
+ esac
+ else
+ # PORTME Check for flag to pass linker flags through the system compiler.
+ case $host_os in
+ aix*)
+ lt_prog_compiler_wl='-Wl,'
+ if test "$host_cpu" = ia64; then
+ # AIX 5 now supports IA64 processor
+ lt_prog_compiler_static='-Bstatic'
+ else
+ lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp'
+ fi
+ ;;
+
+ mingw* | cygwin* | pw32* | os2* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ lt_prog_compiler_pic='-DDLL_EXPORT'
+ ;;
+
+ hpux9* | hpux10* | hpux11*)
+ lt_prog_compiler_wl='-Wl,'
+ # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+ # not for PA HP-UX.
+ case $host_cpu in
+ hppa*64*|ia64*)
+ # +Z the default
+ ;;
+ *)
+ lt_prog_compiler_pic='+Z'
+ ;;
+ esac
+ # Is there a better lt_prog_compiler_static that works with the bundled CC?
+ lt_prog_compiler_static='${wl}-a ${wl}archive'
+ ;;
+
+ irix5* | irix6* | nonstopux*)
+ lt_prog_compiler_wl='-Wl,'
+ # PIC (with -KPIC) is the default.
+ lt_prog_compiler_static='-non_shared'
+ ;;
+
+ linux* | k*bsd*-gnu)
+ case $cc_basename in
+ # old Intel for x86_64 which still supported -KPIC.
+ ecc*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-static'
+ ;;
+ # icc used to be incompatible with GCC.
+ # ICC 10 doesn't accept -KPIC any more.
+ icc* | ifort*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-fPIC'
+ lt_prog_compiler_static='-static'
+ ;;
+ # Lahey Fortran 8.1.
+ lf95*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='--shared'
+ lt_prog_compiler_static='--static'
+ ;;
+ pgcc* | pgf77* | pgf90* | pgf95*)
+ # Portland Group compilers (*not* the Pentium gcc compiler,
+ # which looks to be a dead project)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-fpic'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+ ccc*)
+ lt_prog_compiler_wl='-Wl,'
+ # All Alpha code is PIC.
+ lt_prog_compiler_static='-non_shared'
+ ;;
+ xl*)
+ # IBM XL C 8.0/Fortran 10.1 on PPC
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-qpic'
+ lt_prog_compiler_static='-qstaticlink'
+ ;;
+ *)
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*)
+ # Sun C 5.9
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ lt_prog_compiler_wl='-Wl,'
+ ;;
+ *Sun\ F*)
+ # Sun Fortran 8.3 passes all unrecognized flags to the linker
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ lt_prog_compiler_wl=''
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+
+ newsos6)
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ *nto* | *qnx*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ lt_prog_compiler_pic='-fPIC -shared'
+ ;;
+
+ osf3* | osf4* | osf5*)
+ lt_prog_compiler_wl='-Wl,'
+ # All OSF/1 code is PIC.
+ lt_prog_compiler_static='-non_shared'
+ ;;
+
+ rdos*)
+ lt_prog_compiler_static='-non_shared'
+ ;;
+
+ solaris*)
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ case $cc_basename in
+ f77* | f90* | f95*)
+ lt_prog_compiler_wl='-Qoption ld ';;
+ *)
+ lt_prog_compiler_wl='-Wl,';;
+ esac
+ ;;
+
+ sunos4*)
+ lt_prog_compiler_wl='-Qoption ld '
+ lt_prog_compiler_pic='-PIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ sysv4 | sysv4.2uw2* | sysv4.3*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec ;then
+ lt_prog_compiler_pic='-Kconform_pic'
+ lt_prog_compiler_static='-Bstatic'
+ fi
+ ;;
+
+ sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ unicos*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_can_build_shared=no
+ ;;
+
+ uts4*)
+ lt_prog_compiler_pic='-pic'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ *)
+ lt_prog_compiler_can_build_shared=no
+ ;;
+ esac
+ fi
+
+case $host_os in
+ # For platforms which do not support PIC, -DPIC is meaningless:
+ *djgpp*)
+ lt_prog_compiler_pic=
+ ;;
+ *)
+ lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC"
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: result: $lt_prog_compiler_pic" >&5
+$as_echo "$lt_prog_compiler_pic" >&6; }
+
+
+
+
+
+
+#
+# Check to make sure the PIC flag actually works.
+#
+if test -n "$lt_prog_compiler_pic"; then
+ { $as_echo "$as_me:$LINENO: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5
+$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; }
+if test "${lt_cv_prog_compiler_pic_works+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_pic_works=no
+ ac_outfile=conftest.$ac_objext
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+ lt_compiler_flag="$lt_prog_compiler_pic -DPIC"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ # The option is referenced via a variable to avoid confusing sed.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:8749: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>conftest.err)
+ ac_status=$?
+ cat conftest.err >&5
+ echo "$as_me:8753: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s "$ac_outfile"; then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings other than the usual output.
+ $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_pic_works=yes
+ fi
+ fi
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_pic_works" >&5
+$as_echo "$lt_cv_prog_compiler_pic_works" >&6; }
+
+if test x"$lt_cv_prog_compiler_pic_works" = xyes; then
+ case $lt_prog_compiler_pic in
+ "" | " "*) ;;
+ *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;;
+ esac
+else
+ lt_prog_compiler_pic=
+ lt_prog_compiler_can_build_shared=no
+fi
+
+fi
+
+
+
+
+
+
+#
+# Check to make sure the static flag actually works.
+#
+wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\"
+{ $as_echo "$as_me:$LINENO: checking if $compiler static flag $lt_tmp_static_flag works" >&5
+$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; }
+if test "${lt_cv_prog_compiler_static_works+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_static_works=no
+ save_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS $lt_tmp_static_flag"
+ echo "$lt_simple_link_test_code" > conftest.$ac_ext
+ if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+ # The linker can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ if test -s conftest.err; then
+ # Append any errors to the config.log.
+ cat conftest.err 1>&5
+ $ECHO "X$_lt_linker_boilerplate" | $Xsed -e '/^$/d' > conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if diff conftest.exp conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_static_works=yes
+ fi
+ else
+ lt_cv_prog_compiler_static_works=yes
+ fi
+ fi
+ $RM -r conftest*
+ LDFLAGS="$save_LDFLAGS"
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_static_works" >&5
+$as_echo "$lt_cv_prog_compiler_static_works" >&6; }
+
+if test x"$lt_cv_prog_compiler_static_works" = xyes; then
+ :
+else
+ lt_prog_compiler_static=
+fi
+
+
+
+
+
+
+
+ { $as_echo "$as_me:$LINENO: checking if $compiler supports -c -o file.$ac_objext" >&5
+$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
+if test "${lt_cv_prog_compiler_c_o+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_c_o=no
+ $RM -r conftest 2>/dev/null
+ mkdir conftest
+ cd conftest
+ mkdir out
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ lt_compiler_flag="-o out/conftest2.$ac_objext"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:8854: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>out/conftest.err)
+ ac_status=$?
+ cat out/conftest.err >&5
+ echo "$as_me:8858: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s out/conftest2.$ac_objext
+ then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp
+ $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+ if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_c_o=yes
+ fi
+ fi
+ chmod u+w . 2>&5
+ $RM conftest*
+ # SGI C++ compiler will create directory out/ii_files/ for
+ # template instantiation
+ test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+ $RM out/* && rmdir out
+ cd ..
+ $RM -r conftest
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_c_o" >&5
+$as_echo "$lt_cv_prog_compiler_c_o" >&6; }
+
+
+
+
+
+
+ { $as_echo "$as_me:$LINENO: checking if $compiler supports -c -o file.$ac_objext" >&5
+$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
+if test "${lt_cv_prog_compiler_c_o+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_c_o=no
+ $RM -r conftest 2>/dev/null
+ mkdir conftest
+ cd conftest
+ mkdir out
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ lt_compiler_flag="-o out/conftest2.$ac_objext"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:8909: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>out/conftest.err)
+ ac_status=$?
+ cat out/conftest.err >&5
+ echo "$as_me:8913: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s out/conftest2.$ac_objext
+ then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp
+ $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+ if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_c_o=yes
+ fi
+ fi
+ chmod u+w . 2>&5
+ $RM conftest*
+ # SGI C++ compiler will create directory out/ii_files/ for
+ # template instantiation
+ test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+ $RM out/* && rmdir out
+ cd ..
+ $RM -r conftest
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_c_o" >&5
+$as_echo "$lt_cv_prog_compiler_c_o" >&6; }
+
+
+
+
+hard_links="nottested"
+if test "$lt_cv_prog_compiler_c_o" = no && test "$need_locks" != no; then
+ # do not overwrite the value of need_locks provided by the user
+ { $as_echo "$as_me:$LINENO: checking if we can lock with hard links" >&5
+$as_echo_n "checking if we can lock with hard links... " >&6; }
+ hard_links=yes
+ $RM conftest*
+ ln conftest.a conftest.b 2>/dev/null && hard_links=no
+ touch conftest.a
+ ln conftest.a conftest.b 2>&5 || hard_links=no
+ ln conftest.a conftest.b 2>/dev/null && hard_links=no
+ { $as_echo "$as_me:$LINENO: result: $hard_links" >&5
+$as_echo "$hard_links" >&6; }
+ if test "$hard_links" = no; then
+ { $as_echo "$as_me:$LINENO: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5
+$as_echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;}
+ need_locks=warn
+ fi
+else
+ need_locks=no
+fi
+
+
+
+
+
+
+ { $as_echo "$as_me:$LINENO: checking whether the $compiler linker ($LD) supports shared libraries" >&5
+$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; }
+
+ runpath_var=
+ allow_undefined_flag=
+ always_export_symbols=no
+ archive_cmds=
+ archive_expsym_cmds=
+ compiler_needs_object=no
+ enable_shared_with_static_runtimes=no
+ export_dynamic_flag_spec=
+ export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+ hardcode_automatic=no
+ hardcode_direct=no
+ hardcode_direct_absolute=no
+ hardcode_libdir_flag_spec=
+ hardcode_libdir_flag_spec_ld=
+ hardcode_libdir_separator=
+ hardcode_minus_L=no
+ hardcode_shlibpath_var=unsupported
+ inherit_rpath=no
+ link_all_deplibs=unknown
+ module_cmds=
+ module_expsym_cmds=
+ old_archive_from_new_cmds=
+ old_archive_from_expsyms_cmds=
+ thread_safe_flag_spec=
+ whole_archive_flag_spec=
+ # include_expsyms should be a list of space-separated symbols to be *always*
+ # included in the symbol list
+ include_expsyms=
+ # exclude_expsyms can be an extended regexp of symbols to exclude
+ # it will be wrapped by ` (' and `)$', so one must not match beginning or
+ # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc',
+ # as well as any symbol that contains `d'.
+ exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'
+ # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
+ # platforms (ab)use it in PIC code, but their linkers get confused if
+ # the symbol is explicitly referenced. Since portable code cannot
+ # rely on this symbol name, it's probably fine to never include it in
+ # preloaded symbol tables.
+ # Exclude shared library initialization/finalization symbols.
+ extract_expsyms_cmds=
+
+ case $host_os in
+ cygwin* | mingw* | pw32* | cegcc*)
+ # FIXME: the MSVC++ port hasn't been tested in a loooong time
+ # When not using gcc, we currently assume that we are using
+ # Microsoft Visual C++.
+ if test "$GCC" != yes; then
+ with_gnu_ld=no
+ fi
+ ;;
+ interix*)
+ # we just hope/assume this is gcc and not c89 (= MSVC++)
+ with_gnu_ld=yes
+ ;;
+ openbsd*)
+ with_gnu_ld=no
+ ;;
+ esac
+
+ ld_shlibs=yes
+ if test "$with_gnu_ld" = yes; then
+ # If archive_cmds runs LD, not CC, wlarc should be empty
+ wlarc='${wl}'
+
+ # Set some defaults for GNU ld with shared library support. These
+ # are reset later if shared libraries are not supported. Putting them
+ # here allows them to be overridden if necessary.
+ runpath_var=LD_RUN_PATH
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ export_dynamic_flag_spec='${wl}--export-dynamic'
+ # ancient GNU ld didn't support --whole-archive et. al.
+ if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then
+ whole_archive_flag_spec="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+ else
+ whole_archive_flag_spec=
+ fi
+ supports_anon_versioning=no
+ case `$LD -v 2>&1` in
+ *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11
+ *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ...
+ *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ...
+ *\ 2.11.*) ;; # other 2.11 versions
+ *) supports_anon_versioning=yes ;;
+ esac
+
+ # See if GNU ld supports shared libraries.
+ case $host_os in
+ aix[3-9]*)
+ # On AIX/PPC, the GNU linker is very broken
+ if test "$host_cpu" != ia64; then
+ ld_shlibs=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the GNU linker, at least up to release 2.9.1, is reported
+*** to be unable to reliably create shared libraries on AIX.
+*** Therefore, libtool is disabling shared libraries support. If you
+*** really care for shared libraries, you may want to modify your PATH
+*** so that a non-GNU linker is found, and then restart.
+
+_LT_EOF
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds=''
+ ;;
+ m68k)
+ archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_minus_L=yes
+ ;;
+ esac
+ ;;
+
+ beos*)
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ allow_undefined_flag=unsupported
+ # Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+ # support --undefined. This deserves some investigation. FIXME
+ archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ cygwin* | mingw* | pw32* | cegcc*)
+ # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless,
+ # as there is no search path for DLLs.
+ hardcode_libdir_flag_spec='-L$libdir'
+ allow_undefined_flag=unsupported
+ always_export_symbols=no
+ enable_shared_with_static_runtimes=yes
+ export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols'
+
+ if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ # If the export-symbols file already is a .def file (1st line
+ # is EXPORTS), use it as is; otherwise, prepend...
+ archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+ cp $export_symbols $output_objdir/$soname.def;
+ else
+ echo EXPORTS > $output_objdir/$soname.def;
+ cat $export_symbols >> $output_objdir/$soname.def;
+ fi~
+ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ interix[3-9]*)
+ hardcode_direct=no
+ hardcode_shlibpath_var=no
+ hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+ export_dynamic_flag_spec='${wl}-E'
+ # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+ # Instead, shared libraries are loaded at an image base (0x10000000 by
+ # default) and relocated if they conflict, which is a slow very memory
+ # consuming and fragmenting process. To avoid this, we pick a random,
+ # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+ # time. Moving up from 0x10000000 also allows more sbrk(2) space.
+ archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ archive_expsym_cmds='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ ;;
+
+ gnu* | linux* | tpf* | k*bsd*-gnu)
+ tmp_diet=no
+ if test "$host_os" = linux-dietlibc; then
+ case $cc_basename in
+ diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn)
+ esac
+ fi
+ if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \
+ && test "$tmp_diet" = no
+ then
+ tmp_addflag=
+ tmp_sharedflag='-shared'
+ case $cc_basename,$host_cpu in
+ pgcc*) # Portland Group C compiler
+ whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+ tmp_addflag=' $pic_flag'
+ ;;
+ pgf77* | pgf90* | pgf95*) # Portland Group f77 and f90 compilers
+ whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+ tmp_addflag=' $pic_flag -Mnomain' ;;
+ ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64
+ tmp_addflag=' -i_dynamic' ;;
+ efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64
+ tmp_addflag=' -i_dynamic -nofor_main' ;;
+ ifc* | ifort*) # Intel Fortran compiler
+ tmp_addflag=' -nofor_main' ;;
+ lf95*) # Lahey Fortran 8.1
+ whole_archive_flag_spec=
+ tmp_sharedflag='--shared' ;;
+ xl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below)
+ tmp_sharedflag='-qmkshrobj'
+ tmp_addflag= ;;
+ esac
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*) # Sun C 5.9
+ whole_archive_flag_spec='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+ compiler_needs_object=yes
+ tmp_sharedflag='-G' ;;
+ *Sun\ F*) # Sun Fortran 8.3
+ tmp_sharedflag='-G' ;;
+ esac
+ archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+
+ if test "x$supports_anon_versioning" = xyes; then
+ archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
+ fi
+
+ case $cc_basename in
+ xlf*)
+ # IBM XL Fortran 10.1 on PPC cannot create shared libs itself
+ whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive'
+ hardcode_libdir_flag_spec=
+ hardcode_libdir_flag_spec_ld='-rpath $libdir'
+ archive_cmds='$LD -shared $libobjs $deplibs $compiler_flags -soname $soname -o $lib'
+ if test "x$supports_anon_versioning" = xyes; then
+ archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $LD -shared $libobjs $deplibs $compiler_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib'
+ fi
+ ;;
+ esac
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
+ wlarc=
+ else
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ fi
+ ;;
+
+ solaris*)
+ if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then
+ ld_shlibs=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: The releases 2.8.* of the GNU linker cannot reliably
+*** create shared libraries on Solaris systems. Therefore, libtool
+*** is disabling shared libraries support. We urge you to upgrade GNU
+*** binutils to release 2.9.1 or newer. Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+ elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*)
+ case `$LD -v 2>&1` in
+ *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*)
+ ld_shlibs=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not
+*** reliably create shared libraries on SCO systems. Therefore, libtool
+*** is disabling shared libraries support. We urge you to upgrade GNU
+*** binutils to release 2.16.91.0.3 or newer. Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+ ;;
+ *)
+ # For security reasons, it is highly recommended that you always
+ # use absolute paths for naming shared libraries, and exclude the
+ # DT_RUNPATH tag from executables and libraries. But doing so
+ # requires that you compile everything twice, which is a pain.
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+ esac
+ ;;
+
+ sunos4*)
+ archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ wlarc=
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ *)
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+ esac
+
+ if test "$ld_shlibs" = no; then
+ runpath_var=
+ hardcode_libdir_flag_spec=
+ export_dynamic_flag_spec=
+ whole_archive_flag_spec=
+ fi
+ else
+ # PORTME fill in a description of your system's linker (not GNU ld)
+ case $host_os in
+ aix3*)
+ allow_undefined_flag=unsupported
+ always_export_symbols=yes
+ archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname'
+ # Note: this linker hardcodes the directories in LIBPATH if there
+ # are no directories specified by -L.
+ hardcode_minus_L=yes
+ if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then
+ # Neither direct hardcoding nor static linking is supported with a
+ # broken collect2.
+ hardcode_direct=unsupported
+ fi
+ ;;
+
+ aix[4-9]*)
+ if test "$host_cpu" = ia64; then
+ # On IA64, the linker does run time linking by default, so we don't
+ # have to do anything special.
+ aix_use_runtimelinking=no
+ exp_sym_flag='-Bexport'
+ no_entry_flag=""
+ else
+ # If we're using GNU nm, then we don't want the "-C" option.
+ # -C means demangle to AIX nm, but means don't demangle with GNU nm
+ if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+ export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+ else
+ export_symbols_cmds='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+ fi
+ aix_use_runtimelinking=no
+
+ # Test if we are trying to use run time linking or normal
+ # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+ # need to do runtime linking.
+ case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*)
+ for ld_flag in $LDFLAGS; do
+ if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then
+ aix_use_runtimelinking=yes
+ break
+ fi
+ done
+ ;;
+ esac
+
+ exp_sym_flag='-bexport'
+ no_entry_flag='-bnoentry'
+ fi
+
+ # When large executables or shared objects are built, AIX ld can
+ # have problems creating the table of contents. If linking a library
+ # or program results in "error TOC overflow" add -mminimal-toc to
+ # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not
+ # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+ archive_cmds=''
+ hardcode_direct=yes
+ hardcode_direct_absolute=yes
+ hardcode_libdir_separator=':'
+ link_all_deplibs=yes
+ file_list_spec='${wl}-f,'
+
+ if test "$GCC" = yes; then
+ case $host_os in aix4.[012]|aix4.[012].*)
+ # We only want to do this on AIX 4.2 and lower, the check
+ # below for broken collect2 doesn't work under 4.3+
+ collect2name=`${CC} -print-prog-name=collect2`
+ if test -f "$collect2name" &&
+ strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+ then
+ # We have reworked collect2
+ :
+ else
+ # We have old collect2
+ hardcode_direct=unsupported
+ # It fails to find uninstalled libraries when the uninstalled
+ # path is not listed in the libpath. Setting hardcode_minus_L
+ # to unsupported forces relinking
+ hardcode_minus_L=yes
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_libdir_separator=
+ fi
+ ;;
+ esac
+ shared_flag='-shared'
+ if test "$aix_use_runtimelinking" = yes; then
+ shared_flag="$shared_flag "'${wl}-G'
+ fi
+ else
+ # not using gcc
+ if test "$host_cpu" = ia64; then
+ # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+ # chokes on -Wl,-G. The following line is correct:
+ shared_flag='-G'
+ else
+ if test "$aix_use_runtimelinking" = yes; then
+ shared_flag='${wl}-G'
+ else
+ shared_flag='${wl}-bM:SRE'
+ fi
+ fi
+ fi
+
+ export_dynamic_flag_spec='${wl}-bexpall'
+ # It seems that -bexpall does not export symbols beginning with
+ # underscore (_), so it is better to generate a list of symbols to export.
+ always_export_symbols=yes
+ if test "$aix_use_runtimelinking" = yes; then
+ # Warning - without using the other runtime loading flags (-brtl),
+ # -berok will link without error, but may produce a broken library.
+ allow_undefined_flag='-berok'
+ # Determine the default libpath from the value encoded in an
+ # empty executable.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+
+lt_aix_libpath_sed='
+ /Import File Strings/,/^$/ {
+ /^0/ {
+ s/^0 *\(.*\)$/\1/
+ p
+ }
+ }'
+aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+# Check for a 64-bit object if we didn't find anything.
+if test -z "$aix_libpath"; then
+ aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+fi
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
+
+ hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath"
+ archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then $ECHO "X${wl}${allow_undefined_flag}" | $Xsed; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+ else
+ if test "$host_cpu" = ia64; then
+ hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib'
+ allow_undefined_flag="-z nodefs"
+ archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols"
+ else
+ # Determine the default libpath from the value encoded in an
+ # empty executable.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+
+lt_aix_libpath_sed='
+ /Import File Strings/,/^$/ {
+ /^0/ {
+ s/^0 *\(.*\)$/\1/
+ p
+ }
+ }'
+aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+# Check for a 64-bit object if we didn't find anything.
+if test -z "$aix_libpath"; then
+ aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+fi
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
+
+ hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath"
+ # Warning - without using the other run time loading flags,
+ # -berok will link without error, but may produce a broken library.
+ no_undefined_flag=' ${wl}-bernotok'
+ allow_undefined_flag=' ${wl}-berok'
+ # Exported symbols can be pulled into shared objects from archives
+ whole_archive_flag_spec='$convenience'
+ archive_cmds_need_lc=yes
+ # This is similar to how AIX traditionally builds its shared libraries.
+ archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
+ fi
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds=''
+ ;;
+ m68k)
+ archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_minus_L=yes
+ ;;
+ esac
+ ;;
+
+ bsdi[45]*)
+ export_dynamic_flag_spec=-rdynamic
+ ;;
+
+ cygwin* | mingw* | pw32* | cegcc*)
+ # When not using gcc, we currently assume that we are using
+ # Microsoft Visual C++.
+ # hardcode_libdir_flag_spec is actually meaningless, as there is
+ # no search path for DLLs.
+ hardcode_libdir_flag_spec=' '
+ allow_undefined_flag=unsupported
+ # Tell ltmain to make .lib files, not .a files.
+ libext=lib
+ # Tell ltmain to make .dll files, not .so files.
+ shrext_cmds=".dll"
+ # FIXME: Setting linknames here is a bad hack.
+ archive_cmds='$CC -o $lib $libobjs $compiler_flags `$ECHO "X$deplibs" | $Xsed -e '\''s/ -lc$//'\''` -link -dll~linknames='
+ # The linker will automatically build a .lib file if we build a DLL.
+ old_archive_from_new_cmds='true'
+ # FIXME: Should let the user specify the lib program.
+ old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs'
+ fix_srcfile_path='`cygpath -w "$srcfile"`'
+ enable_shared_with_static_runtimes=yes
+ ;;
+
+ darwin* | rhapsody*)
+
+
+ archive_cmds_need_lc=no
+ hardcode_direct=no
+ hardcode_automatic=yes
+ hardcode_shlibpath_var=unsupported
+ whole_archive_flag_spec=''
+ link_all_deplibs=yes
+ allow_undefined_flag="$_lt_dar_allow_undefined"
+ case $cc_basename in
+ ifort*) _lt_dar_can_shared=yes ;;
+ *) _lt_dar_can_shared=$GCC ;;
+ esac
+ if test "$_lt_dar_can_shared" = "yes"; then
+ output_verbose_link_cmd=echo
+ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}"
+ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}"
+ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}"
+ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}"
+
+ else
+ ld_shlibs=no
+ fi
+
+ ;;
+
+ dgux*)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_shlibpath_var=no
+ ;;
+
+ freebsd1*)
+ ld_shlibs=no
+ ;;
+
+ # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
+ # support. Future versions do this automatically, but an explicit c++rt0.o
+ # does not break anything, and helps significantly (at the cost of a little
+ # extra space).
+ freebsd2.2*)
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o'
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ # Unfortunately, older versions of FreeBSD 2 do not have this feature.
+ freebsd2*)
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_direct=yes
+ hardcode_minus_L=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ # FreeBSD 3 and greater uses gcc -shared to do shared libraries.
+ freebsd* | dragonfly*)
+ archive_cmds='$CC -shared -o $lib $libobjs $deplibs $compiler_flags'
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ hpux9*)
+ if test "$GCC" = yes; then
+ archive_cmds='$RM $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+ else
+ archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+ fi
+ hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+ hardcode_libdir_separator=:
+ hardcode_direct=yes
+
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ hardcode_minus_L=yes
+ export_dynamic_flag_spec='${wl}-E'
+ ;;
+
+ hpux10*)
+ if test "$GCC" = yes -a "$with_gnu_ld" = no; then
+ archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
+ fi
+ if test "$with_gnu_ld" = no; then
+ hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+ hardcode_libdir_flag_spec_ld='+b $libdir'
+ hardcode_libdir_separator=:
+ hardcode_direct=yes
+ hardcode_direct_absolute=yes
+ export_dynamic_flag_spec='${wl}-E'
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ hardcode_minus_L=yes
+ fi
+ ;;
+
+ hpux11*)
+ if test "$GCC" = yes -a "$with_gnu_ld" = no; then
+ case $host_cpu in
+ hppa*64*)
+ archive_cmds='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ ia64*)
+ archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+ archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ else
+ case $host_cpu in
+ hppa*64*)
+ archive_cmds='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ ia64*)
+ archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+ archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ fi
+ if test "$with_gnu_ld" = no; then
+ hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+ hardcode_libdir_separator=:
+
+ case $host_cpu in
+ hppa*64*|ia64*)
+ hardcode_direct=no
+ hardcode_shlibpath_var=no
+ ;;
+ *)
+ hardcode_direct=yes
+ hardcode_direct_absolute=yes
+ export_dynamic_flag_spec='${wl}-E'
+
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ hardcode_minus_L=yes
+ ;;
+ esac
+ fi
+ ;;
+
+ irix5* | irix6* | nonstopux*)
+ if test "$GCC" = yes; then
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ # Try to use the -exported_symbol ld option, if it does not
+ # work, assume that -exports_file does not work either and
+ # implicitly export all symbols.
+ save_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null"
+ cat >conftest.$ac_ext <<_ACEOF
+int foo(void) {}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib'
+
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+ LDFLAGS="$save_LDFLAGS"
+ else
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib'
+ fi
+ archive_cmds_need_lc='no'
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ hardcode_libdir_separator=:
+ inherit_rpath=yes
+ link_all_deplibs=yes
+ ;;
+
+ netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out
+ else
+ archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF
+ fi
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ newsos6)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_direct=yes
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ hardcode_libdir_separator=:
+ hardcode_shlibpath_var=no
+ ;;
+
+ *nto* | *qnx*)
+ ;;
+
+ openbsd*)
+ if test -f /usr/libexec/ld.so; then
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ hardcode_direct_absolute=yes
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+ archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols'
+ hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+ export_dynamic_flag_spec='${wl}-E'
+ else
+ case $host_os in
+ openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*)
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_libdir_flag_spec='-R$libdir'
+ ;;
+ *)
+ archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+ ;;
+ esac
+ fi
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ os2*)
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_minus_L=yes
+ allow_undefined_flag=unsupported
+ archive_cmds='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$ECHO DATA >> $output_objdir/$libname.def~$ECHO " SINGLE NONSHARED" >> $output_objdir/$libname.def~$ECHO EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def'
+ old_archive_from_new_cmds='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def'
+ ;;
+
+ osf3*)
+ if test "$GCC" = yes; then
+ allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*'
+ archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ else
+ allow_undefined_flag=' -expect_unresolved \*'
+ archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+ fi
+ archive_cmds_need_lc='no'
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ hardcode_libdir_separator=:
+ ;;
+
+ osf4* | osf5*) # as osf3* with the addition of -msym flag
+ if test "$GCC" = yes; then
+ allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*'
+ archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ else
+ allow_undefined_flag=' -expect_unresolved \*'
+ archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+ archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~
+ $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp'
+
+ # Both c and cxx compiler support -rpath directly
+ hardcode_libdir_flag_spec='-rpath $libdir'
+ fi
+ archive_cmds_need_lc='no'
+ hardcode_libdir_separator=:
+ ;;
+
+ solaris*)
+ no_undefined_flag=' -z defs'
+ if test "$GCC" = yes; then
+ wlarc='${wl}'
+ archive_cmds='$CC -shared ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -shared ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+ else
+ case `$CC -V 2>&1` in
+ *"Compilers 5.0"*)
+ wlarc=''
+ archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp'
+ ;;
+ *)
+ wlarc='${wl}'
+ archive_cmds='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+ ;;
+ esac
+ fi
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_shlibpath_var=no
+ case $host_os in
+ solaris2.[0-5] | solaris2.[0-5].*) ;;
+ *)
+ # The compiler driver will combine and reorder linker options,
+ # but understands `-z linker_flag'. GCC discards it without `$wl',
+ # but is careful enough not to reorder.
+ # Supported since Solaris 2.6 (maybe 2.5.1?)
+ if test "$GCC" = yes; then
+ whole_archive_flag_spec='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract'
+ else
+ whole_archive_flag_spec='-z allextract$convenience -z defaultextract'
+ fi
+ ;;
+ esac
+ link_all_deplibs=yes
+ ;;
+
+ sunos4*)
+ if test "x$host_vendor" = xsequent; then
+ # Use $CC to link under sequent, because it throws in some extra .o
+ # files that make .init and .fini sections work.
+ archive_cmds='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags'
+ fi
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_direct=yes
+ hardcode_minus_L=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ sysv4)
+ case $host_vendor in
+ sni)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_direct=yes # is this really true???
+ ;;
+ siemens)
+ ## LD is ld it makes a PLAMLIB
+ ## CC just makes a GrossModule.
+ archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags'
+ reload_cmds='$CC -r -o $output$reload_objs'
+ hardcode_direct=no
+ ;;
+ motorola)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_direct=no #Motorola manual says yes, but my tests say they lie
+ ;;
+ esac
+ runpath_var='LD_RUN_PATH'
+ hardcode_shlibpath_var=no
+ ;;
+
+ sysv4.3*)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_shlibpath_var=no
+ export_dynamic_flag_spec='-Bexport'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_shlibpath_var=no
+ runpath_var=LD_RUN_PATH
+ hardcode_runpath_var=yes
+ ld_shlibs=yes
+ fi
+ ;;
+
+ sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*)
+ no_undefined_flag='${wl}-z,text'
+ archive_cmds_need_lc=no
+ hardcode_shlibpath_var=no
+ runpath_var='LD_RUN_PATH'
+
+ if test "$GCC" = yes; then
+ archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ fi
+ ;;
+
+ sysv5* | sco3.2v5* | sco5v6*)
+ # Note: We can NOT use -z defs as we might desire, because we do not
+ # link with -lc, and that would cause any symbols used from libc to
+ # always be unresolved, which means just about no library would
+ # ever link correctly. If we're not using GNU ld we use -z text
+ # though, which does catch some bad symbols but isn't as heavy-handed
+ # as -z defs.
+ no_undefined_flag='${wl}-z,text'
+ allow_undefined_flag='${wl}-z,nodefs'
+ archive_cmds_need_lc=no
+ hardcode_shlibpath_var=no
+ hardcode_libdir_flag_spec='${wl}-R,$libdir'
+ hardcode_libdir_separator=':'
+ link_all_deplibs=yes
+ export_dynamic_flag_spec='${wl}-Bexport'
+ runpath_var='LD_RUN_PATH'
+
+ if test "$GCC" = yes; then
+ archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ fi
+ ;;
+
+ uts4*)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_shlibpath_var=no
+ ;;
+
+ *)
+ ld_shlibs=no
+ ;;
+ esac
+
+ if test x$host_vendor = xsni; then
+ case $host in
+ sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+ export_dynamic_flag_spec='${wl}-Blargedynsym'
+ ;;
+ esac
+ fi
+ fi
+
+{ $as_echo "$as_me:$LINENO: result: $ld_shlibs" >&5
+$as_echo "$ld_shlibs" >&6; }
+test "$ld_shlibs" = no && can_build_shared=no
+
+with_gnu_ld=$with_gnu_ld
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#
+# Do we need to explicitly link libc?
+#
+case "x$archive_cmds_need_lc" in
+x|xyes)
+ # Assume -lc should be added
+ archive_cmds_need_lc=yes
+
+ if test "$enable_shared" = yes && test "$GCC" = yes; then
+ case $archive_cmds in
+ *'~'*)
+ # FIXME: we may have to deal with multi-command sequences.
+ ;;
+ '$CC '*)
+ # Test whether the compiler implicitly links with -lc since on some
+ # systems, -lgcc has to come before -lc. If gcc already passes -lc
+ # to ld, don't add -lc before -lgcc.
+ { $as_echo "$as_me:$LINENO: checking whether -lc should be explicitly linked in" >&5
+$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; }
+ $RM conftest*
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } 2>conftest.err; then
+ soname=conftest
+ lib=conftest
+ libobjs=conftest.$ac_objext
+ deplibs=
+ wl=$lt_prog_compiler_wl
+ pic_flag=$lt_prog_compiler_pic
+ compiler_flags=-v
+ linker_flags=-v
+ verstring=
+ output_objdir=.
+ libname=conftest
+ lt_save_allow_undefined_flag=$allow_undefined_flag
+ allow_undefined_flag=
+ if { (eval echo "$as_me:$LINENO: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\"") >&5
+ (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+ then
+ archive_cmds_need_lc=no
+ else
+ archive_cmds_need_lc=yes
+ fi
+ allow_undefined_flag=$lt_save_allow_undefined_flag
+ else
+ cat conftest.err 1>&5
+ fi
+ $RM conftest*
+ { $as_echo "$as_me:$LINENO: result: $archive_cmds_need_lc" >&5
+$as_echo "$archive_cmds_need_lc" >&6; }
+ ;;
+ esac
+ fi
+ ;;
+esac
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ { $as_echo "$as_me:$LINENO: checking dynamic linker characteristics" >&5
+$as_echo_n "checking dynamic linker characteristics... " >&6; }
+
+if test "$GCC" = yes; then
+ case $host_os in
+ darwin*) lt_awk_arg="/^libraries:/,/LR/" ;;
+ *) lt_awk_arg="/^libraries:/" ;;
+ esac
+ lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e "s,=/,/,g"`
+ if $ECHO "$lt_search_path_spec" | $GREP ';' >/dev/null ; then
+ # if the path contains ";" then we assume it to be the separator
+ # otherwise default to the standard path separator (i.e. ":") - it is
+ # assumed that no part of a normal pathname contains ";" but that should
+ # okay in the real world where ";" in dirpaths is itself problematic.
+ lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e 's/;/ /g'`
+ else
+ lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+ fi
+ # Ok, now we have the path, separated by spaces, we can step through it
+ # and add multilib dir if necessary.
+ lt_tmp_lt_search_path_spec=
+ lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null`
+ for lt_sys_path in $lt_search_path_spec; do
+ if test -d "$lt_sys_path/$lt_multi_os_dir"; then
+ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir"
+ else
+ test -d "$lt_sys_path" && \
+ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path"
+ fi
+ done
+ lt_search_path_spec=`$ECHO $lt_tmp_lt_search_path_spec | awk '
+BEGIN {RS=" "; FS="/|\n";} {
+ lt_foo="";
+ lt_count=0;
+ for (lt_i = NF; lt_i > 0; lt_i--) {
+ if ($lt_i != "" && $lt_i != ".") {
+ if ($lt_i == "..") {
+ lt_count++;
+ } else {
+ if (lt_count == 0) {
+ lt_foo="/" $lt_i lt_foo;
+ } else {
+ lt_count--;
+ }
+ }
+ }
+ }
+ if (lt_foo != "") { lt_freq[lt_foo]++; }
+ if (lt_freq[lt_foo] == 1) { print lt_foo; }
+}'`
+ sys_lib_search_path_spec=`$ECHO $lt_search_path_spec`
+else
+ sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+fi
+library_names_spec=
+libname_spec='lib$name'
+soname_spec=
+shrext_cmds=".so"
+postinstall_cmds=
+postuninstall_cmds=
+finish_cmds=
+finish_eval=
+shlibpath_var=
+shlibpath_overrides_runpath=unknown
+version_type=none
+dynamic_linker="$host_os ld.so"
+sys_lib_dlsearch_path_spec="/lib /usr/lib"
+need_lib_prefix=unknown
+hardcode_into_libs=no
+
+# when you set need_version to no, make sure it does not cause -set_version
+# flags to be left without arguments
+need_version=unknown
+
+case $host_os in
+aix3*)
+ version_type=linux
+ library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a'
+ shlibpath_var=LIBPATH
+
+ # AIX 3 has no versioning support, so we append a major version to the name.
+ soname_spec='${libname}${release}${shared_ext}$major'
+ ;;
+
+aix[4-9]*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ hardcode_into_libs=yes
+ if test "$host_cpu" = ia64; then
+ # AIX 5 supports IA64
+ library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}'
+ shlibpath_var=LD_LIBRARY_PATH
+ else
+ # With GCC up to 2.95.x, collect2 would create an import file
+ # for dependence libraries. The import file would start with
+ # the line `#! .'. This would cause the generated library to
+ # depend on `.', always an invalid library. This was fixed in
+ # development snapshots of GCC prior to 3.0.
+ case $host_os in
+ aix4 | aix4.[01] | aix4.[01].*)
+ if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
+ echo ' yes '
+ echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then
+ :
+ else
+ can_build_shared=no
+ fi
+ ;;
+ esac
+ # AIX (on Power*) has no versioning support, so currently we can not hardcode correct
+ # soname into executable. Probably we can add versioning support to
+ # collect2, so additional links can be useful in future.
+ if test "$aix_use_runtimelinking" = yes; then
+ # If using run time linking (on AIX 4.2 or later) use lib<name>.so
+ # instead of lib<name>.a to let people know that these are not
+ # typical AIX shared libraries.
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ else
+ # We preserve .a as extension for shared libraries through AIX4.2
+ # and later when we are not doing run time linking.
+ library_names_spec='${libname}${release}.a $libname.a'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ fi
+ shlibpath_var=LIBPATH
+ fi
+ ;;
+
+amigaos*)
+ case $host_cpu in
+ powerpc)
+ # Since July 2007 AmigaOS4 officially supports .so libraries.
+ # When compiling the executable, add -use-dynld -Lsobjs: to the compileline.
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ ;;
+ m68k)
+ library_names_spec='$libname.ixlibrary $libname.a'
+ # Create ${libname}_ixlibrary.a entries in /sys/libs.
+ finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$ECHO "X$lib" | $Xsed -e '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
+ ;;
+ esac
+ ;;
+
+beos*)
+ library_names_spec='${libname}${shared_ext}'
+ dynamic_linker="$host_os ld.so"
+ shlibpath_var=LIBRARY_PATH
+ ;;
+
+bsdi[45]*)
+ version_type=linux
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
+ sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
+ # the default ld.so.conf also contains /usr/contrib/lib and
+ # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
+ # libtool to hard-code these into programs
+ ;;
+
+cygwin* | mingw* | pw32* | cegcc*)
+ version_type=windows
+ shrext_cmds=".dll"
+ need_version=no
+ need_lib_prefix=no
+
+ case $GCC,$host_os in
+ yes,cygwin* | yes,mingw* | yes,pw32* | yes,cegcc*)
+ library_names_spec='$libname.dll.a'
+ # DLL is installed to $(libdir)/../bin by postinstall_cmds
+ postinstall_cmds='base_file=`basename \${file}`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~
+ dldir=$destdir/`dirname \$dlpath`~
+ test -d \$dldir || mkdir -p \$dldir~
+ $install_prog $dir/$dlname \$dldir/$dlname~
+ chmod a+x \$dldir/$dlname~
+ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+ eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+ fi'
+ postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+ dlpath=$dir/\$dldll~
+ $RM \$dlpath'
+ shlibpath_overrides_runpath=yes
+
+ case $host_os in
+ cygwin*)
+ # Cygwin DLLs use 'cyg' prefix rather than 'lib'
+ soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+ sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib"
+ ;;
+ mingw* | cegcc*)
+ # MinGW DLLs use traditional 'lib' prefix
+ soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+ sys_lib_search_path_spec=`$CC -print-search-dirs | $GREP "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"`
+ if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then
+ # It is most probably a Windows format PATH printed by
+ # mingw gcc, but we are running on Cygwin. Gcc prints its search
+ # path with ; separators, and with drive letters. We can handle the
+ # drive letters (cygwin fileutils understands them), so leave them,
+ # especially as we might pass files found there to a mingw objdump,
+ # which wouldn't understand a cygwinified path. Ahh.
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
+ else
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+ fi
+ ;;
+ pw32*)
+ # pw32 DLLs use 'pw' prefix rather than 'lib'
+ library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+ ;;
+ esac
+ ;;
+
+ *)
+ library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib'
+ ;;
+ esac
+ dynamic_linker='Win32 ld.exe'
+ # FIXME: first we should search . and the directory the executable is in
+ shlibpath_var=PATH
+ ;;
+
+darwin* | rhapsody*)
+ dynamic_linker="$host_os dyld"
+ version_type=darwin
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext'
+ soname_spec='${libname}${release}${major}$shared_ext'
+ shlibpath_overrides_runpath=yes
+ shlibpath_var=DYLD_LIBRARY_PATH
+ shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`'
+
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"
+ sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'
+ ;;
+
+dgux*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+freebsd1*)
+ dynamic_linker=no
+ ;;
+
+freebsd* | dragonfly*)
+ # DragonFly does not have aout. When/if they implement a new
+ # versioning mechanism, adjust this.
+ if test -x /usr/bin/objformat; then
+ objformat=`/usr/bin/objformat`
+ else
+ case $host_os in
+ freebsd[123]*) objformat=aout ;;
+ *) objformat=elf ;;
+ esac
+ fi
+ version_type=freebsd-$objformat
+ case $version_type in
+ freebsd-elf*)
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+ need_version=no
+ need_lib_prefix=no
+ ;;
+ freebsd-*)
+ library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix'
+ need_version=yes
+ ;;
+ esac
+ shlibpath_var=LD_LIBRARY_PATH
+ case $host_os in
+ freebsd2*)
+ shlibpath_overrides_runpath=yes
+ ;;
+ freebsd3.[01]* | freebsdelf3.[01]*)
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+ freebsd3.[2-9]* | freebsdelf3.[2-9]* | \
+ freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1)
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+ *) # from 4.6 on, and DragonFly
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+ esac
+ ;;
+
+gnu*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ hardcode_into_libs=yes
+ ;;
+
+hpux9* | hpux10* | hpux11*)
+ # Give a soname corresponding to the major version so that dld.sl refuses to
+ # link against other versions.
+ version_type=sunos
+ need_lib_prefix=no
+ need_version=no
+ case $host_cpu in
+ ia64*)
+ shrext_cmds='.so'
+ hardcode_into_libs=yes
+ dynamic_linker="$host_os dld.so"
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ if test "X$HPUX_IA64_MODE" = X32; then
+ sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
+ else
+ sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
+ fi
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ ;;
+ hppa*64*)
+ shrext_cmds='.sl'
+ hardcode_into_libs=yes
+ dynamic_linker="$host_os dld.sl"
+ shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
+ shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ ;;
+ *)
+ shrext_cmds='.sl'
+ dynamic_linker="$host_os dld.sl"
+ shlibpath_var=SHLIB_PATH
+ shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ ;;
+ esac
+ # HP-UX runs *really* slowly unless shared libraries are mode 555.
+ postinstall_cmds='chmod 555 $lib'
+ ;;
+
+interix[3-9]*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+
+irix5* | irix6* | nonstopux*)
+ case $host_os in
+ nonstopux*) version_type=nonstopux ;;
+ *)
+ if test "$lt_cv_prog_gnu_ld" = yes; then
+ version_type=linux
+ else
+ version_type=irix
+ fi ;;
+ esac
+ need_lib_prefix=no
+ need_version=no
+ soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}'
+ case $host_os in
+ irix5* | nonstopux*)
+ libsuff= shlibsuff=
+ ;;
+ *)
+ case $LD in # libtool.m4 will add one of these switches to LD
+ *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ")
+ libsuff= shlibsuff= libmagic=32-bit;;
+ *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ")
+ libsuff=32 shlibsuff=N32 libmagic=N32;;
+ *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ")
+ libsuff=64 shlibsuff=64 libmagic=64-bit;;
+ *) libsuff= shlibsuff= libmagic=never-match;;
+ esac
+ ;;
+ esac
+ shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
+ shlibpath_overrides_runpath=no
+ sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}"
+ sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}"
+ hardcode_into_libs=yes
+ ;;
+
+# No shared lib support for Linux oldld, aout, or coff.
+linux*oldld* | linux*aout* | linux*coff*)
+ dynamic_linker=no
+ ;;
+
+# This must be Linux ELF.
+linux* | k*bsd*-gnu)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ # Some binutils ld are patched to set DT_RUNPATH
+ save_LDFLAGS=$LDFLAGS
+ save_libdir=$libdir
+ eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \
+ LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\""
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then
+ shlibpath_overrides_runpath=yes
+fi
+
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+ LDFLAGS=$save_LDFLAGS
+ libdir=$save_libdir
+
+ # This implies no fast_install, which is unacceptable.
+ # Some rework will be needed to allow for fast_install
+ # before this can be enabled.
+ hardcode_into_libs=yes
+
+ # Add ABI-specific directories to the system library path.
+ sys_lib_dlsearch_path_spec="/lib64 /usr/lib64 /lib /usr/lib"
+
+ # Append ld.so.conf contents to the search path
+ if test -f /etc/ld.so.conf; then
+ lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '`
+ sys_lib_dlsearch_path_spec="$sys_lib_dlsearch_path_spec $lt_ld_extra"
+ fi
+
+ # We used to test for /lib/ld.so.1 and disable shared libraries on
+ # powerpc, because MkLinux only supported shared libraries with the
+ # GNU dynamic linker. Since this was broken with cross compilers,
+ # most powerpc-linux boxes support dynamic linking these days and
+ # people can always --disable-shared, the test was removed, and we
+ # assume the GNU/Linux dynamic linker is in use.
+ dynamic_linker='GNU/Linux ld.so'
+ ;;
+
+netbsd*)
+ version_type=sunos
+ need_lib_prefix=no
+ need_version=no
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ dynamic_linker='NetBSD (a.out) ld.so'
+ else
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ dynamic_linker='NetBSD ld.elf_so'
+ fi
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+
+newsos6)
+ version_type=linux
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ ;;
+
+*nto* | *qnx*)
+ version_type=qnx
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ dynamic_linker='ldqnx.so'
+ ;;
+
+openbsd*)
+ version_type=sunos
+ sys_lib_dlsearch_path_spec="/usr/lib"
+ need_lib_prefix=no
+ # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs.
+ case $host_os in
+ openbsd3.3 | openbsd3.3.*) need_version=yes ;;
+ *) need_version=no ;;
+ esac
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+ case $host_os in
+ openbsd2.[89] | openbsd2.[89].*)
+ shlibpath_overrides_runpath=no
+ ;;
+ *)
+ shlibpath_overrides_runpath=yes
+ ;;
+ esac
+ else
+ shlibpath_overrides_runpath=yes
+ fi
+ ;;
+
+os2*)
+ libname_spec='$name'
+ shrext_cmds=".dll"
+ need_lib_prefix=no
+ library_names_spec='$libname${shared_ext} $libname.a'
+ dynamic_linker='OS/2 ld.exe'
+ shlibpath_var=LIBPATH
+ ;;
+
+osf3* | osf4* | osf5*)
+ version_type=osf
+ need_lib_prefix=no
+ need_version=no
+ soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ shlibpath_var=LD_LIBRARY_PATH
+ sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
+ sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec"
+ ;;
+
+rdos*)
+ dynamic_linker=no
+ ;;
+
+solaris*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ # ldd complains unless libraries are executable
+ postinstall_cmds='chmod +x $lib'
+ ;;
+
+sunos4*)
+ version_type=sunos
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+ finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ if test "$with_gnu_ld" = yes; then
+ need_lib_prefix=no
+ fi
+ need_version=yes
+ ;;
+
+sysv4 | sysv4.3*)
+ version_type=linux
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ case $host_vendor in
+ sni)
+ shlibpath_overrides_runpath=no
+ need_lib_prefix=no
+ runpath_var=LD_RUN_PATH
+ ;;
+ siemens)
+ need_lib_prefix=no
+ ;;
+ motorola)
+ need_lib_prefix=no
+ need_version=no
+ shlibpath_overrides_runpath=no
+ sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
+ ;;
+ esac
+ ;;
+
+sysv4*MP*)
+ if test -d /usr/nec ;then
+ version_type=linux
+ library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}'
+ soname_spec='$libname${shared_ext}.$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ fi
+ ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+ version_type=freebsd-elf
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ if test "$with_gnu_ld" = yes; then
+ sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib'
+ else
+ sys_lib_search_path_spec='/usr/ccs/lib /usr/lib'
+ case $host_os in
+ sco3.2v5*)
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /lib"
+ ;;
+ esac
+ fi
+ sys_lib_dlsearch_path_spec='/usr/lib'
+ ;;
+
+tpf*)
+ # TPF is a cross-target only. Preferred cross-host = GNU/Linux.
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+
+uts4*)
+ version_type=linux
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+*)
+ dynamic_linker=no
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: result: $dynamic_linker" >&5
+$as_echo "$dynamic_linker" >&6; }
+test "$dynamic_linker" = no && can_build_shared=no
+
+variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
+if test "$GCC" = yes; then
+ variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
+fi
+
+if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then
+ sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec"
+fi
+if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then
+ sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec"
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ { $as_echo "$as_me:$LINENO: checking how to hardcode library paths into programs" >&5
+$as_echo_n "checking how to hardcode library paths into programs... " >&6; }
+hardcode_action=
+if test -n "$hardcode_libdir_flag_spec" ||
+ test -n "$runpath_var" ||
+ test "X$hardcode_automatic" = "Xyes" ; then
+
+ # We can hardcode non-existent directories.
+ if test "$hardcode_direct" != no &&
+ # If the only mechanism to avoid hardcoding is shlibpath_var, we
+ # have to relink, otherwise we might link with an installed library
+ # when we should be linking with a yet-to-be-installed one
+ ## test "$_LT_TAGVAR(hardcode_shlibpath_var, )" != no &&
+ test "$hardcode_minus_L" != no; then
+ # Linking always hardcodes the temporary library directory.
+ hardcode_action=relink
+ else
+ # We can link without hardcoding, and we can hardcode nonexisting dirs.
+ hardcode_action=immediate
+ fi
+else
+ # We cannot hardcode anything, or else we can only hardcode existing
+ # directories.
+ hardcode_action=unsupported
+fi
+{ $as_echo "$as_me:$LINENO: result: $hardcode_action" >&5
+$as_echo "$hardcode_action" >&6; }
+
+if test "$hardcode_action" = relink ||
+ test "$inherit_rpath" = yes; then
+ # Fast installation is not supported
+ enable_fast_install=no
+elif test "$shlibpath_overrides_runpath" = yes ||
+ test "$enable_shared" = no; then
+ # Fast installation is not necessary
+ enable_fast_install=needless
+fi
+
+
+
+
+
+
+ if test "x$enable_dlopen" != xyes; then
+ enable_dlopen=unknown
+ enable_dlopen_self=unknown
+ enable_dlopen_self_static=unknown
+else
+ lt_cv_dlopen=no
+ lt_cv_dlopen_libs=
+
+ case $host_os in
+ beos*)
+ lt_cv_dlopen="load_add_on"
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=yes
+ ;;
+
+ mingw* | pw32* | cegcc*)
+ lt_cv_dlopen="LoadLibrary"
+ lt_cv_dlopen_libs=
+ ;;
+
+ cygwin*)
+ lt_cv_dlopen="dlopen"
+ lt_cv_dlopen_libs=
+ ;;
+
+ darwin*)
+ # if libdl is installed we need to link against it
+ { $as_echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5
+$as_echo_n "checking for dlopen in -ldl... " >&6; }
+if test "${ac_cv_lib_dl_dlopen+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_lib_dl_dlopen=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_dl_dlopen=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5
+$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
+if test "x$ac_cv_lib_dl_dlopen" = x""yes; then
+ lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"
+else
+
+ lt_cv_dlopen="dyld"
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=yes
+
+fi
+
+ ;;
+
+ *)
+ { $as_echo "$as_me:$LINENO: checking for shl_load" >&5
+$as_echo_n "checking for shl_load... " >&6; }
+if test "${ac_cv_func_shl_load+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define shl_load to an innocuous variant, in case <limits.h> declares shl_load.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define shl_load innocuous_shl_load
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char shl_load (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef shl_load
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char shl_load ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_shl_load || defined __stub___shl_load
+choke me
+#endif
+
+int
+main ()
+{
+return shl_load ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_func_shl_load=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_func_shl_load=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_shl_load" >&5
+$as_echo "$ac_cv_func_shl_load" >&6; }
+if test "x$ac_cv_func_shl_load" = x""yes; then
+ lt_cv_dlopen="shl_load"
+else
+ { $as_echo "$as_me:$LINENO: checking for shl_load in -ldld" >&5
+$as_echo_n "checking for shl_load in -ldld... " >&6; }
+if test "${ac_cv_lib_dld_shl_load+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldld $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char shl_load ();
+int
+main ()
+{
+return shl_load ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_lib_dld_shl_load=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_dld_shl_load=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_dld_shl_load" >&5
+$as_echo "$ac_cv_lib_dld_shl_load" >&6; }
+if test "x$ac_cv_lib_dld_shl_load" = x""yes; then
+ lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"
+else
+ { $as_echo "$as_me:$LINENO: checking for dlopen" >&5
+$as_echo_n "checking for dlopen... " >&6; }
+if test "${ac_cv_func_dlopen+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define dlopen to an innocuous variant, in case <limits.h> declares dlopen.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define dlopen innocuous_dlopen
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char dlopen (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef dlopen
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_dlopen || defined __stub___dlopen
+choke me
+#endif
+
+int
+main ()
+{
+return dlopen ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_func_dlopen=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_func_dlopen=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_dlopen" >&5
+$as_echo "$ac_cv_func_dlopen" >&6; }
+if test "x$ac_cv_func_dlopen" = x""yes; then
+ lt_cv_dlopen="dlopen"
+else
+ { $as_echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5
+$as_echo_n "checking for dlopen in -ldl... " >&6; }
+if test "${ac_cv_lib_dl_dlopen+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_lib_dl_dlopen=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_dl_dlopen=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5
+$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
+if test "x$ac_cv_lib_dl_dlopen" = x""yes; then
+ lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"
+else
+ { $as_echo "$as_me:$LINENO: checking for dlopen in -lsvld" >&5
+$as_echo_n "checking for dlopen in -lsvld... " >&6; }
+if test "${ac_cv_lib_svld_dlopen+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsvld $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_lib_svld_dlopen=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_svld_dlopen=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_svld_dlopen" >&5
+$as_echo "$ac_cv_lib_svld_dlopen" >&6; }
+if test "x$ac_cv_lib_svld_dlopen" = x""yes; then
+ lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"
+else
+ { $as_echo "$as_me:$LINENO: checking for dld_link in -ldld" >&5
+$as_echo_n "checking for dld_link in -ldld... " >&6; }
+if test "${ac_cv_lib_dld_dld_link+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldld $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dld_link ();
+int
+main ()
+{
+return dld_link ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_lib_dld_dld_link=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_dld_dld_link=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_dld_dld_link" >&5
+$as_echo "$ac_cv_lib_dld_dld_link" >&6; }
+if test "x$ac_cv_lib_dld_dld_link" = x""yes; then
+ lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+ ;;
+ esac
+
+ if test "x$lt_cv_dlopen" != xno; then
+ enable_dlopen=yes
+ else
+ enable_dlopen=no
+ fi
+
+ case $lt_cv_dlopen in
+ dlopen)
+ save_CPPFLAGS="$CPPFLAGS"
+ test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+
+ save_LDFLAGS="$LDFLAGS"
+ wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
+
+ save_LIBS="$LIBS"
+ LIBS="$lt_cv_dlopen_libs $LIBS"
+
+ { $as_echo "$as_me:$LINENO: checking whether a program can dlopen itself" >&5
+$as_echo_n "checking whether a program can dlopen itself... " >&6; }
+if test "${lt_cv_dlopen_self+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then :
+ lt_cv_dlopen_self=cross
+else
+ lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+ lt_status=$lt_dlunknown
+ cat > conftest.$ac_ext <<_LT_EOF
+#line 11712 "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+# define LT_DLGLOBAL RTLD_GLOBAL
+#else
+# ifdef DL_GLOBAL
+# define LT_DLGLOBAL DL_GLOBAL
+# else
+# define LT_DLGLOBAL 0
+# endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+ find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+# ifdef RTLD_LAZY
+# define LT_DLLAZY_OR_NOW RTLD_LAZY
+# else
+# ifdef DL_LAZY
+# define LT_DLLAZY_OR_NOW DL_LAZY
+# else
+# ifdef RTLD_NOW
+# define LT_DLLAZY_OR_NOW RTLD_NOW
+# else
+# ifdef DL_NOW
+# define LT_DLLAZY_OR_NOW DL_NOW
+# else
+# define LT_DLLAZY_OR_NOW 0
+# endif
+# endif
+# endif
+# endif
+#endif
+
+void fnord() { int i=42;}
+int main ()
+{
+ void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+ int status = $lt_dlunknown;
+
+ if (self)
+ {
+ if (dlsym (self,"fnord")) status = $lt_dlno_uscore;
+ else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
+ /* dlclose (self); */
+ }
+ else
+ puts (dlerror ());
+
+ return status;
+}
+_LT_EOF
+ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && test -s conftest${ac_exeext} 2>/dev/null; then
+ (./conftest; exit; ) >&5 2>/dev/null
+ lt_status=$?
+ case x$lt_status in
+ x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;;
+ x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;;
+ x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;;
+ esac
+ else :
+ # compilation failed
+ lt_cv_dlopen_self=no
+ fi
+fi
+rm -fr conftest*
+
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $lt_cv_dlopen_self" >&5
+$as_echo "$lt_cv_dlopen_self" >&6; }
+
+ if test "x$lt_cv_dlopen_self" = xyes; then
+ wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\"
+ { $as_echo "$as_me:$LINENO: checking whether a statically linked program can dlopen itself" >&5
+$as_echo_n "checking whether a statically linked program can dlopen itself... " >&6; }
+if test "${lt_cv_dlopen_self_static+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then :
+ lt_cv_dlopen_self_static=cross
+else
+ lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+ lt_status=$lt_dlunknown
+ cat > conftest.$ac_ext <<_LT_EOF
+#line 11808 "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+# define LT_DLGLOBAL RTLD_GLOBAL
+#else
+# ifdef DL_GLOBAL
+# define LT_DLGLOBAL DL_GLOBAL
+# else
+# define LT_DLGLOBAL 0
+# endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+ find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+# ifdef RTLD_LAZY
+# define LT_DLLAZY_OR_NOW RTLD_LAZY
+# else
+# ifdef DL_LAZY
+# define LT_DLLAZY_OR_NOW DL_LAZY
+# else
+# ifdef RTLD_NOW
+# define LT_DLLAZY_OR_NOW RTLD_NOW
+# else
+# ifdef DL_NOW
+# define LT_DLLAZY_OR_NOW DL_NOW
+# else
+# define LT_DLLAZY_OR_NOW 0
+# endif
+# endif
+# endif
+# endif
+#endif
+
+void fnord() { int i=42;}
+int main ()
+{
+ void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+ int status = $lt_dlunknown;
+
+ if (self)
+ {
+ if (dlsym (self,"fnord")) status = $lt_dlno_uscore;
+ else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
+ /* dlclose (self); */
+ }
+ else
+ puts (dlerror ());
+
+ return status;
+}
+_LT_EOF
+ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && test -s conftest${ac_exeext} 2>/dev/null; then
+ (./conftest; exit; ) >&5 2>/dev/null
+ lt_status=$?
+ case x$lt_status in
+ x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;;
+ x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;;
+ x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;;
+ esac
+ else :
+ # compilation failed
+ lt_cv_dlopen_self_static=no
+ fi
+fi
+rm -fr conftest*
+
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $lt_cv_dlopen_self_static" >&5
+$as_echo "$lt_cv_dlopen_self_static" >&6; }
+ fi
+
+ CPPFLAGS="$save_CPPFLAGS"
+ LDFLAGS="$save_LDFLAGS"
+ LIBS="$save_LIBS"
+ ;;
+ esac
+
+ case $lt_cv_dlopen_self in
+ yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
+ *) enable_dlopen_self=unknown ;;
+ esac
+
+ case $lt_cv_dlopen_self_static in
+ yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
+ *) enable_dlopen_self_static=unknown ;;
+ esac
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+striplib=
+old_striplib=
+{ $as_echo "$as_me:$LINENO: checking whether stripping libraries is possible" >&5
+$as_echo_n "checking whether stripping libraries is possible... " >&6; }
+if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then
+ test -z "$old_striplib" && old_striplib="$STRIP --strip-debug"
+ test -z "$striplib" && striplib="$STRIP --strip-unneeded"
+ { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+# FIXME - insert some real tests, host_os isn't really good enough
+ case $host_os in
+ darwin*)
+ if test -n "$STRIP" ; then
+ striplib="$STRIP -x"
+ old_striplib="$STRIP -S"
+ { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+ else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+ fi
+ ;;
+ *)
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+ ;;
+ esac
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+ # Report which library types will actually be built
+ { $as_echo "$as_me:$LINENO: checking if libtool supports shared libraries" >&5
+$as_echo_n "checking if libtool supports shared libraries... " >&6; }
+ { $as_echo "$as_me:$LINENO: result: $can_build_shared" >&5
+$as_echo "$can_build_shared" >&6; }
+
+ { $as_echo "$as_me:$LINENO: checking whether to build shared libraries" >&5
+$as_echo_n "checking whether to build shared libraries... " >&6; }
+ test "$can_build_shared" = "no" && enable_shared=no
+
+ # On AIX, shared libraries and static libraries use the same namespace, and
+ # are all built from PIC.
+ case $host_os in
+ aix3*)
+ test "$enable_shared" = yes && enable_static=no
+ if test -n "$RANLIB"; then
+ archive_cmds="$archive_cmds~\$RANLIB \$lib"
+ postinstall_cmds='$RANLIB $lib'
+ fi
+ ;;
+
+ aix[4-9]*)
+ if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
+ test "$enable_shared" = yes && enable_static=no
+ fi
+ ;;
+ esac
+ { $as_echo "$as_me:$LINENO: result: $enable_shared" >&5
+$as_echo "$enable_shared" >&6; }
+
+ { $as_echo "$as_me:$LINENO: checking whether to build static libraries" >&5
+$as_echo_n "checking whether to build static libraries... " >&6; }
+ # Make sure either enable_shared or enable_static is yes.
+ test "$enable_shared" = yes || enable_static=yes
+ { $as_echo "$as_me:$LINENO: result: $enable_static" >&5
+$as_echo "$enable_static" >&6; }
+
+
+
+
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+CC="$lt_save_CC"
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ac_config_commands="$ac_config_commands libtool"
+
+
+
+
+# Only expand once:
+
+
+
+
+ { $as_echo "$as_me:$LINENO: checking for ppoll" >&5
+$as_echo_n "checking for ppoll... " >&6; }
+if test "${ac_cv_func_ppoll+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define ppoll to an innocuous variant, in case <limits.h> declares ppoll.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define ppoll innocuous_ppoll
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char ppoll (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef ppoll
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ppoll ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_ppoll || defined __stub___ppoll
+choke me
+#endif
+
+int
+main ()
+{
+return ppoll ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_func_ppoll=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_func_ppoll=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_ppoll" >&5
+$as_echo "$ac_cv_func_ppoll" >&6; }
+if test "x$ac_cv_func_ppoll" = x""yes; then
+ dummy=yes
+else
+
+cat >>confdefs.h <<\_ACEOF
+#define NEED_PPOLL 1
+_ACEOF
+
+fi
+
+
+
+{ $as_echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5
+$as_echo_n "checking for dlopen in -ldl... " >&6; }
+if test "${ac_cv_lib_dl_dlopen+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_lib_dl_dlopen=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_dl_dlopen=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5
+$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
+if test "x$ac_cv_lib_dl_dlopen" = x""yes; then
+ dummy=yes
+else
+ { { $as_echo "$as_me:$LINENO: error: dynamic linking loader is required" >&5
+$as_echo "$as_me: error: dynamic linking loader is required" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+
+
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for DBUS" >&5
+$as_echo_n "checking for DBUS... " >&6; }
+
+if test -n "$DBUS_CFLAGS"; then
+ pkg_cv_DBUS_CFLAGS="$DBUS_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"dbus-1 >= 1.0\"") >&5
+ ($PKG_CONFIG --exists --print-errors "dbus-1 >= 1.0") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_DBUS_CFLAGS=`$PKG_CONFIG --cflags "dbus-1 >= 1.0" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$DBUS_LIBS"; then
+ pkg_cv_DBUS_LIBS="$DBUS_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"dbus-1 >= 1.0\"") >&5
+ ($PKG_CONFIG --exists --print-errors "dbus-1 >= 1.0") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_DBUS_LIBS=`$PKG_CONFIG --libs "dbus-1 >= 1.0" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ DBUS_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "dbus-1 >= 1.0" 2>&1`
+ else
+ DBUS_PKG_ERRORS=`$PKG_CONFIG --print-errors "dbus-1 >= 1.0" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$DBUS_PKG_ERRORS" >&5
+
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+ { { $as_echo "$as_me:$LINENO: error: D-Bus library is required" >&5
+$as_echo "$as_me: error: D-Bus library is required" >&2;}
+ { (exit 1); exit 1; }; }
+elif test $pkg_failed = untried; then
+ { { $as_echo "$as_me:$LINENO: error: D-Bus library is required" >&5
+$as_echo "$as_me: error: D-Bus library is required" >&2;}
+ { (exit 1); exit 1; }; }
+else
+ DBUS_CFLAGS=$pkg_cv_DBUS_CFLAGS
+ DBUS_LIBS=$pkg_cv_DBUS_LIBS
+ { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+ dummy=yes
+fi
+ { $as_echo "$as_me:$LINENO: checking for dbus_watch_get_unix_fd in -ldbus-1" >&5
+$as_echo_n "checking for dbus_watch_get_unix_fd in -ldbus-1... " >&6; }
+if test "${ac_cv_lib_dbus_1_dbus_watch_get_unix_fd+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldbus-1 $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dbus_watch_get_unix_fd ();
+int
+main ()
+{
+return dbus_watch_get_unix_fd ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_lib_dbus_1_dbus_watch_get_unix_fd=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_dbus_1_dbus_watch_get_unix_fd=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_dbus_1_dbus_watch_get_unix_fd" >&5
+$as_echo "$ac_cv_lib_dbus_1_dbus_watch_get_unix_fd" >&6; }
+if test "x$ac_cv_lib_dbus_1_dbus_watch_get_unix_fd" = x""yes; then
+ dummy=yes
+else
+
+cat >>confdefs.h <<\_ACEOF
+#define NEED_DBUS_WATCH_GET_UNIX_FD 1
+_ACEOF
+
+fi
+
+ { $as_echo "$as_me:$LINENO: checking for dbus_connection_can_send_type in -ldbus-1" >&5
+$as_echo_n "checking for dbus_connection_can_send_type in -ldbus-1... " >&6; }
+if test "${ac_cv_lib_dbus_1_dbus_connection_can_send_type+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldbus-1 $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dbus_connection_can_send_type ();
+int
+main ()
+{
+return dbus_connection_can_send_type ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_lib_dbus_1_dbus_connection_can_send_type=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_dbus_1_dbus_connection_can_send_type=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_dbus_1_dbus_connection_can_send_type" >&5
+$as_echo "$ac_cv_lib_dbus_1_dbus_connection_can_send_type" >&6; }
+if test "x$ac_cv_lib_dbus_1_dbus_connection_can_send_type" = x""yes; then
+ dummy=yes
+else
+
+cat >>confdefs.h <<\_ACEOF
+#define NEED_DBUS_CONNECTION_CAN_SEND_TYPE 1
+_ACEOF
+
+fi
+
+
+
+
+
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for GLIB" >&5
+$as_echo_n "checking for GLIB... " >&6; }
+
+if test -n "$GLIB_CFLAGS"; then
+ pkg_cv_GLIB_CFLAGS="$GLIB_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"glib-2.0 >= 2.16\"") >&5
+ ($PKG_CONFIG --exists --print-errors "glib-2.0 >= 2.16") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_GLIB_CFLAGS=`$PKG_CONFIG --cflags "glib-2.0 >= 2.16" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$GLIB_LIBS"; then
+ pkg_cv_GLIB_LIBS="$GLIB_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"glib-2.0 >= 2.16\"") >&5
+ ($PKG_CONFIG --exists --print-errors "glib-2.0 >= 2.16") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_GLIB_LIBS=`$PKG_CONFIG --libs "glib-2.0 >= 2.16" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ GLIB_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "glib-2.0 >= 2.16" 2>&1`
+ else
+ GLIB_PKG_ERRORS=`$PKG_CONFIG --print-errors "glib-2.0 >= 2.16" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$GLIB_PKG_ERRORS" >&5
+
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+ { { $as_echo "$as_me:$LINENO: error: GLib library version 2.16 or later is required" >&5
+$as_echo "$as_me: error: GLib library version 2.16 or later is required" >&2;}
+ { (exit 1); exit 1; }; }
+elif test $pkg_failed = untried; then
+ { { $as_echo "$as_me:$LINENO: error: GLib library version 2.16 or later is required" >&5
+$as_echo "$as_me: error: GLib library version 2.16 or later is required" >&2;}
+ { (exit 1); exit 1; }; }
+else
+ GLIB_CFLAGS=$pkg_cv_GLIB_CFLAGS
+ GLIB_LIBS=$pkg_cv_GLIB_LIBS
+ { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+ dummy=yes
+fi
+
+
+
+
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for ALSA" >&5
+$as_echo_n "checking for ALSA... " >&6; }
+
+if test -n "$ALSA_CFLAGS"; then
+ pkg_cv_ALSA_CFLAGS="$ALSA_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"alsa\"") >&5
+ ($PKG_CONFIG --exists --print-errors "alsa") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_ALSA_CFLAGS=`$PKG_CONFIG --cflags "alsa" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$ALSA_LIBS"; then
+ pkg_cv_ALSA_LIBS="$ALSA_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"alsa\"") >&5
+ ($PKG_CONFIG --exists --print-errors "alsa") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_ALSA_LIBS=`$PKG_CONFIG --libs "alsa" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ ALSA_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "alsa" 2>&1`
+ else
+ ALSA_PKG_ERRORS=`$PKG_CONFIG --print-errors "alsa" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$ALSA_PKG_ERRORS" >&5
+
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+ alsa_found=no
+elif test $pkg_failed = untried; then
+ alsa_found=no
+else
+ ALSA_CFLAGS=$pkg_cv_ALSA_CFLAGS
+ ALSA_LIBS=$pkg_cv_ALSA_LIBS
+ { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+ alsa_found=yes
+fi
+ { $as_echo "$as_me:$LINENO: checking for clock_gettime in -lrt" >&5
+$as_echo_n "checking for clock_gettime in -lrt... " >&6; }
+if test "${ac_cv_lib_rt_clock_gettime+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lrt $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char clock_gettime ();
+int
+main ()
+{
+return clock_gettime ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_lib_rt_clock_gettime=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_rt_clock_gettime=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_rt_clock_gettime" >&5
+$as_echo "$ac_cv_lib_rt_clock_gettime" >&6; }
+if test "x$ac_cv_lib_rt_clock_gettime" = x""yes; then
+ ALSA_LIBS="$ALSA_LIBS -lrt"
+else
+ alsa_found=no
+fi
+
+
+
+
+
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for GSTREAMER" >&5
+$as_echo_n "checking for GSTREAMER... " >&6; }
+
+if test -n "$GSTREAMER_CFLAGS"; then
+ pkg_cv_GSTREAMER_CFLAGS="$GSTREAMER_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"gstreamer-0.10 >= 0.10.30 gstreamer-plugins-base-0.10\"") >&5
+ ($PKG_CONFIG --exists --print-errors "gstreamer-0.10 >= 0.10.30 gstreamer-plugins-base-0.10") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_GSTREAMER_CFLAGS=`$PKG_CONFIG --cflags "gstreamer-0.10 >= 0.10.30 gstreamer-plugins-base-0.10" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$GSTREAMER_LIBS"; then
+ pkg_cv_GSTREAMER_LIBS="$GSTREAMER_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"gstreamer-0.10 >= 0.10.30 gstreamer-plugins-base-0.10\"") >&5
+ ($PKG_CONFIG --exists --print-errors "gstreamer-0.10 >= 0.10.30 gstreamer-plugins-base-0.10") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_GSTREAMER_LIBS=`$PKG_CONFIG --libs "gstreamer-0.10 >= 0.10.30 gstreamer-plugins-base-0.10" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ GSTREAMER_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "gstreamer-0.10 >= 0.10.30 gstreamer-plugins-base-0.10" 2>&1`
+ else
+ GSTREAMER_PKG_ERRORS=`$PKG_CONFIG --print-errors "gstreamer-0.10 >= 0.10.30 gstreamer-plugins-base-0.10" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$GSTREAMER_PKG_ERRORS" >&5
+
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+ { $as_echo "$as_me:$LINENO: WARNING: GStreamer library version 0.10.30 or later is required" >&5
+$as_echo "$as_me: WARNING: GStreamer library version 0.10.30 or later is required" >&2;};gstreamer_found=no
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:$LINENO: WARNING: GStreamer library version 0.10.30 or later is required" >&5
+$as_echo "$as_me: WARNING: GStreamer library version 0.10.30 or later is required" >&2;};gstreamer_found=no
+else
+ GSTREAMER_CFLAGS=$pkg_cv_GSTREAMER_CFLAGS
+ GSTREAMER_LIBS=$pkg_cv_GSTREAMER_LIBS
+ { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+ gstreamer_found=yes
+fi
+
+
+ GSTREAMER_PLUGINSDIR=`$PKG_CONFIG --variable=pluginsdir gstreamer-0.10`
+
+
+
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for USB" >&5
+$as_echo_n "checking for USB... " >&6; }
+
+if test -n "$USB_CFLAGS"; then
+ pkg_cv_USB_CFLAGS="$USB_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libusb\"") >&5
+ ($PKG_CONFIG --exists --print-errors "libusb") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_USB_CFLAGS=`$PKG_CONFIG --cflags "libusb" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$USB_LIBS"; then
+ pkg_cv_USB_LIBS="$USB_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libusb\"") >&5
+ ($PKG_CONFIG --exists --print-errors "libusb") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_USB_LIBS=`$PKG_CONFIG --libs "libusb" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ USB_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "libusb" 2>&1`
+ else
+ USB_PKG_ERRORS=`$PKG_CONFIG --print-errors "libusb" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$USB_PKG_ERRORS" >&5
+
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+ usb_found=no
+elif test $pkg_failed = untried; then
+ usb_found=no
+else
+ USB_CFLAGS=$pkg_cv_USB_CFLAGS
+ USB_LIBS=$pkg_cv_USB_LIBS
+ { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+ usb_found=yes
+fi
+
+
+ { $as_echo "$as_me:$LINENO: checking for usb_get_busses in -lusb" >&5
+$as_echo_n "checking for usb_get_busses in -lusb... " >&6; }
+if test "${ac_cv_lib_usb_usb_get_busses+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lusb $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char usb_get_busses ();
+int
+main ()
+{
+return usb_get_busses ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_lib_usb_usb_get_busses=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_usb_usb_get_busses=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_usb_usb_get_busses" >&5
+$as_echo "$ac_cv_lib_usb_usb_get_busses" >&6; }
+if test "x$ac_cv_lib_usb_usb_get_busses" = x""yes; then
+ dummy=yes
+else
+
+cat >>confdefs.h <<\_ACEOF
+#define NEED_USB_GET_BUSSES 1
+_ACEOF
+
+fi
+
+ { $as_echo "$as_me:$LINENO: checking for usb_interrupt_read in -lusb" >&5
+$as_echo_n "checking for usb_interrupt_read in -lusb... " >&6; }
+if test "${ac_cv_lib_usb_usb_interrupt_read+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lusb $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char usb_interrupt_read ();
+int
+main ()
+{
+return usb_interrupt_read ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_lib_usb_usb_interrupt_read=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_usb_usb_interrupt_read=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_usb_usb_interrupt_read" >&5
+$as_echo "$ac_cv_lib_usb_usb_interrupt_read" >&6; }
+if test "x$ac_cv_lib_usb_usb_interrupt_read" = x""yes; then
+ dummy=yes
+else
+
+cat >>confdefs.h <<\_ACEOF
+#define NEED_USB_INTERRUPT_READ 1
+_ACEOF
+
+fi
+
+
+
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for SNDFILE" >&5
+$as_echo_n "checking for SNDFILE... " >&6; }
+
+if test -n "$SNDFILE_CFLAGS"; then
+ pkg_cv_SNDFILE_CFLAGS="$SNDFILE_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"sndfile\"") >&5
+ ($PKG_CONFIG --exists --print-errors "sndfile") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_SNDFILE_CFLAGS=`$PKG_CONFIG --cflags "sndfile" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$SNDFILE_LIBS"; then
+ pkg_cv_SNDFILE_LIBS="$SNDFILE_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"sndfile\"") >&5
+ ($PKG_CONFIG --exists --print-errors "sndfile") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_SNDFILE_LIBS=`$PKG_CONFIG --libs "sndfile" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ SNDFILE_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "sndfile" 2>&1`
+ else
+ SNDFILE_PKG_ERRORS=`$PKG_CONFIG --print-errors "sndfile" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$SNDFILE_PKG_ERRORS" >&5
+
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+ sndfile_found=no
+elif test $pkg_failed = untried; then
+ sndfile_found=no
+else
+ SNDFILE_CFLAGS=$pkg_cv_SNDFILE_CFLAGS
+ SNDFILE_LIBS=$pkg_cv_SNDFILE_LIBS
+ { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+ sndfile_found=yes
+fi
+
+
+
+
+
+# Check whether --with-ouifile was given.
+if test "${with_ouifile+set}" = set; then
+ withval=$with_ouifile; ac_with_ouifile=$withval
+else
+ ac_with_ouifile="/var/lib/misc/oui.txt"
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define OUIFILE "$ac_with_ouifile"
+_ACEOF
+
+
+
+ if test "${ac_cv_header_readline_readline_h+set}" = set; then
+ { $as_echo "$as_me:$LINENO: checking for readline/readline.h" >&5
+$as_echo_n "checking for readline/readline.h... " >&6; }
+if test "${ac_cv_header_readline_readline_h+set}" = set; then
+ $as_echo_n "(cached) " >&6
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_readline_readline_h" >&5
+$as_echo "$ac_cv_header_readline_readline_h" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking readline/readline.h usability" >&5
+$as_echo_n "checking readline/readline.h usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <readline/readline.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking readline/readline.h presence" >&5
+$as_echo_n "checking readline/readline.h presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <readline/readline.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { $as_echo "$as_me:$LINENO: WARNING: readline/readline.h: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: readline/readline.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: readline/readline.h: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: readline/readline.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:$LINENO: WARNING: readline/readline.h: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: readline/readline.h: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: readline/readline.h: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: readline/readline.h: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: readline/readline.h: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: readline/readline.h: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: readline/readline.h: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: readline/readline.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: readline/readline.h: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: readline/readline.h: proceeding with the preprocessor's result" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: readline/readline.h: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: readline/readline.h: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for readline/readline.h" >&5
+$as_echo_n "checking for readline/readline.h... " >&6; }
+if test "${ac_cv_header_readline_readline_h+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_header_readline_readline_h=$ac_header_preproc
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_readline_readline_h" >&5
+$as_echo "$ac_cv_header_readline_readline_h" >&6; }
+
+fi
+if test "x$ac_cv_header_readline_readline_h" = x""yes; then
+ { $as_echo "$as_me:$LINENO: checking for main in -lreadline" >&5
+$as_echo_n "checking for main in -lreadline... " >&6; }
+if test "${ac_cv_lib_readline_main+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lreadline $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+
+int
+main ()
+{
+return main ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_lib_readline_main=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_readline_main=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_readline_main" >&5
+$as_echo "$ac_cv_lib_readline_main" >&6; }
+if test "x$ac_cv_lib_readline_main" = x""yes; then
+ readline_found=yes
+ READLINE_LIBS="-lreadline"
+
+
+else
+ readline_found=no
+fi
+
+fi
+
+
+
+
+
+ debug_enable=no
+ optimization_enable=yes
+ fortify_enable=yes
+ pie_enable=yes
+ sndfile_enable=${sndfile_found}
+ hal_enable=no
+ usb_enable=${usb_found}
+ alsa_enable=${alsa_found}
+ gstreamer_enable=${gstreamer_found}
+ audio_enable=yes
+ input_enable=yes
+ serial_enable=yes
+ network_enable=yes
+ sap_enable=no
+ service_enable=yes
+ health_enable=no
+ pnat_enable=no
+ attrib_enable=no
+ tracer_enable=no
+ tools_enable=yes
+ hidd_enable=no
+ pand_enable=no
+ dund_enable=no
+ cups_enable=no
+ test_enable=no
+ bccmd_enable=no
+ pcmcia_enable=no
+ hid2hci_enable=no
+ dfutool_enable=no
+ udevrules_enable=yes
+ configfiles_enable=yes
+ telephony_driver=dummy
+ maemo6_enable=no
+ sap_driver=dummy
+ dbusoob_enable=no
+
+ # Check whether --enable-optimization was given.
+if test "${enable_optimization+set}" = set; then
+ enableval=$enable_optimization;
+ optimization_enable=${enableval}
+
+fi
+
+
+ # Check whether --enable-fortify was given.
+if test "${enable_fortify+set}" = set; then
+ enableval=$enable_fortify;
+ fortify_enable=${enableval}
+
+fi
+
+
+ # Check whether --enable-pie was given.
+if test "${enable_pie+set}" = set; then
+ enableval=$enable_pie;
+ pie_enable=${enableval}
+
+fi
+
+
+ # Check whether --enable-network was given.
+if test "${enable_network+set}" = set; then
+ enableval=$enable_network;
+ network_enable=${enableval}
+
+fi
+
+
+ # Check whether --enable-sap was given.
+if test "${enable_sap+set}" = set; then
+ enableval=$enable_sap;
+ sap_enable=${enableval}
+
+fi
+
+
+
+# Check whether --with-sap was given.
+if test "${with_sap+set}" = set; then
+ withval=$with_sap;
+ sap_driver=${withval}
+
+fi
+
+ SAP_DRIVER=sap-${sap_driver}.c
+
+
+ # Check whether --enable-serial was given.
+if test "${enable_serial+set}" = set; then
+ enableval=$enable_serial;
+ serial_enable=${enableval}
+
+fi
+
+
+ # Check whether --enable-input was given.
+if test "${enable_input+set}" = set; then
+ enableval=$enable_input;
+ input_enable=${enableval}
+
+fi
+
+
+ # Check whether --enable-audio was given.
+if test "${enable_audio+set}" = set; then
+ enableval=$enable_audio;
+ audio_enable=${enableval}
+
+fi
+
+
+ # Check whether --enable-service was given.
+if test "${enable_service+set}" = set; then
+ enableval=$enable_service;
+ service_enable=${enableval}
+
+fi
+
+
+ # Check whether --enable-health was given.
+if test "${enable_health+set}" = set; then
+ enableval=$enable_health;
+ health_enable=${enableval}
+
+fi
+
+
+ # Check whether --enable-pnat was given.
+if test "${enable_pnat+set}" = set; then
+ enableval=$enable_pnat;
+ pnat_enable=${enableval}
+
+fi
+
+
+ # Check whether --enable-attrib was given.
+if test "${enable_attrib+set}" = set; then
+ enableval=$enable_attrib;
+ attrib_enable=${enableval}
+
+fi
+
+
+ # Check whether --enable-gstreamer was given.
+if test "${enable_gstreamer+set}" = set; then
+ enableval=$enable_gstreamer;
+ gstreamer_enable=${enableval}
+
+fi
+
+
+ # Check whether --enable-alsa was given.
+if test "${enable_alsa+set}" = set; then
+ enableval=$enable_alsa;
+ alsa_enable=${enableval}
+
+fi
+
+
+ # Check whether --enable-usb was given.
+if test "${enable_usb+set}" = set; then
+ enableval=$enable_usb;
+ usb_enable=${enableval}
+
+fi
+
+
+ # Check whether --enable-tracer was given.
+if test "${enable_tracer+set}" = set; then
+ enableval=$enable_tracer;
+ tracer_enable=${enableval}
+
+fi
+
+
+ # Check whether --enable-tools was given.
+if test "${enable_tools+set}" = set; then
+ enableval=$enable_tools;
+ tools_enable=${enableval}
+
+fi
+
+
+ # Check whether --enable-bccmd was given.
+if test "${enable_bccmd+set}" = set; then
+ enableval=$enable_bccmd;
+ bccmd_enable=${enableval}
+
+fi
+
+
+ # Check whether --enable-pcmcia was given.
+if test "${enable_pcmcia+set}" = set; then
+ enableval=$enable_pcmcia;
+ pcmcia_enable=${enableval}
+
+fi
+
+
+ # Check whether --enable-hid2hci was given.
+if test "${enable_hid2hci+set}" = set; then
+ enableval=$enable_hid2hci;
+ hid2hci_enable=${enableval}
+
+fi
+
+
+ # Check whether --enable-dfutool was given.
+if test "${enable_dfutool+set}" = set; then
+ enableval=$enable_dfutool;
+ dfutool_enable=${enableval}
+
+fi
+
+
+ # Check whether --enable-hidd was given.
+if test "${enable_hidd+set}" = set; then
+ enableval=$enable_hidd;
+ hidd_enable=${enableval}
+
+fi
+
+
+ # Check whether --enable-pand was given.
+if test "${enable_pand+set}" = set; then
+ enableval=$enable_pand;
+ pand_enable=${enableval}
+
+fi
+
+
+ # Check whether --enable-dund was given.
+if test "${enable_dund+set}" = set; then
+ enableval=$enable_dund;
+ dund_enable=${enableval}
+
+fi
+
+
+ # Check whether --enable-cups was given.
+if test "${enable_cups+set}" = set; then
+ enableval=$enable_cups;
+ cups_enable=${enableval}
+
+fi
+
+
+ # Check whether --enable-test was given.
+if test "${enable_test+set}" = set; then
+ enableval=$enable_test;
+ test_enable=${enableval}
+
+fi
+
+
+ # Check whether --enable-udevrules was given.
+if test "${enable_udevrules+set}" = set; then
+ enableval=$enable_udevrules;
+ udevrules_enable=${enableval}
+
+fi
+
+
+ # Check whether --enable-configfiles was given.
+if test "${enable_configfiles+set}" = set; then
+ enableval=$enable_configfiles;
+ configfiles_enable=${enableval}
+
+fi
+
+
+ # Check whether --enable-debug was given.
+if test "${enable_debug+set}" = set; then
+ enableval=$enable_debug;
+ debug_enable=${enableval}
+
+fi
+
+
+
+# Check whether --with-telephony was given.
+if test "${with_telephony+set}" = set; then
+ withval=$with_telephony;
+ telephony_driver=${withval}
+
+fi
+
+
+ TELEPHONY_DRIVER=telephony-${telephony_driver}.c
+
+
+ # Check whether --enable-maemo6 was given.
+if test "${enable_maemo6+set}" = set; then
+ enableval=$enable_maemo6;
+ maemo6_enable=${enableval}
+
+fi
+
+
+ # Check whether --enable-dbusoob was given.
+if test "${enable_dbusoob+set}" = set; then
+ enableval=$enable_dbusoob;
+ dbusoob_enable=${enableval}
+
+fi
+
+
+ # Check whether --enable-hal was given.
+if test "${enable_hal+set}" = set; then
+ enableval=$enable_hal;
+ hal_enable=${enableval}
+
+fi
+
+
+ if (test "${fortify_enable}" = "yes"); then
+ CFLAGS="$CFLAGS -D_FORTIFY_SOURCE=2"
+ fi
+
+ if (test "${pie_enable}" = "yes" && test "${ac_cv_prog_cc_pie}" = "yes"); then
+ CFLAGS="$CFLAGS -fPIC"
+ LDFLAGS="$LDFLAGS -pie"
+ fi
+
+ if (test "${debug_enable}" = "yes" && test "${ac_cv_prog_cc_g}" = "yes"); then
+ CFLAGS="$CFLAGS -g"
+ fi
+
+ if (test "${optimization_enable}" = "no"); then
+ CFLAGS="$CFLAGS -O0"
+ fi
+
+ if (test "${usb_enable}" = "yes" && test "${usb_found}" = "yes"); then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_LIBUSB 1
+_ACEOF
+
+ fi
+
+ if test "${sndfile_enable}" = "yes" && test "${sndfile_found}" = "yes"; then
+ SNDFILE_TRUE=
+ SNDFILE_FALSE='#'
+else
+ SNDFILE_TRUE='#'
+ SNDFILE_FALSE=
+fi
+
+ if test "${usb_enable}" = "yes" && test "${usb_found}" = "yes"; then
+ USB_TRUE=
+ USB_FALSE='#'
+else
+ USB_TRUE='#'
+ USB_FALSE=
+fi
+
+ if test "${alsa_enable}" = "yes" || test "${gstreamer_enable}" = "yes" ||
+ test "${test_enable}" = "yes"; then
+ SBC_TRUE=
+ SBC_FALSE='#'
+else
+ SBC_TRUE='#'
+ SBC_FALSE=
+fi
+
+ if test "${alsa_enable}" = "yes" && test "${alsa_found}" = "yes"; then
+ ALSA_TRUE=
+ ALSA_FALSE='#'
+else
+ ALSA_TRUE='#'
+ ALSA_FALSE=
+fi
+
+ if test "${gstreamer_enable}" = "yes" && test "${gstreamer_found}" = "yes"; then
+ GSTREAMER_TRUE=
+ GSTREAMER_FALSE='#'
+else
+ GSTREAMER_TRUE='#'
+ GSTREAMER_FALSE=
+fi
+
+ if test "${audio_enable}" = "yes"; then
+ AUDIOPLUGIN_TRUE=
+ AUDIOPLUGIN_FALSE='#'
+else
+ AUDIOPLUGIN_TRUE='#'
+ AUDIOPLUGIN_FALSE=
+fi
+
+ if test "${input_enable}" = "yes"; then
+ INPUTPLUGIN_TRUE=
+ INPUTPLUGIN_FALSE='#'
+else
+ INPUTPLUGIN_TRUE='#'
+ INPUTPLUGIN_FALSE=
+fi
+
+ if test "${serial_enable}" = "yes"; then
+ SERIALPLUGIN_TRUE=
+ SERIALPLUGIN_FALSE='#'
+else
+ SERIALPLUGIN_TRUE='#'
+ SERIALPLUGIN_FALSE=
+fi
+
+ if test "${network_enable}" = "yes"; then
+ NETWORKPLUGIN_TRUE=
+ NETWORKPLUGIN_FALSE='#'
+else
+ NETWORKPLUGIN_TRUE='#'
+ NETWORKPLUGIN_FALSE=
+fi
+
+ if test "${sap_enable}" = "yes"; then
+ SAPPLUGIN_TRUE=
+ SAPPLUGIN_FALSE='#'
+else
+ SAPPLUGIN_TRUE='#'
+ SAPPLUGIN_FALSE=
+fi
+
+ if test "${service_enable}" = "yes"; then
+ SERVICEPLUGIN_TRUE=
+ SERVICEPLUGIN_FALSE='#'
+else
+ SERVICEPLUGIN_TRUE='#'
+ SERVICEPLUGIN_FALSE=
+fi
+
+ if test "${health_enable}" = "yes"; then
+ HEALTHPLUGIN_TRUE=
+ HEALTHPLUGIN_FALSE='#'
+else
+ HEALTHPLUGIN_TRUE='#'
+ HEALTHPLUGIN_FALSE=
+fi
+
+ if test "${health_enable}" = "yes"; then
+ MCAP_TRUE=
+ MCAP_FALSE='#'
+else
+ MCAP_TRUE='#'
+ MCAP_FALSE=
+fi
+
+ if test "${hal_enable}" = "yes"; then
+ HAL_TRUE=
+ HAL_FALSE='#'
+else
+ HAL_TRUE='#'
+ HAL_FALSE=
+fi
+
+ if test "${readline_found}" = "yes"; then
+ READLINE_TRUE=
+ READLINE_FALSE='#'
+else
+ READLINE_TRUE='#'
+ READLINE_FALSE=
+fi
+
+ if test "${attrib_enable}" = "yes"; then
+ ATTRIBPLUGIN_TRUE=
+ ATTRIBPLUGIN_FALSE='#'
+else
+ ATTRIBPLUGIN_TRUE='#'
+ ATTRIBPLUGIN_FALSE=
+fi
+
+ if test "no" = "yes"; then
+ ECHOPLUGIN_TRUE=
+ ECHOPLUGIN_FALSE='#'
+else
+ ECHOPLUGIN_TRUE='#'
+ ECHOPLUGIN_FALSE=
+fi
+
+ if test "${pnat_enable}" = "yes"; then
+ PNATPLUGIN_TRUE=
+ PNATPLUGIN_FALSE='#'
+else
+ PNATPLUGIN_TRUE='#'
+ PNATPLUGIN_FALSE=
+fi
+
+ if test "${tracer_enable}" = "yes"; then
+ TRACER_TRUE=
+ TRACER_FALSE='#'
+else
+ TRACER_TRUE='#'
+ TRACER_FALSE=
+fi
+
+ if test "${hidd_enable}" = "yes"; then
+ HIDD_TRUE=
+ HIDD_FALSE='#'
+else
+ HIDD_TRUE='#'
+ HIDD_FALSE=
+fi
+
+ if test "${pand_enable}" = "yes"; then
+ PAND_TRUE=
+ PAND_FALSE='#'
+else
+ PAND_TRUE='#'
+ PAND_FALSE=
+fi
+
+ if test "${dund_enable}" = "yes"; then
+ DUND_TRUE=
+ DUND_FALSE='#'
+else
+ DUND_TRUE='#'
+ DUND_FALSE=
+fi
+
+ if test "${cups_enable}" = "yes"; then
+ CUPS_TRUE=
+ CUPS_FALSE='#'
+else
+ CUPS_TRUE='#'
+ CUPS_FALSE=
+fi
+
+ if test "${test_enable}" = "yes"; then
+ TEST_TRUE=
+ TEST_FALSE='#'
+else
+ TEST_TRUE='#'
+ TEST_FALSE=
+fi
+
+ if test "${tools_enable}" = "yes"; then
+ TOOLS_TRUE=
+ TOOLS_FALSE='#'
+else
+ TOOLS_TRUE='#'
+ TOOLS_FALSE=
+fi
+
+ if test "${bccmd_enable}" = "yes"; then
+ BCCMD_TRUE=
+ BCCMD_FALSE='#'
+else
+ BCCMD_TRUE='#'
+ BCCMD_FALSE=
+fi
+
+ if test "${pcmcia_enable}" = "yes"; then
+ PCMCIA_TRUE=
+ PCMCIA_FALSE='#'
+else
+ PCMCIA_TRUE='#'
+ PCMCIA_FALSE=
+fi
+
+ if test "${hid2hci_enable}" = "yes" && test "${usb_found}" = "yes"; then
+ HID2HCI_TRUE=
+ HID2HCI_FALSE='#'
+else
+ HID2HCI_TRUE='#'
+ HID2HCI_FALSE=
+fi
+
+ if test "${dfutool_enable}" = "yes" && test "${usb_found}" = "yes"; then
+ DFUTOOL_TRUE=
+ DFUTOOL_FALSE='#'
+else
+ DFUTOOL_TRUE='#'
+ DFUTOOL_FALSE=
+fi
+
+ if test "${udevrules_enable}" = "yes"; then
+ UDEVRULES_TRUE=
+ UDEVRULES_FALSE='#'
+else
+ UDEVRULES_TRUE='#'
+ UDEVRULES_FALSE=
+fi
+
+ if test "${configfiles_enable}" = "yes"; then
+ CONFIGFILES_TRUE=
+ CONFIGFILES_FALSE='#'
+else
+ CONFIGFILES_TRUE='#'
+ CONFIGFILES_FALSE=
+fi
+
+ if test "${maemo6_enable}" = "yes"; then
+ MAEMO6PLUGIN_TRUE=
+ MAEMO6PLUGIN_FALSE='#'
+else
+ MAEMO6PLUGIN_TRUE='#'
+ MAEMO6PLUGIN_FALSE=
+fi
+
+ if test "${dbusoob_enable}" = "yes"; then
+ DBUSOOBPLUGIN_TRUE=
+ DBUSOOBPLUGIN_FALSE='#'
+else
+ DBUSOOBPLUGIN_TRUE='#'
+ DBUSOOBPLUGIN_FALSE=
+fi
+
+
+
+# Check whether --enable-capng was given.
+if test "${enable_capng+set}" = set; then
+ enableval=$enable_capng; enable_capng=${enableval}
+fi
+
+if (test "${enable_capng}" = "yes"); then
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for CAPNG" >&5
+$as_echo_n "checking for CAPNG... " >&6; }
+
+if test -n "$CAPNG_CFLAGS"; then
+ pkg_cv_CAPNG_CFLAGS="$CAPNG_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libcap-ng\"") >&5
+ ($PKG_CONFIG --exists --print-errors "libcap-ng") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_CAPNG_CFLAGS=`$PKG_CONFIG --cflags "libcap-ng" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$CAPNG_LIBS"; then
+ pkg_cv_CAPNG_LIBS="$CAPNG_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libcap-ng\"") >&5
+ ($PKG_CONFIG --exists --print-errors "libcap-ng") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_CAPNG_LIBS=`$PKG_CONFIG --libs "libcap-ng" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ CAPNG_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "libcap-ng" 2>&1`
+ else
+ CAPNG_PKG_ERRORS=`$PKG_CONFIG --print-errors "libcap-ng" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$CAPNG_PKG_ERRORS" >&5
+
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+ { { $as_echo "$as_me:$LINENO: error: Capabilities library is required" >&5
+$as_echo "$as_me: error: Capabilities library is required" >&2;}
+ { (exit 1); exit 1; }; }
+elif test $pkg_failed = untried; then
+ { { $as_echo "$as_me:$LINENO: error: Capabilities library is required" >&5
+$as_echo "$as_me: error: Capabilities library is required" >&2;}
+ { (exit 1); exit 1; }; }
+else
+ CAPNG_CFLAGS=$pkg_cv_CAPNG_CFLAGS
+ CAPNG_LIBS=$pkg_cv_CAPNG_LIBS
+ { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+ dummy=yes
+fi
+
+
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_CAPNG 1
+_ACEOF
+
+fi
+
+ac_config_files="$ac_config_files Makefile scripts/bluetooth.rules doc/version.xml src/bluetoothd.8 bluez.pc"
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems. If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+ for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { $as_echo "$as_me:$LINENO: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+ *) $as_unset $ac_var ;;
+ esac ;;
+ esac
+ done
+
+ (set) 2>&1 |
+ case $as_nl`(ac_space=' '; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ # `set' does not quote correctly, so add quotes (double-quote
+ # substitution turns \\\\ into \\, and sed turns \\ into \).
+ sed -n \
+ "s/'/'\\\\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+ ;; #(
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+) |
+ sed '
+ /^ac_cv_env_/b end
+ t clear
+ :clear
+ s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+ t end
+ s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+ :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+ if test -w "$cache_file"; then
+ test "x$cache_file" != "x/dev/null" &&
+ { $as_echo "$as_me:$LINENO: updating cache $cache_file" >&5
+$as_echo "$as_me: updating cache $cache_file" >&6;}
+ cat confcache >$cache_file
+ else
+ { $as_echo "$as_me:$LINENO: not updating unwritable cache $cache_file" >&5
+$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+ fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+ # 1. Remove the extension, and $U if already installed.
+ ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+ ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
+ # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR
+ # will be set to the directory where LIBOBJS objects are built.
+ ac_libobjs="$ac_libobjs \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+ ac_ltlibobjs="$ac_ltlibobjs \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+ if test -n "$EXEEXT"; then
+ am__EXEEXT_TRUE=
+ am__EXEEXT_FALSE='#'
+else
+ am__EXEEXT_TRUE='#'
+ am__EXEEXT_FALSE=
+fi
+
+if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then
+ { { $as_echo "$as_me:$LINENO: error: conditional \"MAINTAINER_MODE\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"MAINTAINER_MODE\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then
+ { { $as_echo "$as_me:$LINENO: error: conditional \"AMDEP\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"AMDEP\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then
+ { { $as_echo "$as_me:$LINENO: error: conditional \"am__fastdepCC\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"am__fastdepCC\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+if test -z "${SNDFILE_TRUE}" && test -z "${SNDFILE_FALSE}"; then
+ { { $as_echo "$as_me:$LINENO: error: conditional \"SNDFILE\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"SNDFILE\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+if test -z "${USB_TRUE}" && test -z "${USB_FALSE}"; then
+ { { $as_echo "$as_me:$LINENO: error: conditional \"USB\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"USB\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+if test -z "${SBC_TRUE}" && test -z "${SBC_FALSE}"; then
+ { { $as_echo "$as_me:$LINENO: error: conditional \"SBC\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"SBC\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+if test -z "${ALSA_TRUE}" && test -z "${ALSA_FALSE}"; then
+ { { $as_echo "$as_me:$LINENO: error: conditional \"ALSA\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"ALSA\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+if test -z "${GSTREAMER_TRUE}" && test -z "${GSTREAMER_FALSE}"; then
+ { { $as_echo "$as_me:$LINENO: error: conditional \"GSTREAMER\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"GSTREAMER\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+if test -z "${AUDIOPLUGIN_TRUE}" && test -z "${AUDIOPLUGIN_FALSE}"; then
+ { { $as_echo "$as_me:$LINENO: error: conditional \"AUDIOPLUGIN\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"AUDIOPLUGIN\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+if test -z "${INPUTPLUGIN_TRUE}" && test -z "${INPUTPLUGIN_FALSE}"; then
+ { { $as_echo "$as_me:$LINENO: error: conditional \"INPUTPLUGIN\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"INPUTPLUGIN\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+if test -z "${SERIALPLUGIN_TRUE}" && test -z "${SERIALPLUGIN_FALSE}"; then
+ { { $as_echo "$as_me:$LINENO: error: conditional \"SERIALPLUGIN\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"SERIALPLUGIN\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+if test -z "${NETWORKPLUGIN_TRUE}" && test -z "${NETWORKPLUGIN_FALSE}"; then
+ { { $as_echo "$as_me:$LINENO: error: conditional \"NETWORKPLUGIN\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"NETWORKPLUGIN\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+if test -z "${SAPPLUGIN_TRUE}" && test -z "${SAPPLUGIN_FALSE}"; then
+ { { $as_echo "$as_me:$LINENO: error: conditional \"SAPPLUGIN\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"SAPPLUGIN\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+if test -z "${SERVICEPLUGIN_TRUE}" && test -z "${SERVICEPLUGIN_FALSE}"; then
+ { { $as_echo "$as_me:$LINENO: error: conditional \"SERVICEPLUGIN\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"SERVICEPLUGIN\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+if test -z "${HEALTHPLUGIN_TRUE}" && test -z "${HEALTHPLUGIN_FALSE}"; then
+ { { $as_echo "$as_me:$LINENO: error: conditional \"HEALTHPLUGIN\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"HEALTHPLUGIN\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+if test -z "${MCAP_TRUE}" && test -z "${MCAP_FALSE}"; then
+ { { $as_echo "$as_me:$LINENO: error: conditional \"MCAP\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"MCAP\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+if test -z "${HAL_TRUE}" && test -z "${HAL_FALSE}"; then
+ { { $as_echo "$as_me:$LINENO: error: conditional \"HAL\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"HAL\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+if test -z "${READLINE_TRUE}" && test -z "${READLINE_FALSE}"; then
+ { { $as_echo "$as_me:$LINENO: error: conditional \"READLINE\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"READLINE\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+if test -z "${ATTRIBPLUGIN_TRUE}" && test -z "${ATTRIBPLUGIN_FALSE}"; then
+ { { $as_echo "$as_me:$LINENO: error: conditional \"ATTRIBPLUGIN\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"ATTRIBPLUGIN\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+if test -z "${ECHOPLUGIN_TRUE}" && test -z "${ECHOPLUGIN_FALSE}"; then
+ { { $as_echo "$as_me:$LINENO: error: conditional \"ECHOPLUGIN\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"ECHOPLUGIN\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+if test -z "${PNATPLUGIN_TRUE}" && test -z "${PNATPLUGIN_FALSE}"; then
+ { { $as_echo "$as_me:$LINENO: error: conditional \"PNATPLUGIN\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"PNATPLUGIN\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+if test -z "${TRACER_TRUE}" && test -z "${TRACER_FALSE}"; then
+ { { $as_echo "$as_me:$LINENO: error: conditional \"TRACER\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"TRACER\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+if test -z "${HIDD_TRUE}" && test -z "${HIDD_FALSE}"; then
+ { { $as_echo "$as_me:$LINENO: error: conditional \"HIDD\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"HIDD\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+if test -z "${PAND_TRUE}" && test -z "${PAND_FALSE}"; then
+ { { $as_echo "$as_me:$LINENO: error: conditional \"PAND\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"PAND\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+if test -z "${DUND_TRUE}" && test -z "${DUND_FALSE}"; then
+ { { $as_echo "$as_me:$LINENO: error: conditional \"DUND\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"DUND\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+if test -z "${CUPS_TRUE}" && test -z "${CUPS_FALSE}"; then
+ { { $as_echo "$as_me:$LINENO: error: conditional \"CUPS\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"CUPS\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+if test -z "${TEST_TRUE}" && test -z "${TEST_FALSE}"; then
+ { { $as_echo "$as_me:$LINENO: error: conditional \"TEST\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"TEST\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+if test -z "${TOOLS_TRUE}" && test -z "${TOOLS_FALSE}"; then
+ { { $as_echo "$as_me:$LINENO: error: conditional \"TOOLS\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"TOOLS\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+if test -z "${BCCMD_TRUE}" && test -z "${BCCMD_FALSE}"; then
+ { { $as_echo "$as_me:$LINENO: error: conditional \"BCCMD\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"BCCMD\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+if test -z "${PCMCIA_TRUE}" && test -z "${PCMCIA_FALSE}"; then
+ { { $as_echo "$as_me:$LINENO: error: conditional \"PCMCIA\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"PCMCIA\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+if test -z "${HID2HCI_TRUE}" && test -z "${HID2HCI_FALSE}"; then
+ { { $as_echo "$as_me:$LINENO: error: conditional \"HID2HCI\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"HID2HCI\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+if test -z "${DFUTOOL_TRUE}" && test -z "${DFUTOOL_FALSE}"; then
+ { { $as_echo "$as_me:$LINENO: error: conditional \"DFUTOOL\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"DFUTOOL\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+if test -z "${UDEVRULES_TRUE}" && test -z "${UDEVRULES_FALSE}"; then
+ { { $as_echo "$as_me:$LINENO: error: conditional \"UDEVRULES\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"UDEVRULES\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+if test -z "${CONFIGFILES_TRUE}" && test -z "${CONFIGFILES_FALSE}"; then
+ { { $as_echo "$as_me:$LINENO: error: conditional \"CONFIGFILES\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"CONFIGFILES\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+if test -z "${MAEMO6PLUGIN_TRUE}" && test -z "${MAEMO6PLUGIN_FALSE}"; then
+ { { $as_echo "$as_me:$LINENO: error: conditional \"MAEMO6PLUGIN\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"MAEMO6PLUGIN\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+if test -z "${DBUSOOBPLUGIN_TRUE}" && test -z "${DBUSOOBPLUGIN_FALSE}"; then
+ { { $as_echo "$as_me:$LINENO: error: conditional \"DBUSOOBPLUGIN\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"DBUSOOBPLUGIN\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+: ${CONFIG_STATUS=./config.status}
+ac_write_fail=0
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ $as_echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5
+$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
+cat >$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+SHELL=\${CONFIG_SHELL-$SHELL}
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+## --------------------- ##
+## M4sh Initialization. ##
+## --------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in
+ *posix*) set -o posix ;;
+esac
+
+fi
+
+
+
+
+# PATH needs CR
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+if (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='printf %s\n'
+ as_echo_n='printf %s'
+else
+ if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+ as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+ as_echo_n='/usr/ucb/echo -n'
+ else
+ as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+ as_echo_n_body='eval
+ arg=$1;
+ case $arg in
+ *"$as_nl"*)
+ expr "X$arg" : "X\\(.*\\)$as_nl";
+ arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+ esac;
+ expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+ '
+ export as_echo_n_body
+ as_echo_n='sh -c $as_echo_n_body as_echo'
+ fi
+ export as_echo_body
+ as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+ PATH_SEPARATOR=';'
+ }
+fi
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ as_unset=unset
+else
+ as_unset=false
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" "" $as_nl"
+
+# Find who we are. Look in the path if we contain no directory separator.
+case $0 in
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ { (exit 1); exit 1; }
+fi
+
+# Work around bugs in pre-3.0 UWIN ksh.
+for as_var in ENV MAIL MAILPATH
+do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# CDPATH.
+$as_unset CDPATH
+
+
+
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || {
+
+ # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+ # uniformly replaced by the line number. The first 'sed' inserts a
+ # line-number line after each line using $LINENO; the second 'sed'
+ # does the real work. The second script uses 'N' to pair each
+ # line-number line with the line containing $LINENO, and appends
+ # trailing '-' during substitution so that $LINENO is not a special
+ # case at line end.
+ # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+ # scripts with optimization help from Paolo Bonzini. Blame Lee
+ # E. McMahon (1931-1989) for sed's syntax. :-)
+ sed -n '
+ p
+ /[$]LINENO/=
+ ' <$as_myself |
+ sed '
+ s/[$]LINENO.*/&-/
+ t lineno
+ b
+ :lineno
+ N
+ :loop
+ s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+ t loop
+ s/-\n.*//
+ ' >$as_me.lineno &&
+ chmod +x "$as_me.lineno" ||
+ { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+ { (exit 1); exit 1; }; }
+
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensitive to this).
+ . "./$as_me.lineno"
+ # Exit status is that of the last command.
+ exit
+}
+
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in
+-n*)
+ case `echo 'x\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ *) ECHO_C='\c';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+ if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -p'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -p'
+ elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+ else
+ as_ln_s='cp -p'
+ fi
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p=:
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+if test -x / >/dev/null 2>&1; then
+ as_test_x='test -x'
+else
+ if ls -dL / >/dev/null 2>&1; then
+ as_ls_L_option=L
+ else
+ as_ls_L_option=
+ fi
+ as_test_x='
+ eval sh -c '\''
+ if test -d "$1"; then
+ test -d "$1/.";
+ else
+ case $1 in
+ -*)set "./$1";;
+ esac;
+ case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in
+ ???[sx]*):;;*)false;;esac;fi
+ '\'' sh
+ '
+fi
+as_executable_p=$as_test_x
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+
+# Save the log message, to keep $[0] and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by bluez $as_me 4.91, which was
+generated by GNU Autoconf 2.63. Invocation command line was
+
+ CONFIG_FILES = $CONFIG_FILES
+ CONFIG_HEADERS = $CONFIG_HEADERS
+ CONFIG_LINKS = $CONFIG_LINKS
+ CONFIG_COMMANDS = $CONFIG_COMMANDS
+ $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+case $ac_config_files in *"
+"*) set x $ac_config_files; shift; ac_config_files=$*;;
+esac
+
+case $ac_config_headers in *"
+"*) set x $ac_config_headers; shift; ac_config_headers=$*;;
+esac
+
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+# Files that config.status was made for.
+config_files="$ac_config_files"
+config_headers="$ac_config_headers"
+config_commands="$ac_config_commands"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ac_cs_usage="\
+\`$as_me' instantiates files from templates according to the
+current configuration.
+
+Usage: $0 [OPTION]... [FILE]...
+
+ -h, --help print this help, then exit
+ -V, --version print version number and configuration settings, then exit
+ -q, --quiet, --silent
+ do not print progress messages
+ -d, --debug don't remove temporary files
+ --recheck update $as_me by reconfiguring in the same conditions
+ --file=FILE[:TEMPLATE]
+ instantiate the configuration file FILE
+ --header=FILE[:TEMPLATE]
+ instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Configuration commands:
+$config_commands
+
+Report bugs to <bug-autoconf@gnu.org>."
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_version="\\
+bluez config.status 4.91
+configured by $0, generated by GNU Autoconf 2.63,
+ with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
+
+Copyright (C) 2008 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+INSTALL='$INSTALL'
+MKDIR_P='$MKDIR_P'
+AWK='$AWK'
+test -n "\$AWK" || AWK=awk
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# The default lists apply if the user does not specify any file.
+ac_need_defaults=:
+while test $# != 0
+do
+ case $1 in
+ --*=*)
+ ac_option=`expr "X$1" : 'X\([^=]*\)='`
+ ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+ ac_shift=:
+ ;;
+ *)
+ ac_option=$1
+ ac_optarg=$2
+ ac_shift=shift
+ ;;
+ esac
+
+ case $ac_option in
+ # Handling of the options.
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ ac_cs_recheck=: ;;
+ --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+ $as_echo "$ac_cs_version"; exit ;;
+ --debug | --debu | --deb | --de | --d | -d )
+ debug=: ;;
+ --file | --fil | --fi | --f )
+ $ac_shift
+ case $ac_optarg in
+ *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ CONFIG_FILES="$CONFIG_FILES '$ac_optarg'"
+ ac_need_defaults=false;;
+ --header | --heade | --head | --hea )
+ $ac_shift
+ case $ac_optarg in
+ *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ CONFIG_HEADERS="$CONFIG_HEADERS '$ac_optarg'"
+ ac_need_defaults=false;;
+ --he | --h)
+ # Conflict between --help and --header
+ { $as_echo "$as_me: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&2
+ { (exit 1); exit 1; }; };;
+ --help | --hel | -h )
+ $as_echo "$ac_cs_usage"; exit ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil | --si | --s)
+ ac_cs_silent=: ;;
+
+ # This is an error.
+ -*) { $as_echo "$as_me: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&2
+ { (exit 1); exit 1; }; } ;;
+
+ *) ac_config_targets="$ac_config_targets $1"
+ ac_need_defaults=false ;;
+
+ esac
+ shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+ exec 6>/dev/null
+ ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+if \$ac_cs_recheck; then
+ set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+ shift
+ \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
+ CONFIG_SHELL='$SHELL'
+ export CONFIG_SHELL
+ exec "\$@"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+exec 5>>config.log
+{
+ echo
+ sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+ $as_echo "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+#
+# INIT-COMMANDS
+#
+AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"
+
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+sed_quote_subst='$sed_quote_subst'
+double_quote_subst='$double_quote_subst'
+delay_variable_subst='$delay_variable_subst'
+enable_static='`$ECHO "X$enable_static" | $Xsed -e "$delay_single_quote_subst"`'
+macro_version='`$ECHO "X$macro_version" | $Xsed -e "$delay_single_quote_subst"`'
+macro_revision='`$ECHO "X$macro_revision" | $Xsed -e "$delay_single_quote_subst"`'
+enable_shared='`$ECHO "X$enable_shared" | $Xsed -e "$delay_single_quote_subst"`'
+pic_mode='`$ECHO "X$pic_mode" | $Xsed -e "$delay_single_quote_subst"`'
+enable_fast_install='`$ECHO "X$enable_fast_install" | $Xsed -e "$delay_single_quote_subst"`'
+host_alias='`$ECHO "X$host_alias" | $Xsed -e "$delay_single_quote_subst"`'
+host='`$ECHO "X$host" | $Xsed -e "$delay_single_quote_subst"`'
+host_os='`$ECHO "X$host_os" | $Xsed -e "$delay_single_quote_subst"`'
+build_alias='`$ECHO "X$build_alias" | $Xsed -e "$delay_single_quote_subst"`'
+build='`$ECHO "X$build" | $Xsed -e "$delay_single_quote_subst"`'
+build_os='`$ECHO "X$build_os" | $Xsed -e "$delay_single_quote_subst"`'
+SED='`$ECHO "X$SED" | $Xsed -e "$delay_single_quote_subst"`'
+Xsed='`$ECHO "X$Xsed" | $Xsed -e "$delay_single_quote_subst"`'
+GREP='`$ECHO "X$GREP" | $Xsed -e "$delay_single_quote_subst"`'
+EGREP='`$ECHO "X$EGREP" | $Xsed -e "$delay_single_quote_subst"`'
+FGREP='`$ECHO "X$FGREP" | $Xsed -e "$delay_single_quote_subst"`'
+LD='`$ECHO "X$LD" | $Xsed -e "$delay_single_quote_subst"`'
+NM='`$ECHO "X$NM" | $Xsed -e "$delay_single_quote_subst"`'
+LN_S='`$ECHO "X$LN_S" | $Xsed -e "$delay_single_quote_subst"`'
+max_cmd_len='`$ECHO "X$max_cmd_len" | $Xsed -e "$delay_single_quote_subst"`'
+ac_objext='`$ECHO "X$ac_objext" | $Xsed -e "$delay_single_quote_subst"`'
+exeext='`$ECHO "X$exeext" | $Xsed -e "$delay_single_quote_subst"`'
+lt_unset='`$ECHO "X$lt_unset" | $Xsed -e "$delay_single_quote_subst"`'
+lt_SP2NL='`$ECHO "X$lt_SP2NL" | $Xsed -e "$delay_single_quote_subst"`'
+lt_NL2SP='`$ECHO "X$lt_NL2SP" | $Xsed -e "$delay_single_quote_subst"`'
+reload_flag='`$ECHO "X$reload_flag" | $Xsed -e "$delay_single_quote_subst"`'
+reload_cmds='`$ECHO "X$reload_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+OBJDUMP='`$ECHO "X$OBJDUMP" | $Xsed -e "$delay_single_quote_subst"`'
+deplibs_check_method='`$ECHO "X$deplibs_check_method" | $Xsed -e "$delay_single_quote_subst"`'
+file_magic_cmd='`$ECHO "X$file_magic_cmd" | $Xsed -e "$delay_single_quote_subst"`'
+AR='`$ECHO "X$AR" | $Xsed -e "$delay_single_quote_subst"`'
+AR_FLAGS='`$ECHO "X$AR_FLAGS" | $Xsed -e "$delay_single_quote_subst"`'
+STRIP='`$ECHO "X$STRIP" | $Xsed -e "$delay_single_quote_subst"`'
+RANLIB='`$ECHO "X$RANLIB" | $Xsed -e "$delay_single_quote_subst"`'
+old_postinstall_cmds='`$ECHO "X$old_postinstall_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+old_postuninstall_cmds='`$ECHO "X$old_postuninstall_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+old_archive_cmds='`$ECHO "X$old_archive_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+CC='`$ECHO "X$CC" | $Xsed -e "$delay_single_quote_subst"`'
+CFLAGS='`$ECHO "X$CFLAGS" | $Xsed -e "$delay_single_quote_subst"`'
+compiler='`$ECHO "X$compiler" | $Xsed -e "$delay_single_quote_subst"`'
+GCC='`$ECHO "X$GCC" | $Xsed -e "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_pipe='`$ECHO "X$lt_cv_sys_global_symbol_pipe" | $Xsed -e "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_cdecl='`$ECHO "X$lt_cv_sys_global_symbol_to_cdecl" | $Xsed -e "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "X$lt_cv_sys_global_symbol_to_c_name_address" | $Xsed -e "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "X$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $Xsed -e "$delay_single_quote_subst"`'
+objdir='`$ECHO "X$objdir" | $Xsed -e "$delay_single_quote_subst"`'
+SHELL='`$ECHO "X$SHELL" | $Xsed -e "$delay_single_quote_subst"`'
+ECHO='`$ECHO "X$ECHO" | $Xsed -e "$delay_single_quote_subst"`'
+MAGIC_CMD='`$ECHO "X$MAGIC_CMD" | $Xsed -e "$delay_single_quote_subst"`'
+lt_prog_compiler_no_builtin_flag='`$ECHO "X$lt_prog_compiler_no_builtin_flag" | $Xsed -e "$delay_single_quote_subst"`'
+lt_prog_compiler_wl='`$ECHO "X$lt_prog_compiler_wl" | $Xsed -e "$delay_single_quote_subst"`'
+lt_prog_compiler_pic='`$ECHO "X$lt_prog_compiler_pic" | $Xsed -e "$delay_single_quote_subst"`'
+lt_prog_compiler_static='`$ECHO "X$lt_prog_compiler_static" | $Xsed -e "$delay_single_quote_subst"`'
+lt_cv_prog_compiler_c_o='`$ECHO "X$lt_cv_prog_compiler_c_o" | $Xsed -e "$delay_single_quote_subst"`'
+need_locks='`$ECHO "X$need_locks" | $Xsed -e "$delay_single_quote_subst"`'
+DSYMUTIL='`$ECHO "X$DSYMUTIL" | $Xsed -e "$delay_single_quote_subst"`'
+NMEDIT='`$ECHO "X$NMEDIT" | $Xsed -e "$delay_single_quote_subst"`'
+LIPO='`$ECHO "X$LIPO" | $Xsed -e "$delay_single_quote_subst"`'
+OTOOL='`$ECHO "X$OTOOL" | $Xsed -e "$delay_single_quote_subst"`'
+OTOOL64='`$ECHO "X$OTOOL64" | $Xsed -e "$delay_single_quote_subst"`'
+libext='`$ECHO "X$libext" | $Xsed -e "$delay_single_quote_subst"`'
+shrext_cmds='`$ECHO "X$shrext_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+extract_expsyms_cmds='`$ECHO "X$extract_expsyms_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+archive_cmds_need_lc='`$ECHO "X$archive_cmds_need_lc" | $Xsed -e "$delay_single_quote_subst"`'
+enable_shared_with_static_runtimes='`$ECHO "X$enable_shared_with_static_runtimes" | $Xsed -e "$delay_single_quote_subst"`'
+export_dynamic_flag_spec='`$ECHO "X$export_dynamic_flag_spec" | $Xsed -e "$delay_single_quote_subst"`'
+whole_archive_flag_spec='`$ECHO "X$whole_archive_flag_spec" | $Xsed -e "$delay_single_quote_subst"`'
+compiler_needs_object='`$ECHO "X$compiler_needs_object" | $Xsed -e "$delay_single_quote_subst"`'
+old_archive_from_new_cmds='`$ECHO "X$old_archive_from_new_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+old_archive_from_expsyms_cmds='`$ECHO "X$old_archive_from_expsyms_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+archive_cmds='`$ECHO "X$archive_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+archive_expsym_cmds='`$ECHO "X$archive_expsym_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+module_cmds='`$ECHO "X$module_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+module_expsym_cmds='`$ECHO "X$module_expsym_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+with_gnu_ld='`$ECHO "X$with_gnu_ld" | $Xsed -e "$delay_single_quote_subst"`'
+allow_undefined_flag='`$ECHO "X$allow_undefined_flag" | $Xsed -e "$delay_single_quote_subst"`'
+no_undefined_flag='`$ECHO "X$no_undefined_flag" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_libdir_flag_spec='`$ECHO "X$hardcode_libdir_flag_spec" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_libdir_flag_spec_ld='`$ECHO "X$hardcode_libdir_flag_spec_ld" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_libdir_separator='`$ECHO "X$hardcode_libdir_separator" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_direct='`$ECHO "X$hardcode_direct" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_direct_absolute='`$ECHO "X$hardcode_direct_absolute" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_minus_L='`$ECHO "X$hardcode_minus_L" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_shlibpath_var='`$ECHO "X$hardcode_shlibpath_var" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_automatic='`$ECHO "X$hardcode_automatic" | $Xsed -e "$delay_single_quote_subst"`'
+inherit_rpath='`$ECHO "X$inherit_rpath" | $Xsed -e "$delay_single_quote_subst"`'
+link_all_deplibs='`$ECHO "X$link_all_deplibs" | $Xsed -e "$delay_single_quote_subst"`'
+fix_srcfile_path='`$ECHO "X$fix_srcfile_path" | $Xsed -e "$delay_single_quote_subst"`'
+always_export_symbols='`$ECHO "X$always_export_symbols" | $Xsed -e "$delay_single_quote_subst"`'
+export_symbols_cmds='`$ECHO "X$export_symbols_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+exclude_expsyms='`$ECHO "X$exclude_expsyms" | $Xsed -e "$delay_single_quote_subst"`'
+include_expsyms='`$ECHO "X$include_expsyms" | $Xsed -e "$delay_single_quote_subst"`'
+prelink_cmds='`$ECHO "X$prelink_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+file_list_spec='`$ECHO "X$file_list_spec" | $Xsed -e "$delay_single_quote_subst"`'
+variables_saved_for_relink='`$ECHO "X$variables_saved_for_relink" | $Xsed -e "$delay_single_quote_subst"`'
+need_lib_prefix='`$ECHO "X$need_lib_prefix" | $Xsed -e "$delay_single_quote_subst"`'
+need_version='`$ECHO "X$need_version" | $Xsed -e "$delay_single_quote_subst"`'
+version_type='`$ECHO "X$version_type" | $Xsed -e "$delay_single_quote_subst"`'
+runpath_var='`$ECHO "X$runpath_var" | $Xsed -e "$delay_single_quote_subst"`'
+shlibpath_var='`$ECHO "X$shlibpath_var" | $Xsed -e "$delay_single_quote_subst"`'
+shlibpath_overrides_runpath='`$ECHO "X$shlibpath_overrides_runpath" | $Xsed -e "$delay_single_quote_subst"`'
+libname_spec='`$ECHO "X$libname_spec" | $Xsed -e "$delay_single_quote_subst"`'
+library_names_spec='`$ECHO "X$library_names_spec" | $Xsed -e "$delay_single_quote_subst"`'
+soname_spec='`$ECHO "X$soname_spec" | $Xsed -e "$delay_single_quote_subst"`'
+postinstall_cmds='`$ECHO "X$postinstall_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+postuninstall_cmds='`$ECHO "X$postuninstall_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+finish_cmds='`$ECHO "X$finish_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+finish_eval='`$ECHO "X$finish_eval" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_into_libs='`$ECHO "X$hardcode_into_libs" | $Xsed -e "$delay_single_quote_subst"`'
+sys_lib_search_path_spec='`$ECHO "X$sys_lib_search_path_spec" | $Xsed -e "$delay_single_quote_subst"`'
+sys_lib_dlsearch_path_spec='`$ECHO "X$sys_lib_dlsearch_path_spec" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_action='`$ECHO "X$hardcode_action" | $Xsed -e "$delay_single_quote_subst"`'
+enable_dlopen='`$ECHO "X$enable_dlopen" | $Xsed -e "$delay_single_quote_subst"`'
+enable_dlopen_self='`$ECHO "X$enable_dlopen_self" | $Xsed -e "$delay_single_quote_subst"`'
+enable_dlopen_self_static='`$ECHO "X$enable_dlopen_self_static" | $Xsed -e "$delay_single_quote_subst"`'
+old_striplib='`$ECHO "X$old_striplib" | $Xsed -e "$delay_single_quote_subst"`'
+striplib='`$ECHO "X$striplib" | $Xsed -e "$delay_single_quote_subst"`'
+
+LTCC='$LTCC'
+LTCFLAGS='$LTCFLAGS'
+compiler='$compiler_DEFAULT'
+
+# Quote evaled strings.
+for var in SED \
+GREP \
+EGREP \
+FGREP \
+LD \
+NM \
+LN_S \
+lt_SP2NL \
+lt_NL2SP \
+reload_flag \
+OBJDUMP \
+deplibs_check_method \
+file_magic_cmd \
+AR \
+AR_FLAGS \
+STRIP \
+RANLIB \
+CC \
+CFLAGS \
+compiler \
+lt_cv_sys_global_symbol_pipe \
+lt_cv_sys_global_symbol_to_cdecl \
+lt_cv_sys_global_symbol_to_c_name_address \
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \
+SHELL \
+ECHO \
+lt_prog_compiler_no_builtin_flag \
+lt_prog_compiler_wl \
+lt_prog_compiler_pic \
+lt_prog_compiler_static \
+lt_cv_prog_compiler_c_o \
+need_locks \
+DSYMUTIL \
+NMEDIT \
+LIPO \
+OTOOL \
+OTOOL64 \
+shrext_cmds \
+export_dynamic_flag_spec \
+whole_archive_flag_spec \
+compiler_needs_object \
+with_gnu_ld \
+allow_undefined_flag \
+no_undefined_flag \
+hardcode_libdir_flag_spec \
+hardcode_libdir_flag_spec_ld \
+hardcode_libdir_separator \
+fix_srcfile_path \
+exclude_expsyms \
+include_expsyms \
+file_list_spec \
+variables_saved_for_relink \
+libname_spec \
+library_names_spec \
+soname_spec \
+finish_eval \
+old_striplib \
+striplib; do
+ case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in
+ *[\\\\\\\`\\"\\\$]*)
+ eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$sed_quote_subst\\"\\\`\\\\\\""
+ ;;
+ *)
+ eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+ ;;
+ esac
+done
+
+# Double-quote double-evaled strings.
+for var in reload_cmds \
+old_postinstall_cmds \
+old_postuninstall_cmds \
+old_archive_cmds \
+extract_expsyms_cmds \
+old_archive_from_new_cmds \
+old_archive_from_expsyms_cmds \
+archive_cmds \
+archive_expsym_cmds \
+module_cmds \
+module_expsym_cmds \
+export_symbols_cmds \
+prelink_cmds \
+postinstall_cmds \
+postuninstall_cmds \
+finish_cmds \
+sys_lib_search_path_spec \
+sys_lib_dlsearch_path_spec; do
+ case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in
+ *[\\\\\\\`\\"\\\$]*)
+ eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\""
+ ;;
+ *)
+ eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+ ;;
+ esac
+done
+
+# Fix-up fallback echo if it was mangled by the above quoting rules.
+case \$lt_ECHO in
+*'\\\$0 --fallback-echo"') lt_ECHO=\`\$ECHO "X\$lt_ECHO" | \$Xsed -e 's/\\\\\\\\\\\\\\\$0 --fallback-echo"\$/\$0 --fallback-echo"/'\`
+ ;;
+esac
+
+ac_aux_dir='$ac_aux_dir'
+xsi_shell='$xsi_shell'
+lt_shell_append='$lt_shell_append'
+
+# See if we are running on zsh, and set the options which allow our
+# commands through without removal of \ escapes INIT.
+if test -n "\${ZSH_VERSION+set}" ; then
+ setopt NO_GLOB_SUBST
+fi
+
+
+ PACKAGE='$PACKAGE'
+ VERSION='$VERSION'
+ TIMESTAMP='$TIMESTAMP'
+ RM='$RM'
+ ofile='$ofile'
+
+
+
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+ case $ac_config_target in
+ "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
+ "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;;
+ "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;;
+ "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+ "scripts/bluetooth.rules") CONFIG_FILES="$CONFIG_FILES scripts/bluetooth.rules" ;;
+ "doc/version.xml") CONFIG_FILES="$CONFIG_FILES doc/version.xml" ;;
+ "src/bluetoothd.8") CONFIG_FILES="$CONFIG_FILES src/bluetoothd.8" ;;
+ "bluez.pc") CONFIG_FILES="$CONFIG_FILES bluez.pc" ;;
+
+ *) { { $as_echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
+$as_echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
+ { (exit 1); exit 1; }; };;
+ esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used. Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+ test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+ test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+ test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands
+fi
+
+# Have a temporary directory for convenience. Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+ tmp=
+ trap 'exit_status=$?
+ { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status
+' 0
+ trap '{ (exit 1); exit 1; }' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+ tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+ test -n "$tmp" && test -d "$tmp"
+} ||
+{
+ tmp=./conf$$-$RANDOM
+ (umask 077 && mkdir "$tmp")
+} ||
+{
+ $as_echo "$as_me: cannot create a temporary directory in ." >&2
+ { (exit 1); exit 1; }
+}
+
+# Set up the scripts for CONFIG_FILES section.
+# No need to generate them if there are no CONFIG_FILES.
+# This happens for instance with `./config.status config.h'.
+if test -n "$CONFIG_FILES"; then
+
+
+ac_cr=' '
+ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
+if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
+ ac_cs_awk_cr='\\r'
+else
+ ac_cs_awk_cr=$ac_cr
+fi
+
+echo 'BEGIN {' >"$tmp/subs1.awk" &&
+_ACEOF
+
+
+{
+ echo "cat >conf$$subs.awk <<_ACEOF" &&
+ echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
+ echo "_ACEOF"
+} >conf$$subs.sh ||
+ { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
+$as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;}
+ { (exit 1); exit 1; }; }
+ac_delim_num=`echo "$ac_subst_vars" | grep -c '$'`
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+ . ./conf$$subs.sh ||
+ { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
+$as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;}
+ { (exit 1); exit 1; }; }
+
+ ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
+ if test $ac_delim_n = $ac_delim_num; then
+ break
+ elif $ac_last_try; then
+ { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
+$as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;}
+ { (exit 1); exit 1; }; }
+ else
+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+ fi
+done
+rm -f conf$$subs.sh
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"\$tmp/subs1.awk" <<\\_ACAWK &&
+_ACEOF
+sed -n '
+h
+s/^/S["/; s/!.*/"]=/
+p
+g
+s/^[^!]*!//
+:repl
+t repl
+s/'"$ac_delim"'$//
+t delim
+:nl
+h
+s/\(.\{148\}\).*/\1/
+t more1
+s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
+p
+n
+b repl
+:more1
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t nl
+:delim
+h
+s/\(.\{148\}\).*/\1/
+t more2
+s/["\\]/\\&/g; s/^/"/; s/$/"/
+p
+b
+:more2
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t delim
+' <conf$$subs.awk | sed '
+/^[^""]/{
+ N
+ s/\n//
+}
+' >>$CONFIG_STATUS || ac_write_fail=1
+rm -f conf$$subs.awk
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACAWK
+cat >>"\$tmp/subs1.awk" <<_ACAWK &&
+ for (key in S) S_is_set[key] = 1
+ FS = ""
+
+}
+{
+ line = $ 0
+ nfields = split(line, field, "@")
+ substed = 0
+ len = length(field[1])
+ for (i = 2; i < nfields; i++) {
+ key = field[i]
+ keylen = length(key)
+ if (S_is_set[key]) {
+ value = S[key]
+ line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
+ len += length(value) + length(field[++i])
+ substed = 1
+ } else
+ len += 1 + keylen
+ }
+
+ print line
+}
+
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
+ sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
+else
+ cat
+fi < "$tmp/subs1.awk" > "$tmp/subs.awk" \
+ || { { $as_echo "$as_me:$LINENO: error: could not setup config files machinery" >&5
+$as_echo "$as_me: error: could not setup config files machinery" >&2;}
+ { (exit 1); exit 1; }; }
+_ACEOF
+
+# VPATH may cause trouble with some makes, so we remove $(srcdir),
+# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=/{
+s/:*\$(srcdir):*/:/
+s/:*\${srcdir}:*/:/
+s/:*@srcdir@:*/:/
+s/^\([^=]*=[ ]*\):*/\1/
+s/:*$//
+s/^[^=]*=[ ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+fi # test -n "$CONFIG_FILES"
+
+# Set up the scripts for CONFIG_HEADERS section.
+# No need to generate them if there are no CONFIG_HEADERS.
+# This happens for instance with `./config.status Makefile'.
+if test -n "$CONFIG_HEADERS"; then
+cat >"$tmp/defines.awk" <<\_ACAWK ||
+BEGIN {
+_ACEOF
+
+# Transform confdefs.h into an awk script `defines.awk', embedded as
+# here-document in config.status, that substitutes the proper values into
+# config.h.in to produce config.h.
+
+# Create a delimiter string that does not exist in confdefs.h, to ease
+# handling of long lines.
+ac_delim='%!_!# '
+for ac_last_try in false false :; do
+ ac_t=`sed -n "/$ac_delim/p" confdefs.h`
+ if test -z "$ac_t"; then
+ break
+ elif $ac_last_try; then
+ { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_HEADERS" >&5
+$as_echo "$as_me: error: could not make $CONFIG_HEADERS" >&2;}
+ { (exit 1); exit 1; }; }
+ else
+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+ fi
+done
+
+# For the awk script, D is an array of macro values keyed by name,
+# likewise P contains macro parameters if any. Preserve backslash
+# newline sequences.
+
+ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
+sed -n '
+s/.\{148\}/&'"$ac_delim"'/g
+t rset
+:rset
+s/^[ ]*#[ ]*define[ ][ ]*/ /
+t def
+d
+:def
+s/\\$//
+t bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3"/p
+s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p
+d
+:bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3\\\\\\n"\\/p
+t cont
+s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p
+t cont
+d
+:cont
+n
+s/.\{148\}/&'"$ac_delim"'/g
+t clear
+:clear
+s/\\$//
+t bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/"/p
+d
+:bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p
+b cont
+' <confdefs.h | sed '
+s/'"$ac_delim"'/"\\\
+"/g' >>$CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ for (key in D) D_is_set[key] = 1
+ FS = ""
+}
+/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ {
+ line = \$ 0
+ split(line, arg, " ")
+ if (arg[1] == "#") {
+ defundef = arg[2]
+ mac1 = arg[3]
+ } else {
+ defundef = substr(arg[1], 2)
+ mac1 = arg[2]
+ }
+ split(mac1, mac2, "(") #)
+ macro = mac2[1]
+ prefix = substr(line, 1, index(line, defundef) - 1)
+ if (D_is_set[macro]) {
+ # Preserve the white space surrounding the "#".
+ print prefix "define", macro P[macro] D[macro]
+ next
+ } else {
+ # Replace #undef with comments. This is necessary, for example,
+ # in the case of _POSIX_SOURCE, which is predefined and required
+ # on some systems where configure will not decide to define it.
+ if (defundef == "undef") {
+ print "/*", prefix defundef, macro, "*/"
+ next
+ }
+ }
+}
+{ print }
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ { { $as_echo "$as_me:$LINENO: error: could not setup config headers machinery" >&5
+$as_echo "$as_me: error: could not setup config headers machinery" >&2;}
+ { (exit 1); exit 1; }; }
+fi # test -n "$CONFIG_HEADERS"
+
+
+eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS"
+shift
+for ac_tag
+do
+ case $ac_tag in
+ :[FHLC]) ac_mode=$ac_tag; continue;;
+ esac
+ case $ac_mode$ac_tag in
+ :[FHL]*:*);;
+ :L* | :C*:*) { { $as_echo "$as_me:$LINENO: error: invalid tag $ac_tag" >&5
+$as_echo "$as_me: error: invalid tag $ac_tag" >&2;}
+ { (exit 1); exit 1; }; };;
+ :[FH]-) ac_tag=-:-;;
+ :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+ esac
+ ac_save_IFS=$IFS
+ IFS=:
+ set x $ac_tag
+ IFS=$ac_save_IFS
+ shift
+ ac_file=$1
+ shift
+
+ case $ac_mode in
+ :L) ac_source=$1;;
+ :[FH])
+ ac_file_inputs=
+ for ac_f
+ do
+ case $ac_f in
+ -) ac_f="$tmp/stdin";;
+ *) # Look for the file first in the build tree, then in the source tree
+ # (if the path is not absolute). The absolute path cannot be DOS-style,
+ # because $ac_f cannot contain `:'.
+ test -f "$ac_f" ||
+ case $ac_f in
+ [\\/$]*) false;;
+ *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+ esac ||
+ { { $as_echo "$as_me:$LINENO: error: cannot find input file: $ac_f" >&5
+$as_echo "$as_me: error: cannot find input file: $ac_f" >&2;}
+ { (exit 1); exit 1; }; };;
+ esac
+ case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+ ac_file_inputs="$ac_file_inputs '$ac_f'"
+ done
+
+ # Let's still pretend it is `configure' which instantiates (i.e., don't
+ # use $as_me), people would be surprised to read:
+ # /* config.h. Generated by config.status. */
+ configure_input='Generated from '`
+ $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+ `' by configure.'
+ if test x"$ac_file" != x-; then
+ configure_input="$ac_file. $configure_input"
+ { $as_echo "$as_me:$LINENO: creating $ac_file" >&5
+$as_echo "$as_me: creating $ac_file" >&6;}
+ fi
+ # Neutralize special characters interpreted by sed in replacement strings.
+ case $configure_input in #(
+ *\&* | *\|* | *\\* )
+ ac_sed_conf_input=`$as_echo "$configure_input" |
+ sed 's/[\\\\&|]/\\\\&/g'`;; #(
+ *) ac_sed_conf_input=$configure_input;;
+ esac
+
+ case $ac_tag in
+ *:-:* | *:-) cat >"$tmp/stdin" \
+ || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5
+$as_echo "$as_me: error: could not create $ac_file" >&2;}
+ { (exit 1); exit 1; }; } ;;
+ esac
+ ;;
+ esac
+
+ ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$ac_file" : 'X\(//\)[^/]' \| \
+ X"$ac_file" : 'X\(//\)$' \| \
+ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$ac_file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ { as_dir="$ac_dir"
+ case $as_dir in #(
+ -*) as_dir=./$as_dir;;
+ esac
+ test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || {
+ as_dirs=
+ while :; do
+ case $as_dir in #(
+ *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+ *) as_qdir=$as_dir;;
+ esac
+ as_dirs="'$as_qdir' $as_dirs"
+ as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ test -d "$as_dir" && break
+ done
+ test -z "$as_dirs" || eval "mkdir $as_dirs"
+ } || test -d "$as_dir" || { { $as_echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5
+$as_echo "$as_me: error: cannot create directory $as_dir" >&2;}
+ { (exit 1); exit 1; }; }; }
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+ case $ac_mode in
+ :F)
+ #
+ # CONFIG_FILE
+ #
+
+ case $INSTALL in
+ [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+ *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;
+ esac
+ ac_MKDIR_P=$MKDIR_P
+ case $MKDIR_P in
+ [\\/$]* | ?:[\\/]* ) ;;
+ */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;;
+ esac
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+
+ac_sed_dataroot='
+/datarootdir/ {
+ p
+ q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p
+'
+case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ ac_datarootdir_hack='
+ s&@datadir@&$datadir&g
+ s&@docdir@&$docdir&g
+ s&@infodir@&$infodir&g
+ s&@localedir@&$localedir&g
+ s&@mandir@&$mandir&g
+ s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_sed_extra="$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s|@configure_input@|$ac_sed_conf_input|;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@top_build_prefix@&$ac_top_build_prefix&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+s&@INSTALL@&$ac_INSTALL&;t t
+s&@MKDIR_P@&$ac_MKDIR_P&;t t
+$ac_datarootdir_hack
+"
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$tmp/subs.awk" >$tmp/out \
+ || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5
+$as_echo "$as_me: error: could not create $ac_file" >&2;}
+ { (exit 1); exit 1; }; }
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+ { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } &&
+ { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } &&
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined. Please make sure it is defined." >&5
+$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined. Please make sure it is defined." >&2;}
+
+ rm -f "$tmp/stdin"
+ case $ac_file in
+ -) cat "$tmp/out" && rm -f "$tmp/out";;
+ *) rm -f "$ac_file" && mv "$tmp/out" "$ac_file";;
+ esac \
+ || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5
+$as_echo "$as_me: error: could not create $ac_file" >&2;}
+ { (exit 1); exit 1; }; }
+ ;;
+ :H)
+ #
+ # CONFIG_HEADER
+ #
+ if test x"$ac_file" != x-; then
+ {
+ $as_echo "/* $configure_input */" \
+ && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs"
+ } >"$tmp/config.h" \
+ || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5
+$as_echo "$as_me: error: could not create $ac_file" >&2;}
+ { (exit 1); exit 1; }; }
+ if diff "$ac_file" "$tmp/config.h" >/dev/null 2>&1; then
+ { $as_echo "$as_me:$LINENO: $ac_file is unchanged" >&5
+$as_echo "$as_me: $ac_file is unchanged" >&6;}
+ else
+ rm -f "$ac_file"
+ mv "$tmp/config.h" "$ac_file" \
+ || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5
+$as_echo "$as_me: error: could not create $ac_file" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ else
+ $as_echo "/* $configure_input */" \
+ && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" \
+ || { { $as_echo "$as_me:$LINENO: error: could not create -" >&5
+$as_echo "$as_me: error: could not create -" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+# Compute "$ac_file"'s index in $config_headers.
+_am_arg="$ac_file"
+_am_stamp_count=1
+for _am_header in $config_headers :; do
+ case $_am_header in
+ $_am_arg | $_am_arg:* )
+ break ;;
+ * )
+ _am_stamp_count=`expr $_am_stamp_count + 1` ;;
+ esac
+done
+echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" ||
+$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$_am_arg" : 'X\(//\)[^/]' \| \
+ X"$_am_arg" : 'X\(//\)$' \| \
+ X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$_am_arg" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`/stamp-h$_am_stamp_count
+ ;;
+
+ :C) { $as_echo "$as_me:$LINENO: executing $ac_file commands" >&5
+$as_echo "$as_me: executing $ac_file commands" >&6;}
+ ;;
+ esac
+
+
+ case $ac_file$ac_mode in
+ "depfiles":C) test x"$AMDEP_TRUE" != x"" || {
+ # Autoconf 2.62 quotes --file arguments for eval, but not when files
+ # are listed without --file. Let's play safe and only enable the eval
+ # if we detect the quoting.
+ case $CONFIG_FILES in
+ *\'*) eval set x "$CONFIG_FILES" ;;
+ *) set x $CONFIG_FILES ;;
+ esac
+ shift
+ for mf
+ do
+ # Strip MF so we end up with the name of the file.
+ mf=`echo "$mf" | sed -e 's/:.*$//'`
+ # Check whether this is an Automake generated Makefile or not.
+ # We used to match only the files named `Makefile.in', but
+ # some people rename them; so instead we look at the file content.
+ # Grep'ing the first line is not enough: some people post-process
+ # each Makefile.in and add a new line on top of each file to say so.
+ # Grep'ing the whole file is not good either: AIX grep has a line
+ # limit of 2048, but all sed's we know have understand at least 4000.
+ if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
+ dirpart=`$as_dirname -- "$mf" ||
+$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$mf" : 'X\(//\)[^/]' \| \
+ X"$mf" : 'X\(//\)$' \| \
+ X"$mf" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$mf" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ else
+ continue
+ fi
+ # Extract the definition of DEPDIR, am__include, and am__quote
+ # from the Makefile without running `make'.
+ DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
+ test -z "$DEPDIR" && continue
+ am__include=`sed -n 's/^am__include = //p' < "$mf"`
+ test -z "am__include" && continue
+ am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
+ # When using ansi2knr, U may be empty or an underscore; expand it
+ U=`sed -n 's/^U = //p' < "$mf"`
+ # Find all dependency output files, they are included files with
+ # $(DEPDIR) in their names. We invoke sed twice because it is the
+ # simplest approach to changing $(DEPDIR) to its actual value in the
+ # expansion.
+ for file in `sed -n "
+ s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
+ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
+ # Make sure the directory exists.
+ test -f "$dirpart/$file" && continue
+ fdir=`$as_dirname -- "$file" ||
+$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$file" : 'X\(//\)[^/]' \| \
+ X"$file" : 'X\(//\)$' \| \
+ X"$file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ { as_dir=$dirpart/$fdir
+ case $as_dir in #(
+ -*) as_dir=./$as_dir;;
+ esac
+ test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || {
+ as_dirs=
+ while :; do
+ case $as_dir in #(
+ *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+ *) as_qdir=$as_dir;;
+ esac
+ as_dirs="'$as_qdir' $as_dirs"
+ as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ test -d "$as_dir" && break
+ done
+ test -z "$as_dirs" || eval "mkdir $as_dirs"
+ } || test -d "$as_dir" || { { $as_echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5
+$as_echo "$as_me: error: cannot create directory $as_dir" >&2;}
+ { (exit 1); exit 1; }; }; }
+ # echo "creating $dirpart/$file"
+ echo '# dummy' > "$dirpart/$file"
+ done
+ done
+}
+ ;;
+ "libtool":C)
+
+ # See if we are running on zsh, and set the options which allow our
+ # commands through without removal of \ escapes.
+ if test -n "${ZSH_VERSION+set}" ; then
+ setopt NO_GLOB_SUBST
+ fi
+
+ cfgfile="${ofile}T"
+ trap "$RM \"$cfgfile\"; exit 1" 1 2 15
+ $RM "$cfgfile"
+
+ cat <<_LT_EOF >> "$cfgfile"
+#! $SHELL
+
+# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services.
+# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION
+# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+# NOTE: Changes made to this file will be lost: look at ltmain.sh.
+#
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005,
+# 2006, 2007, 2008 Free Software Foundation, Inc.
+# Written by Gordon Matzigkeit, 1996
+#
+# This file is part of GNU Libtool.
+#
+# GNU Libtool 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.
+#
+# As a special exception to the GNU General Public License,
+# if you distribute this file as part of a program or library that
+# is built using GNU Libtool, you may include this file under the
+# same distribution terms that you use for the rest of that program.
+#
+# GNU Libtool 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 GNU Libtool; see the file COPYING. If not, a copy
+# can be downloaded from http://www.gnu.org/licenses/gpl.html, or
+# obtained by writing to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+
+# The names of the tagged configurations supported by this script.
+available_tags=""
+
+# ### BEGIN LIBTOOL CONFIG
+
+# Whether or not to build static libraries.
+build_old_libs=$enable_static
+
+# Which release of libtool.m4 was used?
+macro_version=$macro_version
+macro_revision=$macro_revision
+
+# Whether or not to build shared libraries.
+build_libtool_libs=$enable_shared
+
+# What type of objects to build.
+pic_mode=$pic_mode
+
+# Whether or not to optimize for fast installation.
+fast_install=$enable_fast_install
+
+# The host system.
+host_alias=$host_alias
+host=$host
+host_os=$host_os
+
+# The build system.
+build_alias=$build_alias
+build=$build
+build_os=$build_os
+
+# A sed program that does not truncate output.
+SED=$lt_SED
+
+# Sed that helps us avoid accidentally triggering echo(1) options like -n.
+Xsed="\$SED -e 1s/^X//"
+
+# A grep program that handles long lines.
+GREP=$lt_GREP
+
+# An ERE matcher.
+EGREP=$lt_EGREP
+
+# A literal string matcher.
+FGREP=$lt_FGREP
+
+# A BSD- or MS-compatible name lister.
+NM=$lt_NM
+
+# Whether we need soft or hard links.
+LN_S=$lt_LN_S
+
+# What is the maximum length of a command?
+max_cmd_len=$max_cmd_len
+
+# Object file suffix (normally "o").
+objext=$ac_objext
+
+# Executable file suffix (normally "").
+exeext=$exeext
+
+# whether the shell understands "unset".
+lt_unset=$lt_unset
+
+# turn spaces into newlines.
+SP2NL=$lt_lt_SP2NL
+
+# turn newlines into spaces.
+NL2SP=$lt_lt_NL2SP
+
+# How to create reloadable object files.
+reload_flag=$lt_reload_flag
+reload_cmds=$lt_reload_cmds
+
+# An object symbol dumper.
+OBJDUMP=$lt_OBJDUMP
+
+# Method to check whether dependent libraries are shared objects.
+deplibs_check_method=$lt_deplibs_check_method
+
+# Command to use when deplibs_check_method == "file_magic".
+file_magic_cmd=$lt_file_magic_cmd
+
+# The archiver.
+AR=$lt_AR
+AR_FLAGS=$lt_AR_FLAGS
+
+# A symbol stripping program.
+STRIP=$lt_STRIP
+
+# Commands used to install an old-style archive.
+RANLIB=$lt_RANLIB
+old_postinstall_cmds=$lt_old_postinstall_cmds
+old_postuninstall_cmds=$lt_old_postuninstall_cmds
+
+# A C compiler.
+LTCC=$lt_CC
+
+# LTCC compiler flags.
+LTCFLAGS=$lt_CFLAGS
+
+# Take the output of nm and produce a listing of raw symbols and C names.
+global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe
+
+# Transform the output of nm in a proper C declaration.
+global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl
+
+# Transform the output of nm in a C name address pair.
+global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address
+
+# Transform the output of nm in a C name address pair when lib prefix is needed.
+global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix
+
+# The name of the directory that contains temporary libtool files.
+objdir=$objdir
+
+# Shell to use when invoking shell scripts.
+SHELL=$lt_SHELL
+
+# An echo program that does not interpret backslashes.
+ECHO=$lt_ECHO
+
+# Used to examine libraries when file_magic_cmd begins with "file".
+MAGIC_CMD=$MAGIC_CMD
+
+# Must we lock files when doing compilation?
+need_locks=$lt_need_locks
+
+# Tool to manipulate archived DWARF debug symbol files on Mac OS X.
+DSYMUTIL=$lt_DSYMUTIL
+
+# Tool to change global to local symbols on Mac OS X.
+NMEDIT=$lt_NMEDIT
+
+# Tool to manipulate fat objects and archives on Mac OS X.
+LIPO=$lt_LIPO
+
+# ldd/readelf like tool for Mach-O binaries on Mac OS X.
+OTOOL=$lt_OTOOL
+
+# ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4.
+OTOOL64=$lt_OTOOL64
+
+# Old archive suffix (normally "a").
+libext=$libext
+
+# Shared library suffix (normally ".so").
+shrext_cmds=$lt_shrext_cmds
+
+# The commands to extract the exported symbol list from a shared archive.
+extract_expsyms_cmds=$lt_extract_expsyms_cmds
+
+# Variables whose values should be saved in libtool wrapper scripts and
+# restored at link time.
+variables_saved_for_relink=$lt_variables_saved_for_relink
+
+# Do we need the "lib" prefix for modules?
+need_lib_prefix=$need_lib_prefix
+
+# Do we need a version for libraries?
+need_version=$need_version
+
+# Library versioning type.
+version_type=$version_type
+
+# Shared library runtime path variable.
+runpath_var=$runpath_var
+
+# Shared library path variable.
+shlibpath_var=$shlibpath_var
+
+# Is shlibpath searched before the hard-coded library search path?
+shlibpath_overrides_runpath=$shlibpath_overrides_runpath
+
+# Format of library name prefix.
+libname_spec=$lt_libname_spec
+
+# List of archive names. First name is the real one, the rest are links.
+# The last name is the one that the linker finds with -lNAME
+library_names_spec=$lt_library_names_spec
+
+# The coded name of the library, if different from the real name.
+soname_spec=$lt_soname_spec
+
+# Command to use after installation of a shared archive.
+postinstall_cmds=$lt_postinstall_cmds
+
+# Command to use after uninstallation of a shared archive.
+postuninstall_cmds=$lt_postuninstall_cmds
+
+# Commands used to finish a libtool library installation in a directory.
+finish_cmds=$lt_finish_cmds
+
+# As "finish_cmds", except a single script fragment to be evaled but
+# not shown.
+finish_eval=$lt_finish_eval
+
+# Whether we should hardcode library paths into libraries.
+hardcode_into_libs=$hardcode_into_libs
+
+# Compile-time system search path for libraries.
+sys_lib_search_path_spec=$lt_sys_lib_search_path_spec
+
+# Run-time system search path for libraries.
+sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec
+
+# Whether dlopen is supported.
+dlopen_support=$enable_dlopen
+
+# Whether dlopen of programs is supported.
+dlopen_self=$enable_dlopen_self
+
+# Whether dlopen of statically linked programs is supported.
+dlopen_self_static=$enable_dlopen_self_static
+
+# Commands to strip libraries.
+old_striplib=$lt_old_striplib
+striplib=$lt_striplib
+
+
+# The linker used to build libraries.
+LD=$lt_LD
+
+# Commands used to build an old-style archive.
+old_archive_cmds=$lt_old_archive_cmds
+
+# A language specific compiler.
+CC=$lt_compiler
+
+# Is the compiler the GNU compiler?
+with_gcc=$GCC
+
+# Compiler flag to turn off builtin functions.
+no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag
+
+# How to pass a linker flag through the compiler.
+wl=$lt_lt_prog_compiler_wl
+
+# Additional compiler flags for building library objects.
+pic_flag=$lt_lt_prog_compiler_pic
+
+# Compiler flag to prevent dynamic linking.
+link_static_flag=$lt_lt_prog_compiler_static
+
+# Does compiler simultaneously support -c and -o options?
+compiler_c_o=$lt_lt_cv_prog_compiler_c_o
+
+# Whether or not to add -lc for building shared libraries.
+build_libtool_need_lc=$archive_cmds_need_lc
+
+# Whether or not to disallow shared libs when runtime libs are static.
+allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes
+
+# Compiler flag to allow reflexive dlopens.
+export_dynamic_flag_spec=$lt_export_dynamic_flag_spec
+
+# Compiler flag to generate shared objects directly from archives.
+whole_archive_flag_spec=$lt_whole_archive_flag_spec
+
+# Whether the compiler copes with passing no objects directly.
+compiler_needs_object=$lt_compiler_needs_object
+
+# Create an old-style archive from a shared archive.
+old_archive_from_new_cmds=$lt_old_archive_from_new_cmds
+
+# Create a temporary old-style archive to link instead of a shared archive.
+old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds
+
+# Commands used to build a shared archive.
+archive_cmds=$lt_archive_cmds
+archive_expsym_cmds=$lt_archive_expsym_cmds
+
+# Commands used to build a loadable module if different from building
+# a shared archive.
+module_cmds=$lt_module_cmds
+module_expsym_cmds=$lt_module_expsym_cmds
+
+# Whether we are building with GNU ld or not.
+with_gnu_ld=$lt_with_gnu_ld
+
+# Flag that allows shared libraries with undefined symbols to be built.
+allow_undefined_flag=$lt_allow_undefined_flag
+
+# Flag that enforces no undefined symbols.
+no_undefined_flag=$lt_no_undefined_flag
+
+# Flag to hardcode \$libdir into a binary during linking.
+# This must work even if \$libdir does not exist
+hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec
+
+# If ld is used when linking, flag to hardcode \$libdir into a binary
+# during linking. This must work even if \$libdir does not exist.
+hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld
+
+# Whether we need a single "-rpath" flag with a separated argument.
+hardcode_libdir_separator=$lt_hardcode_libdir_separator
+
+# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes
+# DIR into the resulting binary.
+hardcode_direct=$hardcode_direct
+
+# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes
+# DIR into the resulting binary and the resulting library dependency is
+# "absolute",i.e impossible to change by setting \${shlibpath_var} if the
+# library is relocated.
+hardcode_direct_absolute=$hardcode_direct_absolute
+
+# Set to "yes" if using the -LDIR flag during linking hardcodes DIR
+# into the resulting binary.
+hardcode_minus_L=$hardcode_minus_L
+
+# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR
+# into the resulting binary.
+hardcode_shlibpath_var=$hardcode_shlibpath_var
+
+# Set to "yes" if building a shared library automatically hardcodes DIR
+# into the library and all subsequent libraries and executables linked
+# against it.
+hardcode_automatic=$hardcode_automatic
+
+# Set to yes if linker adds runtime paths of dependent libraries
+# to runtime path list.
+inherit_rpath=$inherit_rpath
+
+# Whether libtool must link a program against all its dependency libraries.
+link_all_deplibs=$link_all_deplibs
+
+# Fix the shell variable \$srcfile for the compiler.
+fix_srcfile_path=$lt_fix_srcfile_path
+
+# Set to "yes" if exported symbols are required.
+always_export_symbols=$always_export_symbols
+
+# The commands to list exported symbols.
+export_symbols_cmds=$lt_export_symbols_cmds
+
+# Symbols that should not be listed in the preloaded symbols.
+exclude_expsyms=$lt_exclude_expsyms
+
+# Symbols that must always be exported.
+include_expsyms=$lt_include_expsyms
+
+# Commands necessary for linking programs (against libraries) with templates.
+prelink_cmds=$lt_prelink_cmds
+
+# Specify filename containing input files.
+file_list_spec=$lt_file_list_spec
+
+# How to hardcode a shared library path into an executable.
+hardcode_action=$hardcode_action
+
+# ### END LIBTOOL CONFIG
+
+_LT_EOF
+
+ case $host_os in
+ aix3*)
+ cat <<\_LT_EOF >> "$cfgfile"
+# AIX sometimes has problems with the GCC collect2 program. For some
+# reason, if we set the COLLECT_NAMES environment variable, the problems
+# vanish in a puff of smoke.
+if test "X${COLLECT_NAMES+set}" != Xset; then
+ COLLECT_NAMES=
+ export COLLECT_NAMES
+fi
+_LT_EOF
+ ;;
+ esac
+
+
+ltmain="$ac_aux_dir/ltmain.sh"
+
+
+ # We use sed instead of cat because bash on DJGPP gets confused if
+ # if finds mixed CR/LF and LF-only lines. Since sed operates in
+ # text mode, it properly converts lines to CR/LF. This bash problem
+ # is reportedly fixed, but why not run on old versions too?
+ sed '/^# Generated shell functions inserted here/q' "$ltmain" >> "$cfgfile" \
+ || (rm -f "$cfgfile"; exit 1)
+
+ case $xsi_shell in
+ yes)
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_dirname file append nondir_replacement
+# Compute the dirname of FILE. If nonempty, add APPEND to the result,
+# otherwise set result to NONDIR_REPLACEMENT.
+func_dirname ()
+{
+ case ${1} in
+ */*) func_dirname_result="${1%/*}${2}" ;;
+ * ) func_dirname_result="${3}" ;;
+ esac
+}
+
+# func_basename file
+func_basename ()
+{
+ func_basename_result="${1##*/}"
+}
+
+# func_dirname_and_basename file append nondir_replacement
+# perform func_basename and func_dirname in a single function
+# call:
+# dirname: Compute the dirname of FILE. If nonempty,
+# add APPEND to the result, otherwise set result
+# to NONDIR_REPLACEMENT.
+# value returned in "$func_dirname_result"
+# basename: Compute filename of FILE.
+# value retuned in "$func_basename_result"
+# Implementation must be kept synchronized with func_dirname
+# and func_basename. For efficiency, we do not delegate to
+# those functions but instead duplicate the functionality here.
+func_dirname_and_basename ()
+{
+ case ${1} in
+ */*) func_dirname_result="${1%/*}${2}" ;;
+ * ) func_dirname_result="${3}" ;;
+ esac
+ func_basename_result="${1##*/}"
+}
+
+# func_stripname prefix suffix name
+# strip PREFIX and SUFFIX off of NAME.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+func_stripname ()
+{
+ # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are
+ # positional parameters, so assign one to ordinary parameter first.
+ func_stripname_result=${3}
+ func_stripname_result=${func_stripname_result#"${1}"}
+ func_stripname_result=${func_stripname_result%"${2}"}
+}
+
+# func_opt_split
+func_opt_split ()
+{
+ func_opt_split_opt=${1%%=*}
+ func_opt_split_arg=${1#*=}
+}
+
+# func_lo2o object
+func_lo2o ()
+{
+ case ${1} in
+ *.lo) func_lo2o_result=${1%.lo}.${objext} ;;
+ *) func_lo2o_result=${1} ;;
+ esac
+}
+
+# func_xform libobj-or-source
+func_xform ()
+{
+ func_xform_result=${1%.*}.lo
+}
+
+# func_arith arithmetic-term...
+func_arith ()
+{
+ func_arith_result=$(( $* ))
+}
+
+# func_len string
+# STRING may not start with a hyphen.
+func_len ()
+{
+ func_len_result=${#1}
+}
+
+_LT_EOF
+ ;;
+ *) # Bourne compatible functions.
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_dirname file append nondir_replacement
+# Compute the dirname of FILE. If nonempty, add APPEND to the result,
+# otherwise set result to NONDIR_REPLACEMENT.
+func_dirname ()
+{
+ # Extract subdirectory from the argument.
+ func_dirname_result=`$ECHO "X${1}" | $Xsed -e "$dirname"`
+ if test "X$func_dirname_result" = "X${1}"; then
+ func_dirname_result="${3}"
+ else
+ func_dirname_result="$func_dirname_result${2}"
+ fi
+}
+
+# func_basename file
+func_basename ()
+{
+ func_basename_result=`$ECHO "X${1}" | $Xsed -e "$basename"`
+}
+
+
+# func_stripname prefix suffix name
+# strip PREFIX and SUFFIX off of NAME.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+# func_strip_suffix prefix name
+func_stripname ()
+{
+ case ${2} in
+ .*) func_stripname_result=`$ECHO "X${3}" \
+ | $Xsed -e "s%^${1}%%" -e "s%\\\\${2}\$%%"`;;
+ *) func_stripname_result=`$ECHO "X${3}" \
+ | $Xsed -e "s%^${1}%%" -e "s%${2}\$%%"`;;
+ esac
+}
+
+# sed scripts:
+my_sed_long_opt='1s/^\(-[^=]*\)=.*/\1/;q'
+my_sed_long_arg='1s/^-[^=]*=//'
+
+# func_opt_split
+func_opt_split ()
+{
+ func_opt_split_opt=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_opt"`
+ func_opt_split_arg=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_arg"`
+}
+
+# func_lo2o object
+func_lo2o ()
+{
+ func_lo2o_result=`$ECHO "X${1}" | $Xsed -e "$lo2o"`
+}
+
+# func_xform libobj-or-source
+func_xform ()
+{
+ func_xform_result=`$ECHO "X${1}" | $Xsed -e 's/\.[^.]*$/.lo/'`
+}
+
+# func_arith arithmetic-term...
+func_arith ()
+{
+ func_arith_result=`expr "$@"`
+}
+
+# func_len string
+# STRING may not start with a hyphen.
+func_len ()
+{
+ func_len_result=`expr "$1" : ".*" 2>/dev/null || echo $max_cmd_len`
+}
+
+_LT_EOF
+esac
+
+case $lt_shell_append in
+ yes)
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_append var value
+# Append VALUE to the end of shell variable VAR.
+func_append ()
+{
+ eval "$1+=\$2"
+}
+_LT_EOF
+ ;;
+ *)
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_append var value
+# Append VALUE to the end of shell variable VAR.
+func_append ()
+{
+ eval "$1=\$$1\$2"
+}
+
+_LT_EOF
+ ;;
+ esac
+
+
+ sed -n '/^# Generated shell functions inserted here/,$p' "$ltmain" >> "$cfgfile" \
+ || (rm -f "$cfgfile"; exit 1)
+
+ mv -f "$cfgfile" "$ofile" ||
+ (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile")
+ chmod +x "$ofile"
+
+ ;;
+
+ esac
+done # for ac_tag
+
+
+{ (exit 0); exit 0; }
+_ACEOF
+chmod +x $CONFIG_STATUS
+ac_clean_files=$ac_clean_files_save
+
+test $ac_write_fail = 0 ||
+ { { $as_echo "$as_me:$LINENO: error: write failure creating $CONFIG_STATUS" >&5
+$as_echo "$as_me: error: write failure creating $CONFIG_STATUS" >&2;}
+ { (exit 1); exit 1; }; }
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded. So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status. When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+ ac_cs_success=:
+ ac_config_status_args=
+ test "$silent" = yes &&
+ ac_config_status_args="$ac_config_status_args --quiet"
+ exec 5>/dev/null
+ $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+ exec 5>>config.log
+ # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+ # would make configure fail if this is the last instruction.
+ $ac_cs_success || { (exit 1); exit 1; }
+fi
+if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
+ { $as_echo "$as_me:$LINENO: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+fi
+
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..1ecdc7b
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,60 @@
+AC_PREREQ(2.60)
+AC_INIT(bluez, 4.91)
+
+AM_INIT_AUTOMAKE([foreign subdir-objects])
+AM_CONFIG_HEADER(config.h)
+
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+AM_MAINTAINER_MODE
+
+PKG_PROG_PKG_CONFIG
+
+AC_INIT_BLUEZ
+
+COMPILER_FLAGS
+
+AC_LANG_C
+
+AC_PROG_CC
+AM_PROG_CC_C_O
+AC_PROG_CC_PIE
+AC_PROG_INSTALL
+AC_PROG_YACC
+AM_PROG_LEX
+AM_PROG_MKDIR_P
+
+m4_define([_LT_AC_TAGCONFIG], [])
+m4_ifdef([AC_LIBTOOL_TAGS], [AC_LIBTOOL_TAGS([])])
+
+AC_DISABLE_STATIC
+AC_PROG_LIBTOOL
+
+AC_FUNC_PPOLL
+
+AC_CHECK_LIB(dl, dlopen, dummy=yes,
+ AC_MSG_ERROR(dynamic linking loader is required))
+
+AC_PATH_DBUS
+AC_PATH_GLIB
+AC_PATH_ALSA
+AC_PATH_GSTREAMER
+AC_PATH_USB
+AC_PATH_SNDFILE
+AC_PATH_OUI
+AC_PATH_READLINE
+
+AC_ARG_BLUEZ
+
+AC_ARG_ENABLE(capng, AC_HELP_STRING([--enable-capng],
+ [enable capabilities support]), [enable_capng=${enableval}])
+if (test "${enable_capng}" = "yes"); then
+ PKG_CHECK_MODULES(CAPNG, libcap-ng, dummy=yes,
+ AC_MSG_ERROR(Capabilities library is required))
+ AC_SUBST(CAPNG_CFLAGS)
+ AC_SUBST(CAPNG_LIBS)
+ AC_DEFINE(HAVE_CAPNG, 1, [Define to 1 if you have capabilities library.])
+fi
+
+AC_OUTPUT(Makefile scripts/bluetooth.rules doc/version.xml
+ src/bluetoothd.8 bluez.pc)
diff --git a/cups/cups.h b/cups/cups.h
new file mode 100644
index 0000000..f4e0c01
--- /dev/null
+++ b/cups/cups.h
@@ -0,0 +1,38 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2003-2010 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
+ *
+ */
+
+enum { /**** Backend exit codes ****/
+ CUPS_BACKEND_OK = 0, /* Job completed successfully */
+ CUPS_BACKEND_FAILED = 1, /* Job failed, use error-policy */
+ CUPS_BACKEND_AUTH_REQUIRED = 2, /* Job failed, authentication required */
+ CUPS_BACKEND_HOLD = 3, /* Job failed, hold job */
+ CUPS_BACKEND_STOP = 4, /* Job failed, stop queue */
+ CUPS_BACKEND_CANCEL = 5, /* Job failed, cancel job */
+ CUPS_BACKEND_RETRY = 6, /* Failure requires us to retry (BlueZ specific) */
+};
+
+int sdp_search_spp(sdp_session_t *sdp, uint8_t *channel);
+int sdp_search_hcrp(sdp_session_t *sdp, unsigned short *ctrl_psm, unsigned short *data_psm);
+
+int spp_print(bdaddr_t *src, bdaddr_t *dst, uint8_t channel, int fd, int copies, const char *cups_class);
+int hcrp_print(bdaddr_t *src, bdaddr_t *dst, unsigned short ctrl_psm, unsigned short data_psm, int fd, int copies, const char *cups_class);
diff --git a/cups/hcrp.c b/cups/hcrp.c
new file mode 100644
index 0000000..7aafcdc
--- /dev/null
+++ b/cups/hcrp.c
@@ -0,0 +1,353 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2003-2010 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 <string.h>
+#include <signal.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <netinet/in.h>
+
+#include "cups.h"
+
+#define HCRP_PDU_CREDIT_GRANT 0x0001
+#define HCRP_PDU_CREDIT_REQUEST 0x0002
+#define HCRP_PDU_GET_LPT_STATUS 0x0005
+
+#define HCRP_STATUS_FEATURE_UNSUPPORTED 0x0000
+#define HCRP_STATUS_SUCCESS 0x0001
+#define HCRP_STATUS_CREDIT_SYNC_ERROR 0x0002
+#define HCRP_STATUS_GENERIC_FAILURE 0xffff
+
+struct hcrp_pdu_hdr {
+ uint16_t pid;
+ uint16_t tid;
+ uint16_t plen;
+} __attribute__ ((packed));
+#define HCRP_PDU_HDR_SIZE 6
+
+struct hcrp_credit_grant_cp {
+ uint32_t credit;
+} __attribute__ ((packed));
+#define HCRP_CREDIT_GRANT_CP_SIZE 4
+
+struct hcrp_credit_grant_rp {
+ uint16_t status;
+} __attribute__ ((packed));
+#define HCRP_CREDIT_GRANT_RP_SIZE 2
+
+struct hcrp_credit_request_rp {
+ uint16_t status;
+ uint32_t credit;
+} __attribute__ ((packed));
+#define HCRP_CREDIT_REQUEST_RP_SIZE 6
+
+struct hcrp_get_lpt_status_rp {
+ uint16_t status;
+ uint8_t lpt_status;
+} __attribute__ ((packed));
+#define HCRP_GET_LPT_STATUS_RP_SIZE 3
+
+static int hcrp_credit_grant(int sk, uint16_t tid, uint32_t credit)
+{
+ struct hcrp_pdu_hdr hdr;
+ struct hcrp_credit_grant_cp cp;
+ struct hcrp_credit_grant_rp rp;
+ unsigned char buf[128];
+ int len;
+
+ hdr.pid = htons(HCRP_PDU_CREDIT_GRANT);
+ hdr.tid = htons(tid);
+ hdr.plen = htons(HCRP_CREDIT_GRANT_CP_SIZE);
+ cp.credit = credit;
+ memcpy(buf, &hdr, HCRP_PDU_HDR_SIZE);
+ memcpy(buf + HCRP_PDU_HDR_SIZE, &cp, HCRP_CREDIT_GRANT_CP_SIZE);
+ len = write(sk, buf, HCRP_PDU_HDR_SIZE + HCRP_CREDIT_GRANT_CP_SIZE);
+
+ len = read(sk, buf, sizeof(buf));
+ memcpy(&hdr, buf, HCRP_PDU_HDR_SIZE);
+ memcpy(&rp, buf + HCRP_PDU_HDR_SIZE, HCRP_CREDIT_GRANT_RP_SIZE);
+
+ if (ntohs(rp.status) != HCRP_STATUS_SUCCESS) {
+ errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int hcrp_credit_request(int sk, uint16_t tid, uint32_t *credit)
+{
+ struct hcrp_pdu_hdr hdr;
+ struct hcrp_credit_request_rp rp;
+ unsigned char buf[128];
+ int len;
+
+ hdr.pid = htons(HCRP_PDU_CREDIT_REQUEST);
+ hdr.tid = htons(tid);
+ hdr.plen = htons(0);
+ memcpy(buf, &hdr, HCRP_PDU_HDR_SIZE);
+ len = write(sk, buf, HCRP_PDU_HDR_SIZE);
+
+ len = read(sk, buf, sizeof(buf));
+ memcpy(&hdr, buf, HCRP_PDU_HDR_SIZE);
+ memcpy(&rp, buf + HCRP_PDU_HDR_SIZE, HCRP_CREDIT_REQUEST_RP_SIZE);
+
+ if (ntohs(rp.status) != HCRP_STATUS_SUCCESS) {
+ errno = EIO;
+ return -1;
+ }
+
+ if (credit)
+ *credit = ntohl(rp.credit);
+
+ return 0;
+}
+
+static int hcrp_get_lpt_status(int sk, uint16_t tid, uint8_t *lpt_status)
+{
+ struct hcrp_pdu_hdr hdr;
+ struct hcrp_get_lpt_status_rp rp;
+ unsigned char buf[128];
+ int len;
+
+ hdr.pid = htons(HCRP_PDU_GET_LPT_STATUS);
+ hdr.tid = htons(tid);
+ hdr.plen = htons(0);
+ memcpy(buf, &hdr, HCRP_PDU_HDR_SIZE);
+ len = write(sk, buf, HCRP_PDU_HDR_SIZE);
+
+ len = read(sk, buf, sizeof(buf));
+ memcpy(&hdr, buf, HCRP_PDU_HDR_SIZE);
+ memcpy(&rp, buf + HCRP_PDU_HDR_SIZE, HCRP_GET_LPT_STATUS_RP_SIZE);
+
+ if (ntohs(rp.status) != HCRP_STATUS_SUCCESS) {
+ errno = EIO;
+ return -1;
+ }
+
+ if (lpt_status)
+ *lpt_status = rp.lpt_status;
+
+ return 0;
+}
+
+static inline int hcrp_get_next_tid(int tid)
+{
+ if (tid > 0xf000)
+ return 0;
+ else
+ return tid + 1;
+}
+
+int hcrp_print(bdaddr_t *src, bdaddr_t *dst, unsigned short ctrl_psm, unsigned short data_psm, int fd, int copies, const char *cups_class)
+{
+ struct sockaddr_l2 addr;
+ struct l2cap_options opts;
+ socklen_t size;
+ unsigned char buf[2048];
+ int i, ctrl_sk, data_sk, count, len, timeout = 0;
+ unsigned int mtu;
+ uint8_t status;
+ uint16_t tid = 0;
+ uint32_t tmp, credit = 0;
+
+ if ((ctrl_sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) < 0) {
+ perror("ERROR: Can't create socket");
+ if (cups_class)
+ return CUPS_BACKEND_FAILED;
+ else
+ return CUPS_BACKEND_RETRY;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ bacpy(&addr.l2_bdaddr, src);
+
+ if (bind(ctrl_sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("ERROR: Can't bind socket");
+ close(ctrl_sk);
+ if (cups_class)
+ return CUPS_BACKEND_FAILED;
+ else
+ return CUPS_BACKEND_RETRY;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ bacpy(&addr.l2_bdaddr, dst);
+ addr.l2_psm = htobs(ctrl_psm);
+
+ if (connect(ctrl_sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("ERROR: Can't connect to device");
+ close(ctrl_sk);
+ if (cups_class)
+ return CUPS_BACKEND_FAILED;
+ else
+ return CUPS_BACKEND_RETRY;
+ }
+
+ if ((data_sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) < 0) {
+ perror("ERROR: Can't create socket");
+ close(ctrl_sk);
+ if (cups_class)
+ return CUPS_BACKEND_FAILED;
+ else
+ return CUPS_BACKEND_RETRY;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ bacpy(&addr.l2_bdaddr, src);
+
+ if (bind(data_sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("ERROR: Can't bind socket");
+ close(data_sk);
+ close(ctrl_sk);
+ if (cups_class)
+ return CUPS_BACKEND_FAILED;
+ else
+ return CUPS_BACKEND_RETRY;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ bacpy(&addr.l2_bdaddr, dst);
+ addr.l2_psm = htobs(data_psm);
+
+ if (connect(data_sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("ERROR: Can't connect to device");
+ close(data_sk);
+ close(ctrl_sk);
+ if (cups_class)
+ return CUPS_BACKEND_FAILED;
+ else
+ return CUPS_BACKEND_RETRY;
+ }
+
+ fputs("STATE: -connecting-to-device\n", stderr);
+
+ memset(&opts, 0, sizeof(opts));
+ size = sizeof(opts);
+
+ if (getsockopt(data_sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &size) < 0) {
+ perror("ERROR: Can't get socket options");
+ close(data_sk);
+ close(ctrl_sk);
+ if (cups_class)
+ return CUPS_BACKEND_FAILED;
+ else
+ return CUPS_BACKEND_RETRY;
+ }
+
+ mtu = opts.omtu;
+
+ /* Ignore SIGTERM signals if printing from stdin */
+ if (fd == 0) {
+#ifdef HAVE_SIGSET
+ sigset(SIGTERM, SIG_IGN);
+#elif defined(HAVE_SIGACTION)
+ memset(&action, 0, sizeof(action));
+ sigemptyset(&action.sa_mask);
+ action.sa_handler = SIG_IGN;
+ sigaction(SIGTERM, &action, NULL);
+#else
+ signal(SIGTERM, SIG_IGN);
+#endif /* HAVE_SIGSET */
+ }
+
+ tid = hcrp_get_next_tid(tid);
+ if (hcrp_credit_grant(ctrl_sk, tid, 0) < 0) {
+ fprintf(stderr, "ERROR: Can't grant initial credits\n");
+ close(data_sk);
+ close(ctrl_sk);
+ if (cups_class)
+ return CUPS_BACKEND_FAILED;
+ else
+ return CUPS_BACKEND_RETRY;
+ }
+
+ for (i = 0; i < copies; i++) {
+
+ if (fd != 0) {
+ fprintf(stderr, "PAGE: 1 1\n");
+ lseek(fd, 0, SEEK_SET);
+ }
+
+ while (1) {
+ if (credit < mtu) {
+ tid = hcrp_get_next_tid(tid);
+ if (!hcrp_credit_request(ctrl_sk, tid, &tmp)) {
+ credit += tmp;
+ timeout = 0;
+ }
+ }
+
+ if (!credit) {
+ if (timeout++ > 300) {
+ tid = hcrp_get_next_tid(tid);
+ if (!hcrp_get_lpt_status(ctrl_sk, tid, &status))
+ fprintf(stderr, "ERROR: LPT status 0x%02x\n", status);
+ break;
+ }
+
+ sleep(1);
+ continue;
+ }
+
+ count = read(fd, buf, (credit > mtu) ? mtu : credit);
+ if (count <= 0)
+ break;
+
+ len = write(data_sk, buf, count);
+ if (len < 0) {
+ perror("ERROR: Error writing to device");
+ close(data_sk);
+ close(ctrl_sk);
+ return CUPS_BACKEND_FAILED;
+ }
+
+ if (len != count)
+ fprintf(stderr, "ERROR: Can't send complete data\n");
+
+ credit -= len;
+ }
+
+ }
+
+ close(data_sk);
+ close(ctrl_sk);
+
+ return CUPS_BACKEND_OK;
+}
diff --git a/cups/main.c b/cups/main.c
new file mode 100644
index 0000000..7f3f4b0
--- /dev/null
+++ b/cups/main.c
@@ -0,0 +1,876 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2003-2010 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 <signal.h>
+#include <sys/socket.h>
+#include <glib.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <gdbus.h>
+
+#include "cups.h"
+
+struct cups_device {
+ char *bdaddr;
+ char *name;
+ char *id;
+};
+
+static GSList *device_list = NULL;
+static GMainLoop *loop = NULL;
+static DBusConnection *conn = NULL;
+static gboolean doing_disco = FALSE;
+
+#define ATTRID_1284ID 0x0300
+
+struct context_data {
+ gboolean found;
+ char *id;
+};
+
+static void element_start(GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data, GError **err)
+{
+ struct context_data *ctx_data = user_data;
+
+ if (!strcmp(element_name, "record"))
+ return;
+
+ if (!strcmp(element_name, "attribute")) {
+ int i;
+ for (i = 0; attribute_names[i]; i++) {
+ if (strcmp(attribute_names[i], "id") != 0)
+ continue;
+ if (strtol(attribute_values[i], 0, 0) == ATTRID_1284ID)
+ ctx_data->found = TRUE;
+ break;
+ }
+ return;
+ }
+
+ if (ctx_data->found && !strcmp(element_name, "text")) {
+ int i;
+ for (i = 0; attribute_names[i]; i++) {
+ if (!strcmp(attribute_names[i], "value")) {
+ ctx_data->id = g_strdup(attribute_values[i] + 2);
+ ctx_data->found = FALSE;
+ }
+ }
+ }
+}
+
+static GMarkupParser parser = {
+ element_start, NULL, NULL, NULL, NULL
+};
+
+static char *sdp_xml_parse_record(const char *data)
+{
+ GMarkupParseContext *ctx;
+ struct context_data ctx_data;
+ int size;
+
+ size = strlen(data);
+ ctx_data.found = FALSE;
+ ctx_data.id = NULL;
+ ctx = g_markup_parse_context_new(&parser, 0, &ctx_data, NULL);
+
+ if (g_markup_parse_context_parse(ctx, data, size, NULL) == FALSE) {
+ g_markup_parse_context_free(ctx);
+ g_free(ctx_data.id);
+ return NULL;
+ }
+
+ g_markup_parse_context_free(ctx);
+
+ return ctx_data.id;
+}
+
+static char *device_get_ieee1284_id(const char *adapter, const char *device)
+{
+ DBusMessage *message, *reply;
+ DBusMessageIter iter, reply_iter;
+ DBusMessageIter reply_iter_entry;
+ const char *hcr_print = "00001126-0000-1000-8000-00805f9b34fb";
+ const char *xml;
+ char *id = NULL;
+
+ /* Look for the service handle of the HCRP service */
+ message = dbus_message_new_method_call("org.bluez", device,
+ "org.bluez.Device",
+ "DiscoverServices");
+ dbus_message_iter_init_append(message, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &hcr_print);
+
+ reply = dbus_connection_send_with_reply_and_block(conn,
+ message, -1, NULL);
+
+ dbus_message_unref(message);
+
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init(reply, &reply_iter);
+
+ if (dbus_message_iter_get_arg_type(&reply_iter) != DBUS_TYPE_ARRAY) {
+ dbus_message_unref(reply);
+ return NULL;
+ }
+
+ dbus_message_iter_recurse(&reply_iter, &reply_iter_entry);
+
+ /* Hopefully we only get one handle, or take a punt */
+ while (dbus_message_iter_get_arg_type(&reply_iter_entry) ==
+ DBUS_TYPE_DICT_ENTRY) {
+ guint32 key;
+ DBusMessageIter dict_entry;
+
+ dbus_message_iter_recurse(&reply_iter_entry, &dict_entry);
+
+ /* Key ? */
+ dbus_message_iter_get_basic(&dict_entry, &key);
+ if (!key) {
+ dbus_message_iter_next(&reply_iter_entry);
+ continue;
+ }
+
+ /* Try to get the value */
+ if (!dbus_message_iter_next(&dict_entry)) {
+ dbus_message_iter_next(&reply_iter_entry);
+ continue;
+ }
+
+ dbus_message_iter_get_basic(&dict_entry, &xml);
+
+ id = sdp_xml_parse_record(xml);
+ if (id != NULL)
+ break;
+ dbus_message_iter_next(&reply_iter_entry);
+ }
+
+ dbus_message_unref(reply);
+
+ return id;
+}
+
+static void print_printer_details(const char *name, const char *bdaddr,
+ const char *id)
+{
+ char *uri, *escaped;
+
+ escaped = g_strdelimit(g_strdup(name), "\"", '\'');
+ uri = g_strdup_printf("bluetooth://%c%c%c%c%c%c%c%c%c%c%c%c",
+ bdaddr[0], bdaddr[1],
+ bdaddr[3], bdaddr[4],
+ bdaddr[6], bdaddr[7],
+ bdaddr[9], bdaddr[10],
+ bdaddr[12], bdaddr[13],
+ bdaddr[15], bdaddr[16]);
+ printf("direct %s \"%s\" \"%s (Bluetooth)\"", uri, escaped, escaped);
+ if (id != NULL)
+ printf(" \"%s\"\n", id);
+ else
+ printf("\n");
+ g_free(escaped);
+ g_free(uri);
+}
+
+static void add_device_to_list(const char *name, const char *bdaddr,
+ const char *id)
+{
+ struct cups_device *device;
+ GSList *l;
+
+ /* Look for the device in the list */
+ for (l = device_list; l != NULL; l = l->next) {
+ device = (struct cups_device *) l->data;
+
+ if (strcmp(device->bdaddr, bdaddr) == 0) {
+ if (device->name != name) {
+ g_free(device->name);
+ device->name = g_strdup(name);
+ }
+ g_free(device->id);
+ device->id = g_strdup(id);
+ return;
+ }
+ }
+
+ /* Or add it to the list if it's not there */
+ device = g_new0(struct cups_device, 1);
+ device->bdaddr = g_strdup(bdaddr);
+ device->name = g_strdup(name);
+ device->id = g_strdup(id);
+
+ device_list = g_slist_prepend(device_list, device);
+ print_printer_details(device->name, device->bdaddr, device->id);
+}
+
+static gboolean parse_device_properties(DBusMessageIter *reply_iter,
+ char **name, char **bdaddr)
+{
+ guint32 class = 0;
+ DBusMessageIter reply_iter_entry;
+
+ if (dbus_message_iter_get_arg_type(reply_iter) != DBUS_TYPE_ARRAY)
+ return FALSE;
+
+ dbus_message_iter_recurse(reply_iter, &reply_iter_entry);
+
+ while (dbus_message_iter_get_arg_type(&reply_iter_entry) ==
+ DBUS_TYPE_DICT_ENTRY) {
+ const char *key;
+ DBusMessageIter dict_entry, iter_dict_val;
+
+ dbus_message_iter_recurse(&reply_iter_entry, &dict_entry);
+
+ /* Key == Class ? */
+ dbus_message_iter_get_basic(&dict_entry, &key);
+ if (!key) {
+ dbus_message_iter_next(&reply_iter_entry);
+ continue;
+ }
+
+ if (strcmp(key, "Class") != 0 &&
+ strcmp(key, "Alias") != 0 &&
+ strcmp(key, "Address") != 0) {
+ dbus_message_iter_next(&reply_iter_entry);
+ continue;
+ }
+
+ /* Try to get the value */
+ if (!dbus_message_iter_next(&dict_entry)) {
+ dbus_message_iter_next(&reply_iter_entry);
+ continue;
+ }
+ dbus_message_iter_recurse(&dict_entry, &iter_dict_val);
+ if (strcmp(key, "Class") == 0) {
+ dbus_message_iter_get_basic(&iter_dict_val, &class);
+ } else {
+ const char *value;
+ dbus_message_iter_get_basic(&iter_dict_val, &value);
+ if (strcmp(key, "Alias") == 0) {
+ *name = g_strdup(value);
+ } else if (bdaddr) {
+ *bdaddr = g_strdup(value);
+ }
+ }
+ dbus_message_iter_next(&reply_iter_entry);
+ }
+
+ if (class == 0)
+ return FALSE;
+ if (((class & 0x1f00) >> 8) == 0x06 && (class & 0x80))
+ return TRUE;
+
+ return FALSE;
+}
+
+static gboolean device_is_printer(const char *adapter, const char *device_path, char **name, char **bdaddr)
+{
+ DBusMessage *message, *reply;
+ DBusMessageIter reply_iter;
+ gboolean retval;
+
+ message = dbus_message_new_method_call("org.bluez", device_path,
+ "org.bluez.Device",
+ "GetProperties");
+
+ reply = dbus_connection_send_with_reply_and_block(conn,
+ message, -1, NULL);
+
+ dbus_message_unref(message);
+
+ if (!reply)
+ return FALSE;
+
+ dbus_message_iter_init(reply, &reply_iter);
+
+ retval = parse_device_properties(&reply_iter, name, bdaddr);
+
+ dbus_message_unref(reply);
+
+ return retval;
+}
+
+static void remote_device_found(const char *adapter, const char *bdaddr,
+ const char *name)
+{
+ DBusMessage *message, *reply, *adapter_reply;
+ DBusMessageIter iter;
+ char *object_path = NULL;
+ char *id;
+
+ adapter_reply = NULL;
+
+ if (adapter == NULL) {
+ message = dbus_message_new_method_call("org.bluez", "/",
+ "org.bluez.Manager",
+ "DefaultAdapter");
+
+ adapter_reply = dbus_connection_send_with_reply_and_block(conn,
+ message, -1, NULL);
+
+ dbus_message_unref(message);
+
+ if (dbus_message_get_args(adapter_reply, NULL,
+ DBUS_TYPE_OBJECT_PATH, &adapter,
+ DBUS_TYPE_INVALID) == FALSE)
+ return;
+ }
+
+ message = dbus_message_new_method_call("org.bluez", adapter,
+ "org.bluez.Adapter",
+ "FindDevice");
+ dbus_message_iter_init_append(message, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &bdaddr);
+
+ if (adapter_reply != NULL)
+ dbus_message_unref(adapter_reply);
+
+ reply = dbus_connection_send_with_reply_and_block(conn,
+ message, -1, NULL);
+
+ dbus_message_unref(message);
+
+ if (!reply) {
+ message = dbus_message_new_method_call("org.bluez", adapter,
+ "org.bluez.Adapter",
+ "CreateDevice");
+ dbus_message_iter_init_append(message, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &bdaddr);
+
+ reply = dbus_connection_send_with_reply_and_block(conn,
+ message, -1, NULL);
+
+ dbus_message_unref(message);
+
+ if (!reply)
+ return;
+ }
+
+ if (dbus_message_get_args(reply, NULL,
+ DBUS_TYPE_OBJECT_PATH, &object_path,
+ DBUS_TYPE_INVALID) == FALSE)
+ return;
+
+ id = device_get_ieee1284_id(adapter, object_path);
+ add_device_to_list(name, bdaddr, id);
+ g_free(id);
+}
+
+static void discovery_completed(void)
+{
+ g_slist_free(device_list);
+ device_list = NULL;
+
+ g_main_loop_quit(loop);
+}
+
+static void remote_device_disappeared(const char *bdaddr)
+{
+ GSList *l;
+
+ for (l = device_list; l != NULL; l = l->next) {
+ struct cups_device *device = l->data;
+
+ if (strcmp(device->bdaddr, bdaddr) == 0) {
+ g_free(device->name);
+ g_free(device->bdaddr);
+ g_free(device);
+ device_list = g_slist_delete_link(device_list, l);
+ return;
+ }
+ }
+}
+
+static gboolean list_known_printers(const char *adapter)
+{
+ DBusMessageIter reply_iter, iter_array;
+ DBusError error;
+ DBusMessage *message, *reply;
+
+ message = dbus_message_new_method_call("org.bluez", adapter,
+ "org.bluez.Adapter",
+ "ListDevices");
+ if (message == NULL)
+ return FALSE;
+
+ dbus_error_init(&error);
+ reply = dbus_connection_send_with_reply_and_block(conn, message,
+ -1, &error);
+
+ dbus_message_unref(message);
+
+ if (dbus_error_is_set(&error))
+ return FALSE;
+
+ dbus_message_iter_init(reply, &reply_iter);
+ if (dbus_message_iter_get_arg_type(&reply_iter) != DBUS_TYPE_ARRAY) {
+ dbus_message_unref(reply);
+ return FALSE;
+ }
+
+ dbus_message_iter_recurse(&reply_iter, &iter_array);
+ while (dbus_message_iter_get_arg_type(&iter_array) ==
+ DBUS_TYPE_OBJECT_PATH) {
+ const char *object_path;
+ char *name = NULL;
+ char *bdaddr = NULL;
+
+ dbus_message_iter_get_basic(&iter_array, &object_path);
+ if (device_is_printer(adapter, object_path, &name, &bdaddr)) {
+ char *id;
+
+ id = device_get_ieee1284_id(adapter, object_path);
+ add_device_to_list(name, bdaddr, id);
+ g_free(id);
+ }
+ g_free(name);
+ g_free(bdaddr);
+ dbus_message_iter_next(&iter_array);
+ }
+
+ dbus_message_unref(reply);
+
+ return FALSE;
+}
+
+static DBusHandlerResult filter_func(DBusConnection *connection,
+ DBusMessage *message, void *user_data)
+{
+ if (dbus_message_is_signal(message, "org.bluez.Adapter",
+ "DeviceFound")) {
+ const char *adapter, *bdaddr;
+ char *name;
+ DBusMessageIter iter;
+
+ dbus_message_iter_init(message, &iter);
+ dbus_message_iter_get_basic(&iter, &bdaddr);
+ dbus_message_iter_next(&iter);
+
+ adapter = dbus_message_get_path(message);
+ if (parse_device_properties(&iter, &name, NULL))
+ remote_device_found(adapter, bdaddr, name);
+ g_free (name);
+ } else if (dbus_message_is_signal(message, "org.bluez.Adapter",
+ "DeviceDisappeared")) {
+ const char *bdaddr;
+
+ dbus_message_get_args(message, NULL,
+ DBUS_TYPE_STRING, &bdaddr,
+ DBUS_TYPE_INVALID);
+ remote_device_disappeared(bdaddr);
+ } else if (dbus_message_is_signal(message, "org.bluez.Adapter",
+ "PropertyChanged")) {
+ DBusMessageIter iter, value_iter;
+ const char *name;
+ gboolean discovering;
+
+ dbus_message_iter_init(message, &iter);
+ dbus_message_iter_get_basic(&iter, &name);
+ if (name == NULL || strcmp(name, "Discovering") != 0)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ dbus_message_iter_next(&iter);
+ dbus_message_iter_recurse(&iter, &value_iter);
+ dbus_message_iter_get_basic(&value_iter, &discovering);
+
+ if (discovering == FALSE && doing_disco) {
+ doing_disco = FALSE;
+ discovery_completed();
+ }
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static gboolean list_printers(void)
+{
+ /* 1. Connect to the bus
+ * 2. Get the manager
+ * 3. Get the default adapter
+ * 4. Get a list of devices
+ * 5. Get the class of each device
+ * 6. Print the details from each printer device
+ */
+ DBusError error;
+ dbus_bool_t hcid_exists;
+ DBusMessage *reply, *message;
+ DBusMessageIter reply_iter;
+ char *adapter, *match;
+
+ conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
+ if (conn == NULL)
+ return TRUE;
+
+ dbus_error_init(&error);
+ hcid_exists = dbus_bus_name_has_owner(conn, "org.bluez", &error);
+ if (dbus_error_is_set(&error))
+ return TRUE;
+
+ if (!hcid_exists)
+ return TRUE;
+
+ /* Get the default adapter */
+ message = dbus_message_new_method_call("org.bluez", "/",
+ "org.bluez.Manager",
+ "DefaultAdapter");
+ if (message == NULL) {
+ dbus_connection_unref(conn);
+ return FALSE;
+ }
+
+ reply = dbus_connection_send_with_reply_and_block(conn,
+ message, -1, &error);
+
+ dbus_message_unref(message);
+
+ if (dbus_error_is_set(&error)) {
+ dbus_connection_unref(conn);
+ /* No adapter */
+ return TRUE;
+ }
+
+ dbus_message_iter_init(reply, &reply_iter);
+ if (dbus_message_iter_get_arg_type(&reply_iter) !=
+ DBUS_TYPE_OBJECT_PATH) {
+ dbus_message_unref(reply);
+ dbus_connection_unref(conn);
+ return FALSE;
+ }
+
+ dbus_message_iter_get_basic(&reply_iter, &adapter);
+ adapter = g_strdup(adapter);
+ dbus_message_unref(reply);
+
+ if (!dbus_connection_add_filter(conn, filter_func, adapter, g_free)) {
+ g_free(adapter);
+ dbus_connection_unref(conn);
+ return FALSE;
+ }
+
+#define MATCH_FORMAT \
+ "type='signal'," \
+ "interface='org.bluez.Adapter'," \
+ "sender='org.bluez'," \
+ "path='%s'"
+
+ match = g_strdup_printf(MATCH_FORMAT, adapter);
+ dbus_bus_add_match(conn, match, &error);
+ g_free(match);
+
+ /* Add the the recent devices */
+ list_known_printers(adapter);
+
+ doing_disco = TRUE;
+ message = dbus_message_new_method_call("org.bluez", adapter,
+ "org.bluez.Adapter",
+ "StartDiscovery");
+
+ if (!dbus_connection_send_with_reply(conn, message, NULL, -1)) {
+ dbus_message_unref(message);
+ dbus_connection_unref(conn);
+ g_free(adapter);
+ return FALSE;
+ }
+ dbus_message_unref(message);
+
+ loop = g_main_loop_new(NULL, TRUE);
+ g_main_loop_run(loop);
+
+ g_free(adapter);
+ dbus_connection_unref(conn);
+
+ return TRUE;
+}
+
+static gboolean print_ieee1284(const char *bdaddr)
+{
+ DBusMessage *message, *reply, *adapter_reply;
+ DBusMessageIter iter;
+ char *object_path = NULL;
+ char *adapter;
+ char *id;
+
+ adapter_reply = NULL;
+
+ conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
+ if (conn == NULL)
+ return FALSE;
+
+ message = dbus_message_new_method_call("org.bluez", "/",
+ "org.bluez.Manager",
+ "DefaultAdapter");
+
+ adapter_reply = dbus_connection_send_with_reply_and_block(conn,
+ message, -1, NULL);
+
+ dbus_message_unref(message);
+
+ if (dbus_message_get_args(adapter_reply, NULL,
+ DBUS_TYPE_OBJECT_PATH, &adapter,
+ DBUS_TYPE_INVALID) == FALSE)
+ return FALSE;
+
+ message = dbus_message_new_method_call("org.bluez", adapter,
+ "org.bluez.Adapter",
+ "FindDevice");
+ dbus_message_iter_init_append(message, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &bdaddr);
+
+ if (adapter_reply != NULL)
+ dbus_message_unref(adapter_reply);
+
+ reply = dbus_connection_send_with_reply_and_block(conn,
+ message, -1, NULL);
+
+ dbus_message_unref(message);
+
+ if (!reply) {
+ message = dbus_message_new_method_call("org.bluez", adapter,
+ "org.bluez.Adapter",
+ "CreateDevice");
+ dbus_message_iter_init_append(message, &iter);
+ dbus_message_iter_append_basic(&iter,
+ DBUS_TYPE_STRING, &bdaddr);
+
+ reply = dbus_connection_send_with_reply_and_block(conn,
+ message, -1, NULL);
+
+ dbus_message_unref(message);
+
+ if (!reply)
+ return FALSE;
+ }
+
+ if (dbus_message_get_args(reply, NULL,
+ DBUS_TYPE_OBJECT_PATH, &object_path,
+ DBUS_TYPE_INVALID) == FALSE)
+ return FALSE;
+
+ id = device_get_ieee1284_id(adapter, object_path);
+ if (id == NULL)
+ return FALSE;
+ printf("%s", id);
+ g_free(id);
+
+ return TRUE;
+}
+
+/*
+ * Usage: printer-uri job-id user title copies options [file]
+ *
+ */
+
+int main(int argc, char *argv[])
+{
+ sdp_session_t *sdp;
+ bdaddr_t bdaddr;
+ unsigned short ctrl_psm, data_psm;
+ uint8_t channel, b[6];
+ char *ptr, str[3], device[18], service[12];
+ const char *uri, *cups_class;
+ int i, err, fd, copies, proto;
+
+ /* Make sure status messages are not buffered */
+ setbuf(stderr, NULL);
+
+ /* Make sure output is not buffered */
+ setbuf(stdout, NULL);
+
+ /* Ignore SIGPIPE signals */
+#ifdef HAVE_SIGSET
+ sigset(SIGPIPE, SIG_IGN);
+#elif defined(HAVE_SIGACTION)
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &action, NULL);
+#else
+ signal(SIGPIPE, SIG_IGN);
+#endif /* HAVE_SIGSET */
+
+ if (argc == 1) {
+ if (list_printers() == TRUE)
+ return CUPS_BACKEND_OK;
+ else
+ return CUPS_BACKEND_FAILED;
+ } else if (argc == 3 && strcmp(argv[1], "--get-deviceid") == 0) {
+ if (bachk(argv[2]) < 0) {
+ fprintf(stderr, "Invalid Bluetooth address '%s'\n",
+ argv[2]);
+ return CUPS_BACKEND_FAILED;
+ }
+ if (print_ieee1284(argv[2]) == FALSE)
+ return CUPS_BACKEND_FAILED;
+ return CUPS_BACKEND_OK;
+ }
+
+ if (argc < 6 || argc > 7) {
+ fprintf(stderr, "Usage: bluetooth job-id user title copies"
+ " options [file]\n");
+ fprintf(stderr, " bluetooth --get-deviceid [bdaddr]\n");
+ return CUPS_BACKEND_FAILED;
+ }
+
+ if (argc == 6) {
+ fd = 0;
+ copies = 1;
+ } else {
+ if ((fd = open(argv[6], O_RDONLY)) < 0) {
+ perror("ERROR: Unable to open print file");
+ return CUPS_BACKEND_FAILED;
+ }
+ copies = atoi(argv[4]);
+ }
+
+ uri = getenv("DEVICE_URI");
+ if (!uri)
+ uri = argv[0];
+
+ if (strncasecmp(uri, "bluetooth://", 12)) {
+ fprintf(stderr, "ERROR: No device URI found\n");
+ return CUPS_BACKEND_FAILED;
+ }
+
+ ptr = argv[0] + 12;
+ for (i = 0; i < 6; i++) {
+ strncpy(str, ptr, 2);
+ b[i] = (uint8_t) strtol(str, NULL, 16);
+ ptr += 2;
+ }
+ sprintf(device, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
+ b[0], b[1], b[2], b[3], b[4], b[5]);
+
+ str2ba(device, &bdaddr);
+
+ ptr = strchr(ptr, '/');
+ if (ptr) {
+ strncpy(service, ptr + 1, 12);
+
+ if (!strncasecmp(ptr + 1, "spp", 3))
+ proto = 1;
+ else if (!strncasecmp(ptr + 1, "hcrp", 4))
+ proto = 2;
+ else
+ proto = 0;
+ } else {
+ strcpy(service, "auto");
+ proto = 0;
+ }
+
+ cups_class = getenv("CLASS");
+
+ fprintf(stderr,
+ "DEBUG: %s device %s service %s fd %d copies %d class %s\n",
+ argv[0], device, service, fd, copies,
+ cups_class ? cups_class : "(none)");
+
+ fputs("STATE: +connecting-to-device\n", stderr);
+
+service_search:
+ sdp = sdp_connect(BDADDR_ANY, &bdaddr, SDP_RETRY_IF_BUSY);
+ if (!sdp) {
+ fprintf(stderr, "ERROR: Can't open Bluetooth connection\n");
+ return CUPS_BACKEND_FAILED;
+ }
+
+ switch (proto) {
+ case 1:
+ err = sdp_search_spp(sdp, &channel);
+ break;
+ case 2:
+ err = sdp_search_hcrp(sdp, &ctrl_psm, &data_psm);
+ break;
+ default:
+ proto = 2;
+ err = sdp_search_hcrp(sdp, &ctrl_psm, &data_psm);
+ if (err) {
+ proto = 1;
+ err = sdp_search_spp(sdp, &channel);
+ }
+ break;
+ }
+
+ sdp_close(sdp);
+
+ if (err) {
+ if (cups_class) {
+ fputs("INFO: Unable to contact printer, queuing on "
+ "next printer in class...\n", stderr);
+ sleep(5);
+ return CUPS_BACKEND_FAILED;
+ }
+ sleep(20);
+ fprintf(stderr, "ERROR: Can't get service information\n");
+ goto service_search;
+ }
+
+connect:
+ switch (proto) {
+ case 1:
+ err = spp_print(BDADDR_ANY, &bdaddr, channel,
+ fd, copies, cups_class);
+ break;
+ case 2:
+ err = hcrp_print(BDADDR_ANY, &bdaddr, ctrl_psm, data_psm,
+ fd, copies, cups_class);
+ break;
+ default:
+ err = CUPS_BACKEND_FAILED;
+ fprintf(stderr, "ERROR: Unsupported protocol\n");
+ break;
+ }
+
+ if (err == CUPS_BACKEND_FAILED && cups_class) {
+ fputs("INFO: Unable to contact printer, queuing on "
+ "next printer in class...\n", stderr);
+ sleep(5);
+ return CUPS_BACKEND_FAILED;
+ } else if (err == CUPS_BACKEND_RETRY) {
+ sleep(20);
+ goto connect;
+ }
+
+ if (fd != 0)
+ close(fd);
+
+ if (!err)
+ fprintf(stderr, "INFO: Ready to print\n");
+
+ return err;
+}
diff --git a/cups/sdp.c b/cups/sdp.c
new file mode 100644
index 0000000..c7f17a4
--- /dev/null
+++ b/cups/sdp.c
@@ -0,0 +1,119 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2003-2010 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 <signal.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include "cups.h"
+
+int sdp_search_hcrp(sdp_session_t *sdp, unsigned short *ctrl_psm, unsigned short *data_psm)
+{
+ sdp_list_t *srch, *attrs, *rsp;
+ uuid_t svclass;
+ uint16_t attr1, attr2;
+ int err;
+
+ if (!sdp)
+ return -1;
+
+ sdp_uuid16_create(&svclass, HCR_PRINT_SVCLASS_ID);
+ srch = sdp_list_append(NULL, &svclass);
+
+ attr1 = SDP_ATTR_PROTO_DESC_LIST;
+ attrs = sdp_list_append(NULL, &attr1);
+ attr2 = SDP_ATTR_ADD_PROTO_DESC_LIST;
+ attrs = sdp_list_append(attrs, &attr2);
+
+ err = sdp_service_search_attr_req(sdp, srch, SDP_ATTR_REQ_INDIVIDUAL, attrs, &rsp);
+ if (err)
+ return -1;
+
+ for (; rsp; rsp = rsp->next) {
+ sdp_record_t *rec = (sdp_record_t *) rsp->data;
+ sdp_list_t *protos;
+
+ if (!sdp_get_access_protos(rec, &protos)) {
+ unsigned short psm = sdp_get_proto_port(protos, L2CAP_UUID);
+ if (psm > 0) {
+ *ctrl_psm = psm;
+ }
+ }
+
+ if (!sdp_get_add_access_protos(rec, &protos)) {
+ unsigned short psm = sdp_get_proto_port(protos, L2CAP_UUID);
+ if (psm > 0 && *ctrl_psm > 0) {
+ *data_psm = psm;
+ return 0;
+ }
+ }
+ }
+
+ return -1;
+}
+
+int sdp_search_spp(sdp_session_t *sdp, uint8_t *channel)
+{
+ sdp_list_t *srch, *attrs, *rsp;
+ uuid_t svclass;
+ uint16_t attr;
+ int err;
+
+ if (!sdp)
+ return -1;
+
+ sdp_uuid16_create(&svclass, SERIAL_PORT_SVCLASS_ID);
+ srch = sdp_list_append(NULL, &svclass);
+
+ attr = SDP_ATTR_PROTO_DESC_LIST;
+ attrs = sdp_list_append(NULL, &attr);
+
+ err = sdp_service_search_attr_req(sdp, srch, SDP_ATTR_REQ_INDIVIDUAL, attrs, &rsp);
+ if (err)
+ return -1;
+
+ for (; rsp; rsp = rsp->next) {
+ sdp_record_t *rec = (sdp_record_t *) rsp->data;
+ sdp_list_t *protos;
+
+ if (!sdp_get_access_protos(rec, &protos)) {
+ uint8_t ch = sdp_get_proto_port(protos, RFCOMM_UUID);
+ if (ch > 0) {
+ *channel = ch;
+ return 0;
+ }
+ }
+ }
+
+ return -1;
+}
diff --git a/cups/spp.c b/cups/spp.c
new file mode 100644
index 0000000..d906ed2
--- /dev/null
+++ b/cups/spp.c
@@ -0,0 +1,118 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2003-2010 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 <signal.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include "cups.h"
+
+int spp_print(bdaddr_t *src, bdaddr_t *dst, uint8_t channel, int fd, int copies, const char *cups_class)
+{
+ struct sockaddr_rc addr;
+ unsigned char buf[2048];
+ int i, sk, err, len;
+
+ if ((sk = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0) {
+ perror("ERROR: Can't create socket");
+ if (cups_class)
+ return CUPS_BACKEND_FAILED;
+ else
+ return CUPS_BACKEND_RETRY;
+ }
+
+ addr.rc_family = AF_BLUETOOTH;
+ bacpy(&addr.rc_bdaddr, src);
+ addr.rc_channel = 0;
+
+ if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("ERROR: Can't bind socket");
+ close(sk);
+ if (cups_class)
+ return CUPS_BACKEND_FAILED;
+ else
+ return CUPS_BACKEND_RETRY;
+ }
+
+ addr.rc_family = AF_BLUETOOTH;
+ bacpy(&addr.rc_bdaddr, dst);
+ addr.rc_channel = channel;
+
+ if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("ERROR: Can't connect to device");
+ close(sk);
+ if (cups_class)
+ return CUPS_BACKEND_FAILED;
+ else
+ return CUPS_BACKEND_RETRY;
+ }
+
+ fputs("STATE: -connecting-to-device\n", stderr);
+
+ /* Ignore SIGTERM signals if printing from stdin */
+ if (fd == 0) {
+#ifdef HAVE_SIGSET
+ sigset(SIGTERM, SIG_IGN);
+#elif defined(HAVE_SIGACTION)
+ memset(&action, 0, sizeof(action));
+ sigemptyset(&action.sa_mask);
+ action.sa_handler = SIG_IGN;
+ sigaction(SIGTERM, &action, NULL);
+#else
+ signal(SIGTERM, SIG_IGN);
+#endif /* HAVE_SIGSET */
+ }
+
+ for (i = 0; i < copies; i++) {
+
+ if (fd != 0) {
+ fprintf(stderr, "PAGE: 1 1\n");
+ lseek(fd, 0, SEEK_SET);
+ }
+
+ while ((len = read(fd, buf, sizeof(buf))) > 0) {
+ err = write(sk, buf, len);
+ if (err < 0) {
+ perror("ERROR: Error writing to device");
+ close(sk);
+ return CUPS_BACKEND_FAILED;
+ }
+ }
+
+ }
+
+ close(sk);
+
+ return CUPS_BACKEND_OK;
+}
diff --git a/depcomp b/depcomp
new file mode 100755
index 0000000..df8eea7
--- /dev/null
+++ b/depcomp
@@ -0,0 +1,630 @@
+#! /bin/sh
+# depcomp - compile a program generating dependencies as side-effects
+
+scriptversion=2009-04-28.21; # UTC
+
+# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2006, 2007, 2009 Free
+# Software Foundation, Inc.
+
+# 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, 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, see <http://www.gnu.org/licenses/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
+
+case $1 in
+ '')
+ echo "$0: No command. Try \`$0 --help' for more information." 1>&2
+ exit 1;
+ ;;
+ -h | --h*)
+ cat <<\EOF
+Usage: depcomp [--help] [--version] PROGRAM [ARGS]
+
+Run PROGRAMS ARGS to compile a file, generating dependencies
+as side-effects.
+
+Environment variables:
+ depmode Dependency tracking mode.
+ source Source file read by `PROGRAMS ARGS'.
+ object Object file output by `PROGRAMS ARGS'.
+ DEPDIR directory where to store dependencies.
+ depfile Dependency file to output.
+ tmpdepfile Temporary file to use when outputing dependencies.
+ libtool Whether libtool is used (yes/no).
+
+Report bugs to <bug-automake@gnu.org>.
+EOF
+ exit $?
+ ;;
+ -v | --v*)
+ echo "depcomp $scriptversion"
+ exit $?
+ ;;
+esac
+
+if test -z "$depmode" || test -z "$source" || test -z "$object"; then
+ echo "depcomp: Variables source, object and depmode must be set" 1>&2
+ exit 1
+fi
+
+# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
+depfile=${depfile-`echo "$object" |
+ sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
+tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
+
+rm -f "$tmpdepfile"
+
+# Some modes work just like other modes, but use different flags. We
+# parameterize here, but still list the modes in the big case below,
+# to make depend.m4 easier to write. Note that we *cannot* use a case
+# here, because this file can only contain one case statement.
+if test "$depmode" = hp; then
+ # HP compiler uses -M and no extra arg.
+ gccflag=-M
+ depmode=gcc
+fi
+
+if test "$depmode" = dashXmstdout; then
+ # This is just like dashmstdout with a different argument.
+ dashmflag=-xM
+ depmode=dashmstdout
+fi
+
+cygpath_u="cygpath -u -f -"
+if test "$depmode" = msvcmsys; then
+ # This is just like msvisualcpp but w/o cygpath translation.
+ # Just convert the backslash-escaped backslashes to single forward
+ # slashes to satisfy depend.m4
+ cygpath_u="sed s,\\\\\\\\,/,g"
+ depmode=msvisualcpp
+fi
+
+case "$depmode" in
+gcc3)
+## gcc 3 implements dependency tracking that does exactly what
+## we want. Yay! Note: for some reason libtool 1.4 doesn't like
+## it if -MD -MP comes after the -MF stuff. Hmm.
+## Unfortunately, FreeBSD c89 acceptance of flags depends upon
+## the command line argument order; so add the flags where they
+## appear in depend2.am. Note that the slowdown incurred here
+## affects only configure: in makefiles, %FASTDEP% shortcuts this.
+ for arg
+ do
+ case $arg in
+ -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;;
+ *) set fnord "$@" "$arg" ;;
+ esac
+ shift # fnord
+ shift # $arg
+ done
+ "$@"
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ mv "$tmpdepfile" "$depfile"
+ ;;
+
+gcc)
+## There are various ways to get dependency output from gcc. Here's
+## why we pick this rather obscure method:
+## - Don't want to use -MD because we'd like the dependencies to end
+## up in a subdir. Having to rename by hand is ugly.
+## (We might end up doing this anyway to support other compilers.)
+## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
+## -MM, not -M (despite what the docs say).
+## - Using -M directly means running the compiler twice (even worse
+## than renaming).
+ if test -z "$gccflag"; then
+ gccflag=-MD,
+ fi
+ "$@" -Wp,"$gccflag$tmpdepfile"
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ rm -f "$depfile"
+ echo "$object : \\" > "$depfile"
+ alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
+## The second -e expression handles DOS-style file names with drive letters.
+ sed -e 's/^[^:]*: / /' \
+ -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
+## This next piece of magic avoids the `deleted header file' problem.
+## The problem is that when a header file which appears in a .P file
+## is deleted, the dependency causes make to die (because there is
+## typically no way to rebuild the header). We avoid this by adding
+## dummy dependencies for each header file. Too bad gcc doesn't do
+## this for us directly.
+ tr ' ' '
+' < "$tmpdepfile" |
+## Some versions of gcc put a space before the `:'. On the theory
+## that the space means something, we add a space to the output as
+## well.
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly. Breaking it into two sed invocations is a workaround.
+ sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+hp)
+ # This case exists only to let depend.m4 do its work. It works by
+ # looking at the text of this script. This case will never be run,
+ # since it is checked for above.
+ exit 1
+ ;;
+
+sgi)
+ if test "$libtool" = yes; then
+ "$@" "-Wp,-MDupdate,$tmpdepfile"
+ else
+ "$@" -MDupdate "$tmpdepfile"
+ fi
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ rm -f "$depfile"
+
+ if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files
+ echo "$object : \\" > "$depfile"
+
+ # Clip off the initial element (the dependent). Don't try to be
+ # clever and replace this with sed code, as IRIX sed won't handle
+ # lines with more than a fixed number of characters (4096 in
+ # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines;
+ # the IRIX cc adds comments like `#:fec' to the end of the
+ # dependency line.
+ tr ' ' '
+' < "$tmpdepfile" \
+ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \
+ tr '
+' ' ' >> "$depfile"
+ echo >> "$depfile"
+
+ # The second pass generates a dummy entry for each header file.
+ tr ' ' '
+' < "$tmpdepfile" \
+ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
+ >> "$depfile"
+ else
+ # The sourcefile does not contain any dependencies, so just
+ # store a dummy comment line, to avoid errors with the Makefile
+ # "include basename.Plo" scheme.
+ echo "#dummy" > "$depfile"
+ fi
+ rm -f "$tmpdepfile"
+ ;;
+
+aix)
+ # The C for AIX Compiler uses -M and outputs the dependencies
+ # in a .u file. In older versions, this file always lives in the
+ # current directory. Also, the AIX compiler puts `$object:' at the
+ # start of each line; $object doesn't have directory information.
+ # Version 6 uses the directory in both cases.
+ dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
+ test "x$dir" = "x$object" && dir=
+ base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
+ if test "$libtool" = yes; then
+ tmpdepfile1=$dir$base.u
+ tmpdepfile2=$base.u
+ tmpdepfile3=$dir.libs/$base.u
+ "$@" -Wc,-M
+ else
+ tmpdepfile1=$dir$base.u
+ tmpdepfile2=$dir$base.u
+ tmpdepfile3=$dir$base.u
+ "$@" -M
+ fi
+ stat=$?
+
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+ exit $stat
+ fi
+
+ for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+ do
+ test -f "$tmpdepfile" && break
+ done
+ if test -f "$tmpdepfile"; then
+ # Each line is of the form `foo.o: dependent.h'.
+ # Do two passes, one to just change these to
+ # `$object: dependent.h' and one to simply `dependent.h:'.
+ sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
+ # That's a tab and a space in the [].
+ sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
+ else
+ # The sourcefile does not contain any dependencies, so just
+ # store a dummy comment line, to avoid errors with the Makefile
+ # "include basename.Plo" scheme.
+ echo "#dummy" > "$depfile"
+ fi
+ rm -f "$tmpdepfile"
+ ;;
+
+icc)
+ # Intel's C compiler understands `-MD -MF file'. However on
+ # icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c
+ # ICC 7.0 will fill foo.d with something like
+ # foo.o: sub/foo.c
+ # foo.o: sub/foo.h
+ # which is wrong. We want:
+ # sub/foo.o: sub/foo.c
+ # sub/foo.o: sub/foo.h
+ # sub/foo.c:
+ # sub/foo.h:
+ # ICC 7.1 will output
+ # foo.o: sub/foo.c sub/foo.h
+ # and will wrap long lines using \ :
+ # foo.o: sub/foo.c ... \
+ # sub/foo.h ... \
+ # ...
+
+ "$@" -MD -MF "$tmpdepfile"
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ rm -f "$depfile"
+ # Each line is of the form `foo.o: dependent.h',
+ # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'.
+ # Do two passes, one to just change these to
+ # `$object: dependent.h' and one to simply `dependent.h:'.
+ sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
+ # Some versions of the HPUX 10.20 sed can't process this invocation
+ # correctly. Breaking it into two sed invocations is a workaround.
+ sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" |
+ sed -e 's/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+hp2)
+ # The "hp" stanza above does not work with aCC (C++) and HP's ia64
+ # compilers, which have integrated preprocessors. The correct option
+ # to use with these is +Maked; it writes dependencies to a file named
+ # 'foo.d', which lands next to the object file, wherever that
+ # happens to be.
+ # Much of this is similar to the tru64 case; see comments there.
+ dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
+ test "x$dir" = "x$object" && dir=
+ base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
+ if test "$libtool" = yes; then
+ tmpdepfile1=$dir$base.d
+ tmpdepfile2=$dir.libs/$base.d
+ "$@" -Wc,+Maked
+ else
+ tmpdepfile1=$dir$base.d
+ tmpdepfile2=$dir$base.d
+ "$@" +Maked
+ fi
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile1" "$tmpdepfile2"
+ exit $stat
+ fi
+
+ for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2"
+ do
+ test -f "$tmpdepfile" && break
+ done
+ if test -f "$tmpdepfile"; then
+ sed -e "s,^.*\.[a-z]*:,$object:," "$tmpdepfile" > "$depfile"
+ # Add `dependent.h:' lines.
+ sed -ne '2,${
+ s/^ *//
+ s/ \\*$//
+ s/$/:/
+ p
+ }' "$tmpdepfile" >> "$depfile"
+ else
+ echo "#dummy" > "$depfile"
+ fi
+ rm -f "$tmpdepfile" "$tmpdepfile2"
+ ;;
+
+tru64)
+ # The Tru64 compiler uses -MD to generate dependencies as a side
+ # effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'.
+ # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
+ # dependencies in `foo.d' instead, so we check for that too.
+ # Subdirectories are respected.
+ dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
+ test "x$dir" = "x$object" && dir=
+ base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
+
+ if test "$libtool" = yes; then
+ # With Tru64 cc, shared objects can also be used to make a
+ # static library. This mechanism is used in libtool 1.4 series to
+ # handle both shared and static libraries in a single compilation.
+ # With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d.
+ #
+ # With libtool 1.5 this exception was removed, and libtool now
+ # generates 2 separate objects for the 2 libraries. These two
+ # compilations output dependencies in $dir.libs/$base.o.d and
+ # in $dir$base.o.d. We have to check for both files, because
+ # one of the two compilations can be disabled. We should prefer
+ # $dir$base.o.d over $dir.libs/$base.o.d because the latter is
+ # automatically cleaned when .libs/ is deleted, while ignoring
+ # the former would cause a distcleancheck panic.
+ tmpdepfile1=$dir.libs/$base.lo.d # libtool 1.4
+ tmpdepfile2=$dir$base.o.d # libtool 1.5
+ tmpdepfile3=$dir.libs/$base.o.d # libtool 1.5
+ tmpdepfile4=$dir.libs/$base.d # Compaq CCC V6.2-504
+ "$@" -Wc,-MD
+ else
+ tmpdepfile1=$dir$base.o.d
+ tmpdepfile2=$dir$base.d
+ tmpdepfile3=$dir$base.d
+ tmpdepfile4=$dir$base.d
+ "$@" -MD
+ fi
+
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
+ exit $stat
+ fi
+
+ for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
+ do
+ test -f "$tmpdepfile" && break
+ done
+ if test -f "$tmpdepfile"; then
+ sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
+ # That's a tab and a space in the [].
+ sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
+ else
+ echo "#dummy" > "$depfile"
+ fi
+ rm -f "$tmpdepfile"
+ ;;
+
+#nosideeffect)
+ # This comment above is used by automake to tell side-effect
+ # dependency tracking mechanisms from slower ones.
+
+dashmstdout)
+ # Important note: in order to support this mode, a compiler *must*
+ # always write the preprocessed file to stdout, regardless of -o.
+ "$@" || exit $?
+
+ # Remove the call to Libtool.
+ if test "$libtool" = yes; then
+ while test "X$1" != 'X--mode=compile'; do
+ shift
+ done
+ shift
+ fi
+
+ # Remove `-o $object'.
+ IFS=" "
+ for arg
+ do
+ case $arg in
+ -o)
+ shift
+ ;;
+ $object)
+ shift
+ ;;
+ *)
+ set fnord "$@" "$arg"
+ shift # fnord
+ shift # $arg
+ ;;
+ esac
+ done
+
+ test -z "$dashmflag" && dashmflag=-M
+ # Require at least two characters before searching for `:'
+ # in the target name. This is to cope with DOS-style filenames:
+ # a dependency such as `c:/foo/bar' could be seen as target `c' otherwise.
+ "$@" $dashmflag |
+ sed 's:^[ ]*[^: ][^:][^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile"
+ rm -f "$depfile"
+ cat < "$tmpdepfile" > "$depfile"
+ tr ' ' '
+' < "$tmpdepfile" | \
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly. Breaking it into two sed invocations is a workaround.
+ sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+dashXmstdout)
+ # This case only exists to satisfy depend.m4. It is never actually
+ # run, as this mode is specially recognized in the preamble.
+ exit 1
+ ;;
+
+makedepend)
+ "$@" || exit $?
+ # Remove any Libtool call
+ if test "$libtool" = yes; then
+ while test "X$1" != 'X--mode=compile'; do
+ shift
+ done
+ shift
+ fi
+ # X makedepend
+ shift
+ cleared=no eat=no
+ for arg
+ do
+ case $cleared in
+ no)
+ set ""; shift
+ cleared=yes ;;
+ esac
+ if test $eat = yes; then
+ eat=no
+ continue
+ fi
+ case "$arg" in
+ -D*|-I*)
+ set fnord "$@" "$arg"; shift ;;
+ # Strip any option that makedepend may not understand. Remove
+ # the object too, otherwise makedepend will parse it as a source file.
+ -arch)
+ eat=yes ;;
+ -*|$object)
+ ;;
+ *)
+ set fnord "$@" "$arg"; shift ;;
+ esac
+ done
+ obj_suffix=`echo "$object" | sed 's/^.*\././'`
+ touch "$tmpdepfile"
+ ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
+ rm -f "$depfile"
+ cat < "$tmpdepfile" > "$depfile"
+ sed '1,2d' "$tmpdepfile" | tr ' ' '
+' | \
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly. Breaking it into two sed invocations is a workaround.
+ sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile" "$tmpdepfile".bak
+ ;;
+
+cpp)
+ # Important note: in order to support this mode, a compiler *must*
+ # always write the preprocessed file to stdout.
+ "$@" || exit $?
+
+ # Remove the call to Libtool.
+ if test "$libtool" = yes; then
+ while test "X$1" != 'X--mode=compile'; do
+ shift
+ done
+ shift
+ fi
+
+ # Remove `-o $object'.
+ IFS=" "
+ for arg
+ do
+ case $arg in
+ -o)
+ shift
+ ;;
+ $object)
+ shift
+ ;;
+ *)
+ set fnord "$@" "$arg"
+ shift # fnord
+ shift # $arg
+ ;;
+ esac
+ done
+
+ "$@" -E |
+ sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
+ -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' |
+ sed '$ s: \\$::' > "$tmpdepfile"
+ rm -f "$depfile"
+ echo "$object : \\" > "$depfile"
+ cat < "$tmpdepfile" >> "$depfile"
+ sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+msvisualcpp)
+ # Important note: in order to support this mode, a compiler *must*
+ # always write the preprocessed file to stdout.
+ "$@" || exit $?
+
+ # Remove the call to Libtool.
+ if test "$libtool" = yes; then
+ while test "X$1" != 'X--mode=compile'; do
+ shift
+ done
+ shift
+ fi
+
+ IFS=" "
+ for arg
+ do
+ case "$arg" in
+ -o)
+ shift
+ ;;
+ $object)
+ shift
+ ;;
+ "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
+ set fnord "$@"
+ shift
+ shift
+ ;;
+ *)
+ set fnord "$@" "$arg"
+ shift
+ shift
+ ;;
+ esac
+ done
+ "$@" -E 2>/dev/null |
+ sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile"
+ rm -f "$depfile"
+ echo "$object : \\" > "$depfile"
+ sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile"
+ echo " " >> "$depfile"
+ sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+msvcmsys)
+ # This case exists only to let depend.m4 do its work. It works by
+ # looking at the text of this script. This case will never be run,
+ # since it is checked for above.
+ exit 1
+ ;;
+
+none)
+ exec "$@"
+ ;;
+
+*)
+ echo "Unknown depmode $depmode" 1>&2
+ exit 1
+ ;;
+esac
+
+exit 0
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/doc/adapter-api.txt b/doc/adapter-api.txt
new file mode 100644
index 0000000..f34d58f
--- /dev/null
+++ b/doc/adapter-api.txt
@@ -0,0 +1,286 @@
+BlueZ D-Bus Adapter API description
+***********************************
+
+Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+Copyright (C) 2005-2006 Johan Hedberg <johan.hedberg@nokia.com>
+Copyright (C) 2005-2006 Claudio Takahasi <claudio.takahasi@indt.org.br>
+Copyright (C) 2006-2007 Luiz von Dentz <luiz.dentz@indt.org.br>
+
+
+Adapter hierarchy
+=================
+
+Service org.bluez
+Interface org.bluez.Adapter
+Object path [variable prefix]/{hci0,hci1,...}
+
+Methods dict GetProperties()
+
+ Returns all properties for the adapter. See the
+ properties section for available properties.
+
+ Possible Errors: org.bluez.Error.NotReady
+
+ void SetProperty(string name, variant value)
+
+ Changes the value of the specified property. Only
+ properties that are listed a read-write are changeable.
+ On success this will emit a PropertyChanged signal.
+
+ Possible Errors: org.bluez.Error.InvalidArguments
+
+ void RequestSession()
+
+ This method requests a client session that provides
+ operational Bluetooth. A possible mode change must be
+ confirmed by the user via the agent.
+
+ Clients may request multiple sessions. All sessions
+ are released when adapter's mode is changed to off
+ state.
+
+ Possible Errors: org.bluez.Error.Rejected
+
+ void ReleaseSession()
+
+ Release a previously requested session. It sets
+ adapter to the mode in use on the moment of session
+ request.
+
+ SetProperty method call changes adapter's mode
+ persistently, such that session release will not
+ modify it.
+
+ Possible Errors: org.bluez.Error.DoesNotExist
+
+ void StartDiscovery()
+
+ This method starts the device discovery session. This
+ includes an inquiry procedure and remote device name
+ resolving. Use StopDiscovery to release the sessions
+ acquired.
+
+ This process will start emitting DeviceFound and
+ PropertyChanged "Discovering" signals.
+
+ Possible errors: org.bluez.Error.NotReady
+ org.bluez.Error.Failed
+
+ void StopDiscovery()
+
+ This method will cancel any previous StartDiscovery
+ transaction.
+
+ Note that a discovery procedure is shared between all
+ discovery sessions thus calling StopDiscovery will only
+ release a single session.
+
+ Possible errors: org.bluez.Error.NotReady
+ org.bluez.Error.Failed
+ org.bluez.Error.NotAuthorized
+
+ object FindDevice(string address)
+
+ Returns the object path of device for given address.
+ The device object needs to be first created via
+ CreateDevice or CreatePairedDevice.
+
+ Possible Errors: org.bluez.Error.DoesNotExist
+ org.bluez.Error.InvalidArguments
+
+ array{object} ListDevices() {deprecated}
+
+ Returns list of device object paths.
+ This method is deprecated, instead use the Devices
+ Property to get the list of devices object paths.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.Failed
+ org.bluez.Error.OutOfMemory
+
+ object CreateDevice(string address)
+
+ Creates a new object path for a remote device. This
+ method will connect to the remote device and retrieve
+ all SDP records.
+
+ If the object for the remote device already exists
+ this method will fail.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.Failed
+
+ object CreatePairedDevice(string address, object agent,
+ string capability)
+
+ Creates a new object path for a remote device. This
+ method will connect to the remote device and retrieve
+ all SDP records and then initiate the pairing.
+
+ If previously CreateDevice was used successfully,
+ this method will only initiate the pairing.
+
+ Compared to CreateDevice this method will fail if
+ the pairing already exists, but not if the object
+ path already has been created. This allows applications
+ to use CreateDevice first and the if needed use
+ CreatePairedDevice to initiate pairing.
+
+ The agent object path is assumed to reside within the
+ process (D-Bus connection instance) that calls this
+ method. No separate registration procedure is needed
+ for it and it gets automatically released once the
+ pairing operation is complete.
+
+ The capability parameter is the same as for the
+ RegisterAgent method.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.Failed
+
+ void CancelDeviceCreation(string address)
+
+ Aborts either a CreateDevice call or a
+ CreatePairedDevice call.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.NotInProgress
+
+ void RemoveDevice(object device)
+
+ This removes the remote device object at the given
+ path. It will remove also the pairing information.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.Failed
+
+ void RegisterAgent(object agent, string capability)
+
+ This registers the adapter wide agent.
+
+ The object path defines the path of the agent
+ that will be called when user input is needed.
+
+ If an application disconnects from the bus all
+ of its registered agents will be removed.
+
+ The capability parameter can have the values
+ "DisplayOnly", "DisplayYesNo", "KeyboardOnly" and
+ "NoInputNoOutput" which reflects the input and output
+ capabilities of the agent. If an empty string is
+ used it will fallback to "DisplayYesNo".
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.AlreadyExists
+
+ void UnregisterAgent(object agent)
+
+ This unregisters the agent that has been previously
+ registered. The object path parameter must match the
+ same value that has been used on registration.
+
+ Possible errors: org.bluez.Error.DoesNotExist
+
+Signals PropertyChanged(string name, variant value)
+
+ This signal indicates a changed value of the given
+ property.
+
+ DeviceFound(string address, dict values)
+
+ This signal will be sent every time an inquiry result
+ has been found by the service daemon. In general they
+ only appear during a device discovery.
+
+ The dictionary can contain basically the same values
+ that are returned by the GetProperties method
+ from the org.bluez.Device interface. In addition there
+ can be values for the RSSI, the TX power level and
+ Broadcaster role.
+
+ DeviceDisappeared(string address)
+
+ This signal will be sent when an inquiry session for
+ a periodic discovery finishes and previously found
+ devices are no longer in range or visible.
+
+ DeviceCreated(object device)
+
+ Parameter is object path of created device.
+
+ DeviceRemoved(object device)
+
+ Parameter is object path of removed device.
+
+Properties string Address [readonly]
+
+ The Bluetooth device address.
+
+ string Name [readwrite]
+
+ The Bluetooth friendly name. This value can be
+ changed and a PropertyChanged signal will be emitted.
+
+ uint32 Class [readonly]
+
+ The Bluetooth class of device.
+
+ boolean Powered [readwrite]
+
+ Switch an adapter on or off. This will also set the
+ appropiate connectable state.
+
+ boolean Discoverable [readwrite]
+
+ Switch an adapter to discoverable or non-discoverable
+ to either make it visible or hide it. This is a global
+ setting and should only be used by the settings
+ application.
+
+ If the DiscoverableTimeout is set to a non-zero
+ value then the system will set this value back to
+ false after the timer expired.
+
+ In case the adapter is switched off, setting this
+ value will fail.
+
+ When changing the Powered property the new state of
+ this property will be updated via a PropertyChanged
+ signal.
+
+ boolean Pairable [readwrite]
+
+ Switch an adapter to pairable or non-pairable. This is
+ a global setting and should only be used by the
+ settings application.
+
+ Note that this property only affects incoming pairing
+ requests.
+
+ uint32 PaireableTimeout [readwrite]
+
+ The pairable timeout in seconds. A value of zero
+ means that the timeout is disabled and it will stay in
+ pareable mode forever.
+
+ uint32 DiscoverableTimeout [readwrite]
+
+ The discoverable timeout in seconds. A value of zero
+ means that the timeout is disabled and it will stay in
+ discoverable/limited mode forever.
+
+ The default value for the discoverable timeout should
+ be 180 seconds (3 minutes).
+
+ boolean Discovering [readonly]
+
+ Indicates that a device discovery procedure is active.
+
+ array{object} Devices [readonly]
+
+ List of device object paths.
+
+ array{string} UUIDs [readonly]
+
+ List of 128-bit UUIDs that represents the available
+ local services.
diff --git a/doc/agent-api.txt b/doc/agent-api.txt
new file mode 100644
index 0000000..d8d35c0
--- /dev/null
+++ b/doc/agent-api.txt
@@ -0,0 +1,91 @@
+BlueZ D-Bus Agent API description
+**********************************
+
+Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+Copyright (C) 2005-2006 Johan Hedberg <johan.hedberg@nokia.com>
+
+
+Agent hierarchy
+===============
+
+Service unique name
+Interface org.bluez.Agent
+Object path freely definable
+
+Methods void Release()
+
+ This method gets called when the service daemon
+ unregisters the agent. An agent can use it to do
+ cleanup tasks. There is no need to unregister the
+ agent, because when this method gets called it has
+ already been unregistered.
+
+ string RequestPinCode(object device)
+
+ This method gets called when the service daemon
+ needs to get the passkey for an authentication.
+
+ The return value should be a string of 1-16 characters
+ length. The string can be alphanumeric.
+
+ Possible errors: org.bluez.Error.Rejected
+ org.bluez.Error.Canceled
+
+ uint32 RequestPasskey(object device)
+
+ This method gets called when the service daemon
+ needs to get the passkey for an authentication.
+
+ The return value should be a numeric value
+ between 0-999999.
+
+ Possible errors: org.bluez.Error.Rejected
+ org.bluez.Error.Canceled
+
+ void DisplayPasskey(object device, uint32 passkey, uint8 entered)
+
+ This method gets called when the service daemon
+ needs to display a passkey for an authentication.
+
+ The entered parameter indicates the number of already
+ typed keys on the remote side.
+
+ An empty reply should be returned. When the passkey
+ needs no longer to be displayed, the Cancel method
+ of the agent will be called.
+
+ During the pairing process this method might be
+ called multiple times to update the entered value.
+
+ void RequestConfirmation(object device, uint32 passkey)
+
+ This method gets called when the service daemon
+ needs to confirm a passkey for an authentication.
+
+ To confirm the value it should return an empty reply
+ or an error in case the passkey is invalid.
+
+ Possible errors: org.bluez.Error.Rejected
+ org.bluez.Error.Canceled
+
+ void Authorize(object device, string uuid)
+
+ This method gets called when the service daemon
+ needs to authorize a connection/service request.
+
+ Possible errors: org.bluez.Error.Rejected
+ org.bluez.Error.Canceled
+
+ void ConfirmModeChange(string mode)
+
+ This method gets called if a mode change is requested
+ that needs to be confirmed by the user. An example
+ would be leaving flight mode.
+
+ Possible errors: org.bluez.Error.Rejected
+ org.bluez.Error.Canceled
+
+ void Cancel()
+
+ This method gets called to indicate that the agent
+ request failed before a reply was returned.
diff --git a/doc/assigned-numbers.txt b/doc/assigned-numbers.txt
new file mode 100644
index 0000000..cda934c
--- /dev/null
+++ b/doc/assigned-numbers.txt
@@ -0,0 +1,23 @@
+RFCOMM Channels
+===============
+
+Since there are a limited amount of possible RFCOMM channels (1-31)
+they've been pre-allocated for currently known profiles in order to
+avoid conflicts.
+
+Profile Channel
+-----------------------
+DUN 1
+HFP HF 7
+OPP 9
+FTP 10
+BIP 11
+HSP AG 12
+HFP AG 13
+SYNCH (IrMC) 14
+PBAP 15
+MAP 16
+SyncEvolution 19
+PC/Ovi Suite 24
+SyncML Client 25
+SyncML Server 26
diff --git a/doc/attribute-api.txt b/doc/attribute-api.txt
new file mode 100644
index 0000000..f2778f3
--- /dev/null
+++ b/doc/attribute-api.txt
@@ -0,0 +1,164 @@
+BlueZ D-Bus Attribute API description
+*************************************
+
+Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+
+Service details
+---------------
+
+One service object path for every remote SDP record or service in the
+attribute database. One service object path for every local SDP record
+or service from attribute database.
+
+Local services are children of the adapter object path. Remote services
+are children of the remote device object path. This doesn't solve the
+problem where local atttributes can have different instances based on
+the remote device.
+
+In general the idea is to also represent SDP records as services so that
+new style application can just use the service interfaces to retrieve the
+needed information. That way the usage of SDP and GATT would be mostly
+fully transparent and a differentiation becomes unimportant in the future.
+
+A service consists of some generic service information and a set of
+characteristics. All characteristic are presented as object path as well.
+
+
+Local Service hierarchy
+=======================
+
+Service org.bluez
+Interface org.bluez.Service
+ org.bluez.Characteristic
+Object path [prefix]/{hci0}/{service0, service1, ...}
+
+Methods
+
+Properties
+
+
+Device Service hierarchy
+========================
+
+Service org.bluez
+Interface org.bluez.Characteristic
+Object path [prefix]/{hci0}/{device0}/{service0, service1, ...}
+ [prefix]/{hci0}/{device1}/{service0, service1, ...}
+
+Methods dict GetProperties()
+
+ Returns all properties for the interface. See the
+ Properties section for the available properties.
+
+ array{object} DiscoverCharacteristics()
+
+ Discover all characteristics that belongs in this service.
+ When it returns all the characteristics paths will be
+ already registered. It will return the characteristics paths
+ as soon as they are discovered. After that it will try to
+ read all values.
+
+ RegisterCharacteristicsWatcher(object agent)
+
+ Register a watcher to monitor characteristic changes.
+
+ A watcher will be registered for this service and will
+ notify about any changed characteristics in the service.
+ This also notifies about any included characteristics.
+
+ UnregisterCharacteristicsWatcher(object agent)
+
+ Unregister a watcher.
+
+Properties string Name (mandatory) [readonly]
+
+ General name of service
+
+ string Description (optional) [readonly]
+
+ Description of service
+
+ string UUID (mandatory) [readonly]
+
+ UUID of service. Service class value for SDP and GATT
+ UUID for attribute based services.
+
+ array{object} Characteristics [readonly]
+
+ This list contains the characteristics owned by this
+ specific service and other characteristics from service
+ includes. That way no complicated service includes array
+ is needed.
+
+
+Device Characteristic hierarchy
+===============================
+
+Service org.bluez
+Interface org.bluez.Characteristic
+Object path [prefix]/{hci0}/{device0}/{service0}/{characteristic0,...}
+ [prefix]/{hci0}/{device0}/{service1}/{characteristic0,...}
+
+Methods dict GetProperties()
+
+ Returns all properties for the characteristic. See the
+ properties section for available properties.
+
+ void SetProperty(string name, variant value)
+
+ Changes the value of the specified property. Only
+ read-write properties can be changed. On success
+ this will emit a PropertyChanged signal.
+
+ Possible Errors: org.bluez.Error.InvalidArguments
+
+Properties string UUID [readonly]
+
+ UUID128 of this characteristic.
+
+ string Name [readonly]
+
+ Optional field containing a friendly name for the
+ Characteristic UUID.
+
+ string Description [readonly]
+
+ Textual optional characteristic descriptor describing
+ the Characteristic Value.
+
+ struct Format [readonly]
+
+ Optional Characteristic descriptor which defines the
+ format of the Characteristic Value. For numeric
+ values, the actual value can be value * 10^Exponent.
+ NameSpace and Description are defined on the Assigned
+ Number Specification.
+
+ uint8 | Format: format of the value
+ uint8 | Exponent: Field to determine how the value is
+ | further formatted.
+ uint16 | Unit: unit of the characteristic
+ uint8 | NameSpace: Name space of description.
+ uint16 | Description: Description of the characteristic defined
+ | in a high layer profile.
+
+ array{byte} Value [readwrite]
+
+ Raw value of the Characteristic Value attribute.
+
+ string Representation (of the binary Value) [readonly]
+
+ Friendly representation of the Characteristic Value
+ based on the format attribute.
+
+
+Characteristic Watcher hierarchy
+===============================
+
+Service unique name
+Interface org.bluez.Watcher
+Object path freely definable
+
+Methods void ValueChanged(object characteristic, array{byte})
+
+ New raw value of the Characteristic Value attribute.
diff --git a/doc/audio-api.txt b/doc/audio-api.txt
new file mode 100644
index 0000000..e7991f3
--- /dev/null
+++ b/doc/audio-api.txt
@@ -0,0 +1,458 @@
+BlueZ D-Bus Audio API description
+*********************************
+
+Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+Copyright (C) 2005-2007 Johan Hedberg <johan.hedberg@nokia.com>
+Copyright (C) 2005-2006 Brad Midgley <bmidgley@xmission.com>
+
+Audio hierarchy
+===============
+
+Service org.bluez
+Interface org.bluez.Audio
+Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+This is a generic audio interface that abstracts the different audio profiles.
+
+Methods void Connect()
+
+ Connect all supported audio profiles on the device.
+
+ void Disconnect()
+
+ Disconnect all audio profiles on the device
+
+ dict GetProperties()
+
+ Returns all properties for the interface. See the
+ properties section for available properties.
+
+Signals void PropertyChanged(string name, variant value)
+
+ This signal indicates a changed value of the given
+ property.
+
+Properties string State
+
+ Possible values: "disconnected", "connecting",
+ "connected"
+
+ "disconnected" -> "connecting"
+ Either an incoming or outgoing connection
+ attempt ongoing.
+
+ "connecting" -> "disconnected"
+ Connection attempt failed
+
+ "connecting" -> "connected"
+ Successfully connected
+
+ "connected" -> "disconnected"
+ Disconnected from the remote device
+
+Headset hierarchy
+=================
+
+Service org.bluez
+Interface org.bluez.Headset
+Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Methods void Connect()
+
+ Connect to the HSP/HFP service on the remote device.
+
+ void Disconnect()
+
+ Disconnect from the HSP/HFP service on the remote
+ device.
+
+ boolean IsConnected() {deprecated}
+
+ Returns TRUE if there is a active connection to the
+ HSP/HFP connection on the remote device.
+
+ void IndicateCall()
+
+ Indicate an incoming call on the headset
+ connected to the stream. Will continue to
+ ring the headset about every 3 seconds.
+
+ void CancelCall()
+
+ Cancel the incoming call indication.
+
+ void Play()
+
+ Open the audio connection to the headset.
+
+ void Stop()
+
+ Close the audio connection.
+
+ boolean IsPlaying() {deprecated}
+
+ Returns true if an audio connection to the headset
+ is active.
+
+ uint16 GetSpeakerGain() {deprecated}
+
+ Returns the current speaker gain if available,
+ otherwise returns the error NotAvailable.
+
+ uint16 GetMicrophoneGain() {deprecated}
+
+ Returns the current microphone gain if available,
+ otherwise returns the error NotAvailable.
+
+ void SetSpeakerGain(uint16 gain) {deprecated}
+
+ Changes the current speaker gain if possible.
+
+ void SetMicrophoneGain(uint16 gain) {deprecated}
+
+ Changes the current speaker gain if possible.
+
+ dict GetProperties()
+
+ Returns all properties for the interface. See the
+ properties section for available properties.
+
+ Possible Errors: org.bluez.Error.InvalidArguments
+
+ void SetProperty(string name, variant value)
+
+ Changes the value of the specified property. Only
+ properties that are listed a read-write are changeable.
+ On success this will emit a PropertyChanged signal.
+
+ Possible Errors: org.bluez.Error.DoesNotExist
+ org.bluez.Error.InvalidArguments
+
+Signals void AnswerRequested()
+
+ Sent when the answer button is pressed on the headset
+
+ void Connected() {deprecated}
+
+ Sent when the device has been connected to.
+
+ void Disconnected() {deprecated}
+
+ Sent when the device has been disconnected from.
+
+ void Stopped() {deprecated}
+
+ Sent when the audio connection is closed
+
+ void Playing() {deprecated}
+
+ Sent when the audio connection is opened
+
+ void SpeakerGainChanged(uint16 gain) {deprecated}
+
+ The speaker gain changed.
+
+ void MicrophoneGainChanged(uint16 gain) {deprecated}
+
+ The microphone gain changed.
+
+ PropertyChanged(string name, variant value)
+
+ This signal indicates a changed value of the given
+ property.
+
+properties string State [readonly]
+
+ Possible values: "disconnected", "connecting",
+ "connected", "playing"
+
+ "disconnected" -> "connecting"
+ Either an incoming or outgoing connection
+ attempt ongoing.
+
+ "connecting" -> "disconnected"
+ Connection attempt failed
+
+ "connecting" -> "connected"
+ Successfully connected
+
+ "connected" -> "playing"
+ SCO audio connection successfully opened
+
+ "playing" -> "connected"
+ SCO audio connection closed
+
+ "connected" -> "disconnected"
+ "playing" -> "disconnected"
+ Disconnected from the remote device
+
+ boolean Connected [readonly]
+
+ Indicates if there is a active connection to the
+ HSP/HFP connection on the remote device.
+
+ boolean Playing [readonly]
+
+ Indicates if an audio connection to the headset
+ is active.
+
+ uint16 SpeakerGain [readwrite]
+
+ The speaker gain when available.
+
+ uint16 MicrophoneGain [readwrite]
+
+ The speaker gain when available.
+
+
+AudioSink hierarchy
+===================
+
+Service org.bluez
+Interface org.bluez.AudioSink
+Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Methods void Connect()
+
+ Connect and setup a stream to a A2DP sink on the
+ remote device.
+
+ void Disconnect()
+
+ Disconnect from the remote device.
+
+ boolean IsConnected() {deprecated}
+
+ Returns TRUE if a stream is setup to a A2DP sink on
+ the remote device.
+
+ dict GetProperties()
+
+ Returns all properties for the interface. See the
+ properties section for available properties.
+
+ Possible Errors: org.bluez.Error.InvalidArguments
+
+Signals void Connected() {deprecated}
+
+ Sent when a successful connection has been made to the
+ remote A2DP Sink
+
+ void Disconnected() {deprecated}
+
+ Sent when the device has been disconnected from.
+
+ void Playing() {deprecated}
+
+ Sent when a stream with remote device is started.
+
+ void Stopped() {deprecated}
+
+ Sent when a stream with remote device is suspended.
+
+ PropertyChanged(string name, variant value)
+
+ This signal indicates a changed value of the given
+ property.
+
+properties string State [readonly]
+
+ Possible values: "disconnected", "connecting",
+ "connected", "playing"
+
+ "disconnected" -> "connecting"
+ Either an incoming or outgoing connection
+ attempt ongoing.
+
+ "connecting" -> "disconnected"
+ Connection attempt failed
+
+ "connecting" -> "connected"
+ Successfully connected
+
+ "connected" -> "playing"
+ Audio stream active
+
+ "playing" -> "connected"
+ Audio stream suspended
+
+ "connected" -> "disconnected"
+ "playing" -> "disconnected"
+ Disconnected from the remote device
+
+ boolean Connected [readonly]
+
+ Indicates if a stream is setup to a A2DP sink on
+ the remote device.
+
+ boolean Playing [readonly]
+
+ Indicates if a stream is active to a A2DP sink on
+ the remote device.
+
+AudioSource hierarchy
+=====================
+
+Service org.bluez
+Interface org.bluez.AudioSource
+Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Methods void Connect()
+
+ Connect and setup a stream to a A2DP source on the
+ remote device.
+
+ void Disconnect()
+
+ Disconnect from the remote device.
+
+ dict GetProperties()
+
+ Returns all properties for the interface. See the
+ properties section for available properties.
+
+ Possible Errors: org.bluez.Error.InvalidArguments
+
+Signals PropertyChanged(string name, variant value)
+
+ This signal indicates a changed value of the given
+ property.
+
+properties string State [readonly]
+
+ Possible values: "disconnected", "connecting",
+ "connected", "playing"
+
+ "disconnected" -> "connecting"
+ Either an incoming or outgoing connection
+ attempt ongoing.
+
+ "connecting" -> "disconnected"
+ Connection attempt failed
+
+ "connecting" -> "connected"
+ Successfully connected
+
+ "connected" -> "playing"
+ Audio stream active
+
+ "playing" -> "connected"
+ Audio stream suspended
+
+ "connected" -> "disconnected"
+ "playing" -> "disconnected"
+ Disconnected from the remote device
+
+
+HeadsetGateway hierarchy
+========================
+
+Service org.bluez
+Interface org.bluez.HeadsetGateway
+Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+This interface is available for remote devices which can function in the Audio
+Gateway role of the HFP profiles.
+
+Methods void Connect()
+
+ Connect to the AG service on the remote device.
+
+ void Disconnect()
+
+ Disconnect from the AG service on the remote device
+
+ void AnswerCall()
+
+ It has to called only after Ring signal received.
+
+ void TerminateCall()
+
+ Terminate call which is running or reject an incoming
+ call. This has nothing with any 3-way situation incl.
+ RaH. Just plain old PDH.
+
+ void Call(string number)
+
+ Dial a number 'number'. No number processing is done
+ thus if AG would reject to dial it don't blame me :)
+
+ string GetOperatorName()
+
+ Find out the name of the currently selected network
+ operator by AG.
+
+ void SendDTMF(string digits)
+
+ Will send each digit in the 'digits' sequentially. Would
+ send nothing if there is non-dtmf digit.
+
+ string GetSubscriberNumber()
+
+ Get the voicecall subscriber number of AG
+
+ dict GetProperties()
+
+ Returns all properties for the interface. See the
+ properties section for available properties.
+
+Signals void Ring(string number)
+
+ Someone's calling from 'number'.
+ Caller number is provided as received from AG.
+
+ void CallTerminated()
+
+ Call failed to set up. It means that we tried to call
+ someone or someone tried to call us but call was not
+ accepted.
+
+ void CallStarted()
+
+ Call set up successfully.
+
+ void CallEnded()
+
+ Call was started and now ended. In contrast with
+ CallTerminated where call didn't started
+
+ PropertyChanged(string name, variant value)
+
+ This signal indicates a changed value of the given
+ property.
+
+properties boolean Connected [readonly]
+
+ Indicates if there is an active connection to the
+ AG service on the remote device.
+
+ uint16 RegistrationStatus [readonly]
+
+ Service availability indicatior of AG, where:
+ 0 implies no service. No Home/Roam network available.
+ 1 implies presense of service. Home/Roam network
+ available.
+
+ uint16 SignalStrength [readonly]
+
+ Signal strength indicator of AG, the value ranges from
+ 0 to 5.
+
+ uint16 RoamingStatus [readonly]
+
+ Roaming status indicator of AG, where:
+ 0 means roaming is not active
+ 1 means a roaming is active
+
+ uint16 BatteryCharge [readonly]
+
+ Battery Charge indicator of AG, the value ranges from
+ 0 to 5.
+
+ uint16 SpeakerGain [readonly]
+
+ The speaker gain when available.
+
+ uint16 MicrophoneGain [readonly]
+
+ The speaker gain when available.
diff --git a/doc/control-api.txt b/doc/control-api.txt
new file mode 100644
index 0000000..1a42846
--- /dev/null
+++ b/doc/control-api.txt
@@ -0,0 +1,142 @@
+BlueZ D-Bus Control API description
+***********************************
+
+Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+Copyright (C) 2007-2008 David Stockwell <dstockwell@frequency-one.com>
+
+
+Control hierarchy
+=================
+
+Service org.bluez
+Interface org.bluez.Control
+Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Methods void Connect()
+
+ Connect to remote device (CT or TG).
+
+ void Disconnect()
+
+ Disconnect remote device.
+
+ dict GetProperties()
+
+ Returns all properties for the interface. See the
+ properties section for available properties.
+
+ void VolumeUp()
+
+ Adjust remote volume one step up
+
+ void VolumeDown()
+
+ Adjust remote volume one step down
+
+ boolean SendPassthrough(avc_operation_id key, boolean state,
+ string op_data)
+
+ Called to send Passthrough commands. ONLY valid if
+ BlueZ device is in CT role.
+
+ boolean SendVendorDependent(string op_data)
+
+ Called to send VendorDependent commands, other than
+ Metadata or Events defined in the AVRCP+Metadata
+ specification.
+
+ void ChangePlayback(string status, uint32 elapsed)
+
+ The status can be "playing", "stopped", "paused",
+ "forward-seek", "reverse-seek" or "error". Elapsed is
+ the position within the track in milliseconds.
+
+ void ChangeTrack(dict metadata)
+
+ Called to send the mandated TrackChange event and
+ potential metadata information.
+
+ Current defined metadata information are represented
+ with the following keys:
+
+ Title string (mandatory)
+ Artist string
+ Album string
+ Genre string
+ NumberOfTracks uint32
+ TrackNumber uint32
+ TrackDuration uint32 (in milliseconds)
+
+ void ChangeSetting(string setting, variant value)
+
+ Called to transmit Application Settings, CT Status
+ and the like.
+
+ Currenet defined settings are represented with the
+ following keys:
+
+ Equalizer off, on
+ Repeat off, singletrack, alltracks, group
+ Shuffle off, alltracks, group
+ Scan off, alltracks, group
+ Battery normal, warning, critical, external, fullcharge
+ System powered, unpowered, unplugged
+ Volume uint8
+
+Signals Connected()
+
+ Sent when a successful AVRCP connection has been made
+ to the remote device.
+
+ Disconnected()
+
+ Sent when the AVRCP connection to the remote device
+ has been disconnected.
+
+ Passthrough(uint8 key, boolean state, int32 company_id,
+ string op_data)
+
+ Called when Passthrough command is received from
+ connected device.
+
+ NOTE: according to the AV/C Subpanel Spec, company_id
+ and op_data are passed ONLY when the key is
+ "Vendor_Unique", or 0x7E.
+
+ When the key is NOT 0x7E, the signal returns
+ company_id=-1, and zero-length op_data.
+
+ VendorDependentReceived(string op_data)
+
+ Called when VendorDependent message is received from
+ connected device (except for Metadata defined in
+ Bluetooth SIG AVRCP+Metadata spec).
+
+ TrackChanged(dict metadata)
+
+ Called when Metadata is received from connected device.
+ May be multiple meta attribute/element pairs.
+
+ PlaybackChanged(string status, uint32 elapsed)
+
+ SettingChanged(string setting, variant value)
+
+Properties uint8 SubUnitID [readonly]
+
+ The three-bit Subunit ID from the connected device.
+
+ uint8 SubUnitType [readonly]
+
+ The five-bit Subunit Type from the connected device.
+
+ boolean Connected [readonly]
+
+ array{uint32} CompanyIDs [readonly]
+
+ List of three-byte Company IDs (OUI) supported by the
+ connected device. Note that Bluetooth SIG Company
+ ID (0x001958) is always included.
+
+ array{string} Capabilities [readonly]
+
+ List of Capabilities provided by the connected device.
diff --git a/doc/device-api.txt b/doc/device-api.txt
new file mode 100644
index 0000000..d1feb18
--- /dev/null
+++ b/doc/device-api.txt
@@ -0,0 +1,199 @@
+BlueZ D-Bus Device API description
+**********************************
+
+Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+Copyright (C) 2005-2006 Johan Hedberg <johan.hedberg@nokia.com>
+Copyright (C) 2005-2006 Claudio Takahasi <claudio.takahasi@indt.org.br>
+Copyright (C) 2006-2007 Luiz von Dentz <luiz.dentz@indt.org.br>
+
+
+Device hierarchy
+================
+
+Service org.bluez
+Interface org.bluez.Device
+Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Methods dict GetProperties()
+
+ Returns all properties for the device. See the
+ properties section for available properties.
+
+ Possible Errors: org.bluez.Error.DoesNotExist
+ org.bluez.Error.InvalidArguments
+
+ void SetProperty(string name, variant value)
+
+ Changes the value of the specified property. Only
+ properties that are listed a read-write are changeable.
+ On success this will emit a PropertyChanged signal.
+
+ Possible Errors: org.bluez.Error.DoesNotExist
+ org.bluez.Error.InvalidArguments
+
+ dict DiscoverServices(string pattern)
+
+ This method starts the service discovery to retrieve
+ remote service records. The pattern parameter can
+ be used to specify specific UUIDs. And empty string
+ will look for the public browse group.
+
+ The return value is a dictionary with the record
+ handles as keys and the service record in XML format
+ as values. The key is uint32 and the value a string
+ for this dictionary.
+
+ Possible errors: org.bluez.Error.NotReady
+ org.bluez.Error.Failed
+ org.bluez.Error.InProgress
+
+ void CancelDiscovery()
+
+ This method will cancel any previous DiscoverServices
+ transaction.
+
+ Possible errors: org.bluez.Error.NotReady
+ org.bluez.Error.Failed
+ org.bluez.Error.NotAuthorized
+
+ void Disconnect()
+
+ This method disconnects a specific remote device by
+ terminating the low-level ACL connection. The use of
+ this method should be restricted to administrator
+ use.
+
+ A DisconnectRequested signal will be sent and the
+ actual disconnection will only happen 2 seconds later.
+ This enables upper-level applications to terminate
+ their connections gracefully before the ACL connection
+ is terminated.
+
+ Possible errors: org.bluez.Error.NotConnected
+
+ array{object} ListNodes()
+
+ Returns list of device node object paths.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.Failed
+ org.bluez.Error.OutOfMemory
+
+ object CreateNode(string uuid)
+
+ Creates a persistent device node binding with a
+ remote device. The actual support for the specified
+ UUID depends if the device driver has support for
+ persistent binding. At the moment only RFCOMM TTY
+ nodes are supported.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.NotSupported
+
+ void RemoveNode(object node)
+
+ Removes a persistent device node binding.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.DoesNotExist
+
+Signals PropertyChanged(string name, variant value)
+
+ This signal indicates a changed value of the given
+ property.
+
+ DisconnectRequested()
+
+ This signal will be sent when a low level
+ disconnection to a remote device has been requested.
+ The actual disconnection will happen 2 seconds later.
+
+ NodeCreated(object node)
+
+ Parameter is object path of created device node.
+
+ NodeRemoved(object node)
+
+ Parameter is object path of removed device node.
+
+Properties string Address [readonly]
+
+ The Bluetooth device address of the remote device.
+
+ string Name [readonly]
+
+ The Bluetooth remote name. This value can not be
+ changed. Use the Alias property instead.
+
+ string Icon [readonly]
+
+ Proposed icon name according to the freedesktop.org
+ icon naming specification.
+
+ uint32 Class [readonly]
+
+ The Bluetooth class of device of the remote device.
+
+ array{string} UUIDs [readonly]
+
+ List of 128-bit UUIDs that represents the available
+ remote services.
+
+ array{object} Services [readonly]
+
+ List of characteristics based services.
+
+ boolean Paired [readonly]
+
+ Indicates if the remote device is paired.
+
+ boolean Connected [readonly]
+
+ Indicates if the remote device is currently connected.
+ A PropertyChanged signal indicate changes to this
+ status.
+
+ boolean Trusted [readwrite]
+
+ Indicates if the remote is seen as trusted. This
+ setting can be changed by the application.
+
+ boolean Blocked [readwrite]
+
+ If set to true any incoming connections from the
+ device will be immediately rejected. Any device
+ drivers will also be removed and no new ones will
+ be probed as long as the device is blocked.
+
+ string Alias [readwrite]
+
+ The name alias for the remote device. The alias can
+ be used to have a different friendly name for the
+ remote device.
+
+ In case no alias is set, it will return the remote
+ device name. Setting an empty string as alias will
+ convert it back to the remote device name.
+
+ When reseting the alias with an empty string, the
+ emitted PropertyChanged signal will show the remote
+ name again.
+
+ array{object} Nodes [readonly]
+
+ List of device node object paths.
+
+ object Adapter [readonly]
+
+ The object path of the adapter the device belongs to.
+
+ boolean LegacyPairing [readonly]
+
+ Set to true if the device only supports the pre-2.1
+ pairing mechanism. This property is useful in the
+ Adapter.DeviceFound signal to anticipate whether
+ legacy or simple pairing will occur.
+
+ Note that this property can exhibit false-positives
+ in the case of Bluetooth 2.1 (or newer) devices that
+ have disabled Extended Inquiry Response support.
diff --git a/doc/health-api.txt b/doc/health-api.txt
new file mode 100644
index 0000000..3d0a717
--- /dev/null
+++ b/doc/health-api.txt
@@ -0,0 +1,166 @@
+BlueZ D-Bus Health API description
+**********************************
+
+ Santiago Carot-Nemesio <sancane@gmail.com>
+ José Antonio Santos-Cadenas <santoscadenas@gmail.com>
+ Elvis Pfützenreuter <epx@signove.com>
+
+Health Device Profile hierarchy
+===============================
+
+Service org.bluez
+Interface org.bluez.HealthManager
+Object path /org/bluez/
+
+Methods:
+
+ object CreateApplication(dict config)
+
+ Returns the path of the new registered application.
+
+ Dict is defined as below:
+ {
+ "DataType": uint16, (mandatory)
+ "Role" : ("Source" or "Sink"), (mandatory)
+ "Description" : string, (optional)
+ "ChannelType" : ("Reliable" or "Streaming")
+ (just for Sources, optional)
+ }
+
+ Application will be closed by the call or implicitly when the
+ programs leaves the bus.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+
+ void DestroyApplication(object application)
+
+ Closes the HDP application identified by the object path. Also
+ application will be closed if the process that started it leaves
+ the bus. Only the creator of the application will be able to
+ destroy it.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.NotFound
+ org.bluez.Error.NotAllowed
+
+--------------------------------------------------------------------------------
+
+Service org.bluez
+Interface org.bluez.HealthDevice
+Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Methods:
+
+ dict GetProperties()
+
+ Returns all properties for the interface. See the properties
+ section for available properties.
+
+ Posible errors: org.bluez.Error.NotAllowed
+
+ Boolean Echo()
+
+ Sends an echo petition to the remote service. Returns True if
+ response matches with the buffer sent. If some error is detected
+ False value is returned.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.OutOfRange
+
+ object CreateChannel(object application, string configuration)
+
+ Creates a new data channel.
+ The configuration should indicate the channel quality of
+ service using one of this values "Reliable", "Streaming", "Any".
+
+ Returns the object path that identifies the data channel that
+ is already connected.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.HealthError
+
+ void DestroyChannel(object channel)
+
+ Destroys the data channel object. Only the creator of the
+ channel or the creator of the HealtApplication that received the
+ data channel will be able to destroy it
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.NotFound
+ org.bluez.Error.NotAllowed
+
+Signals:
+
+ void ChannelConnected(object channel)
+
+ This signal is launched when a new data channel is created or
+ when a known data channel is reconnected.
+
+ void ChannelDeleted(object channel)
+
+ This signal is launched when a data channel is deleted.
+
+ After this signal the data channel path will not be valid and
+ its path can be reused for future data channels.
+
+ void PropertyChanged(string name, variant value)
+
+ This signal indicates a changed value of the given property.
+
+Properties:
+
+ object MainChannel [readonly]
+
+ The first reliable channel opened. It is needed by upper
+ applications in order to send specific protocol data units. The
+ first reliable can change after a reconnection.
+
+--------------------------------------------------------------------------------
+
+Service org.bluez
+Interface org.bluez.HealthChannel
+Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/chanZZZ
+
+Only the process that created the data channel or the creator of the
+HealthApplication that received it will be able to call this methods.
+
+Methods:
+
+ dict GetProperties()
+
+ Returns all properties for the interface. See the properties
+ section for available properties.
+
+ Posible errors: org.bluez.Error.NotAllowed
+
+ fd Acquire()
+
+ Returns the file descriptor for this data channel. If the data
+ channel is not connected it will also reconnect.
+
+ Possible errors: org.bluez.Error.NotConnected
+ org.bluez.Error.NotAllowed
+
+ void Release()
+
+ Releases the fd. Application should also need to close() it.
+
+ Possible errors: org.bluez.Error.NotAcquired
+ org.bluez.Error.NotAllowed
+
+Properties:
+
+ string Type [readonly]
+
+ The quality of service of the data channel. ("Reliable" or
+ "Streaming")
+
+ object Device [readonly]
+
+ Identifies the Remote Device that is connected with. Maps with
+ a HealthDevice object.
+
+ object Application [readonly]
+
+ Identifies the HealthApplication to which this channel is
+ related to (which indirectly defines its role and data type).
diff --git a/doc/hfp-api.txt b/doc/hfp-api.txt
new file mode 100644
index 0000000..93251e8
--- /dev/null
+++ b/doc/hfp-api.txt
@@ -0,0 +1,86 @@
+Gateway hierarchy
+========================
+
+Service org.bluez
+Interface org.bluez.HandsfreeGateway
+Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+This interface is available for remote devices which can function in the Audio
+Gateway role of the HFP profiles. It is intended to be used with external
+telephony stacks / handlers of the HFP protocol.
+
+Methods void Connect()
+
+ Connect to the AG service on the remote device.
+
+ void Disconnect()
+
+ Disconnect from the AG service on the remote device
+
+ dict GetProperties()
+
+ Returns all properties for the interface. See the
+ properties section for available properties.
+
+ void RegisterAgent(object path)
+
+ The object path defines the path the of the agent
+ that will be called when a new Handsfree connection
+ is established.
+
+ If an application disconnects from the bus all of its
+ registered agents will be removed.
+
+ void UnregisterAgent(object path)
+
+ This unregisters the agent that has been previously
+ registered. The object path parameter must match the
+ same value that has been used on registration.
+
+ Possible Errors: org.bluez.Error.Failed
+ org.bluez.Error.InvalidArguments
+
+
+Signals PropertyChanged(string name, variant value)
+
+ This signal indicates a changed value of the given
+ property.
+
+Properties string State [readonly]
+
+ Indicates the state of the connection. Possible
+ values are:
+ "disconnected"
+ "connecting"
+ "connected"
+ "playing"
+
+HandsfreeAgent hierarchy
+===============
+
+Service unique name
+Interface org.bluez.HandsfreeAgent
+Object path freely definable
+
+Methods void NewConnection(filedescriptor fd)
+
+ This method gets called whenever a new handsfree
+ connection has been established. The objectpath
+ contains the object path of the remote device. This
+ method assumes that DBus daemon with file descriptor
+ passing capability is being used.
+
+ The agent should only return successfully once the
+ establishment of the service level connection (SLC)
+ has been completed. In the case of Handsfree this
+ means that BRSF exchange has been performed and
+ necessary initialization has been done.
+
+ Possible Errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.Failed
+
+ void Release()
+
+ This method gets called whenever the service daemon
+ unregisters the agent or whenever the Adapter where
+ the HandsfreeAgent registers itself is removed.
diff --git a/doc/input-api.txt b/doc/input-api.txt
new file mode 100644
index 0000000..7c3a4b2
--- /dev/null
+++ b/doc/input-api.txt
@@ -0,0 +1,44 @@
+BlueZ D-Bus Input API description
+*********************************
+
+Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+
+
+Input hierarchy
+===============
+
+Service org.bluez
+Interface org.bluez.Input
+Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Methods void Connect()
+
+ Connect to the input device.
+
+ Possible errors: org.bluez.Error.AlreadyConnected
+ org.bluez.Error.ConnectionAttemptFailed
+
+ void Disconnect()
+
+ Disconnect from the input device.
+
+ To abort a connection attempt in case of errors or
+ timeouts in the client it is fine to call this method.
+
+ Possible errors: org.bluez.Error.Failed
+
+ dict GetProperties()
+
+ Returns all properties for the interface. See the
+ properties section for available properties.
+
+ Possible Errors: org.bluez.Error.InvalidArguments
+
+Signals PropertyChanged(string name, variant value)
+
+ This signal indicates a changed value of the given
+ property.
+
+Properties boolean Connected [readonly]
+
+ Indicates if the device is connected.
diff --git a/doc/manager-api.txt b/doc/manager-api.txt
new file mode 100644
index 0000000..d2c1caf
--- /dev/null
+++ b/doc/manager-api.txt
@@ -0,0 +1,74 @@
+BlueZ D-Bus Manager API description
+***********************************
+
+Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+Copyright (C) 2005-2006 Johan Hedberg <johan.hedberg@nokia.com>
+Copyright (C) 2005-2006 Claudio Takahasi <claudio.takahasi@indt.org.br>
+Copyright (C) 2006-2007 Luiz von Dentz <luiz.dentz@indt.org.br>
+
+
+Manager hierarchy
+=================
+
+Service org.bluez
+Interface org.bluez.Manager
+Object path /
+
+Methods dict GetProperties()
+
+ Returns all global properties. See the
+ properties section for available properties.
+
+ Possible Errors: org.bluez.Error.DoesNotExist
+ org.bluez.Error.InvalidArguments
+
+ object DefaultAdapter()
+
+ Returns object path for the default adapter.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.NoSuchAdapter
+
+ object FindAdapter(string pattern)
+
+ Returns object path for the specified adapter. Valid
+ patterns are "hci0" or "00:11:22:33:44:55".
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.NoSuchAdapter
+
+ array{object} ListAdapters() {deprecated}
+
+ Returns list of adapter object paths under /org/bluez.
+ This method is deprecated, instead use the Adapters
+ Property to get the list of adapter object paths.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.Failed
+ org.bluez.Error.OutOfMemory
+
+Signals PropertyChanged(string name, variant value)
+
+ This signal indicates a changed value of the given
+ property.
+
+ AdapterAdded(object adapter)
+
+ Parameter is object path of added adapter.
+
+ AdapterRemoved(object adapter)
+
+ Parameter is object path of removed adapter.
+
+ DefaultAdapterChanged(object adapter)
+
+ Parameter is object path of the new default adapter.
+
+ In case all adapters are removed this signal will not
+ be emitted. The AdapterRemoved signal has to be used
+ to detect that no default adapter is selected or
+ available anymore.
+
+Properties array{object} Adapters [readonly]
+
+ List of adapter object paths.
diff --git a/doc/media-api.txt b/doc/media-api.txt
new file mode 100644
index 0000000..b2f239a
--- /dev/null
+++ b/doc/media-api.txt
@@ -0,0 +1,165 @@
+BlueZ D-Bus Media API description
+*********************************
+
+Media hierarchy
+===============
+
+Service org.bluez
+Interface org.bluez.Media
+Object path [variable prefix]/{hci0,hci1,...}
+
+Methods void RegisterEndpoint(object endpoint, dict properties)
+
+ Register a local end point to sender, the sender can
+ register as many end points as it likes.
+
+ Note: If the sender disconnects the end points are
+ automatically unregistered.
+
+ possible properties:
+
+ string UUID:
+
+ UUID of the profile which the endpoint
+ is for.
+
+ byte Codec:
+
+ Assigned mumber of codec that the
+ endpoint implements. The values should
+ match the profile specification which
+ is indicated by the UUID.
+
+ array{byte} Capabilities:
+
+ Capabilities blob, it is used as it is
+ so the size and byte order must match.
+
+ Possible Errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.NotSupported - emitted
+ when interface for the end-point is
+ disabled.
+
+ void UnregisterEndpoint(object endpoint)
+
+ Unregister sender end point.
+
+MediaEndpoint hierarchy
+=======================
+
+Service unique name
+Interface org.bluez.MediaEndpoint
+Object path freely definable
+
+Methods void SetConfiguration(object transport, dict properties)
+
+ Set configuration for the transport.
+
+ array{byte} SelectConfiguration(array{byte} capabilities)
+
+ Select preferable configuration from the supported
+ capabilities.
+
+ Returns a configuration which can be used to setup
+ a transport.
+
+ Note: There is no need to cache the selected
+ configuration since on success the configuration is
+ send back as parameter of SetConfiguration.
+
+ void ClearConfiguration(object transport)
+
+ Clear transport configuration.
+
+ void Release()
+
+ This method gets called when the service daemon
+ unregisters the endpoint. An endpoint can use it to do
+ cleanup tasks. There is no need to unregister the
+ endpoint, because when this method gets called it has
+ already been unregistered.
+
+MediaTransport hierarchy
+========================
+
+Service org.bluez
+Interface org.bluez.MediaTransport
+Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/fdX
+
+Methods dict GetProperties()
+
+ Returns all properties for the interface. See the
+ properties section for available properties.
+
+ fd, uint16, uint16 Acquire(string accesstype)
+
+ Acquire transport file descriptor and the MTU for read
+ and write respectively.
+
+ possible accesstype:
+
+ "r" : Read only access
+
+ "w" : Write only access
+
+ "rw": Read and write access
+
+ void Release(string accesstype)
+
+ Releases file descriptor.
+
+ void SetProperty(string name, variant value)
+
+ Changes the value of the specified property. Only
+ properties that are listed a read-write can be changed.
+
+ On success this will emit a PropertyChanged signal.
+
+Signals void PropertyChanged(string name, variant value)
+
+ This signal indicates a changed value of the given
+ property.
+
+Properties object Device [readonly]
+
+ Device object which the transport is connected to.
+
+ string UUID [readonly]
+
+ UUID of the profile which the transport is for.
+
+ byte Codec [readonly]
+
+ Assigned mumber of codec that the transport support.
+ The values should match the profile specification which
+ is indicated by the UUID.
+
+ array{byte} Configuration [readonly]
+
+ Configuration blob, it is used as it is so the size and
+ byte order must match.
+
+ uint16 Delay [readwrite]
+
+ Optional. Transport delay in 1/10 of milisecond, this
+ property is only writeable when the transport was
+ acquired by the sender.
+
+ boolean NREC [readwrite]
+
+ Optional. Indicates if echo cancelling and noise
+ reduction functions are active in the transport, this
+ property is only writeable when the transport was
+ acquired by the sender.
+
+ boolean InbandRingtone [readwrite]
+
+ Optional. Indicates if the transport support sending
+ ringtones, this property is only writeable when the
+ transport was acquired by the sender.
+
+ string Routing [readonly]
+
+ Optional. Indicates where is the transport being routed
+
+ Possible Values: "HCI" or "PCM"
diff --git a/doc/network-api.txt b/doc/network-api.txt
new file mode 100644
index 0000000..4dd3e58
--- /dev/null
+++ b/doc/network-api.txt
@@ -0,0 +1,88 @@
+BlueZ D-Bus Network API description
+***********************************
+
+Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+
+
+Network hierarchy
+=================
+
+Service org.bluez
+Interface org.bluez.Network
+Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Methods string Connect(string uuid)
+
+ Connect to the network device and return the network
+ interface name. Examples of the interface name are
+ bnep0, bnep1 etc.
+
+ uuid can be either one of "gn", "panu" or "nap" (case
+ insensitive) or a traditional string representation of
+ UUID or a hexadecimal number.
+
+ The connection will be closed and network device
+ released either upon calling Disconnect() or when
+ the client disappears from the message bus.
+
+ Possible errors: org.bluez.Error.AlreadyConnected
+ org.bluez.Error.ConnectionAttemptFailed
+
+ void Disconnect()
+
+ Disconnect from the network device.
+
+ To abort a connection attempt in case of errors or
+ timeouts in the client it is fine to call this method.
+
+ Possible errors: org.bluez.Error.Failed
+
+ dict GetProperties()
+
+ Returns all properties for the interface. See the
+ properties section for available properties.
+
+Signals PropertyChanged(string name, variant value)
+
+ This signal indicates a changed value of the given
+ property.
+
+Properties boolean Connected [readonly]
+
+ Indicates if the device is connected.
+
+ string Interface [readonly]
+
+ Indicates the network interface name when available.
+
+ string UUID [readonly]
+
+ Indicates the connection role when available.
+
+
+Network server hierarchy
+========================
+
+Service org.bluez
+Interface org.bluez.NetworkServer
+Object path /org/bluez/{hci0,hci1,...}
+
+Methods void Register(string uuid, string bridge)
+
+ Register server for the provided UUID. Every new
+ connection to this server will be added the bridge
+ interface.
+
+ Valid UUIDs are "gn", "panu" or "nap".
+
+ Initially no network server SDP is provided. Only
+ after this method a SDP record will be available
+ and the BNEP server will be ready for incoming
+ connections.
+
+ void Unregister(string uuid)
+
+ Unregister the server for provided UUID.
+
+ All servers will be automatically unregistered when
+ the calling application terminates.
diff --git a/doc/sap-api.txt b/doc/sap-api.txt
new file mode 100644
index 0000000..b8b7253
--- /dev/null
+++ b/doc/sap-api.txt
@@ -0,0 +1,34 @@
+BlueZ D-Bus Sim Access Profile API description
+***********************************
+
+Copyright (C) 2010 ST-Ericsson SA
+
+
+Sim Access Profile hierarchy
+============================
+
+Service org.bluez
+Interface org.bluez.SimAccess
+Object path [variable prefix]/{hci0,hci1,...}
+
+Methods void Disconnect()
+
+ Disconnects SAP client from the server.
+
+ Possible errors: org.bluez.Error.Failed
+
+ dict GetProperties()
+
+ Return all properties for the interface. See the
+ properties section for available properties.
+
+ Possible Errors: org.bluez.Error.Failed
+
+Signals PropertyChanged(string name, variant value)
+
+ This signal indicates a changed value of the given
+ property.
+
+Properties boolean Connected [readonly]
+
+ Indicates if SAP client is connected to the server.
diff --git a/doc/serial-api.txt b/doc/serial-api.txt
new file mode 100644
index 0000000..5f9bd5f
--- /dev/null
+++ b/doc/serial-api.txt
@@ -0,0 +1,41 @@
+BlueZ D-Bus Serial API description
+**********************************
+
+Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+
+
+Serial hierarchy
+================
+
+Service org.bluez
+Interface org.bluez.Serial
+Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Methods string Connect(string pattern)
+
+ Connects to a specific RFCOMM based service on a
+ remote device and then creates a RFCOMM TTY
+ device for it. The RFCOMM TTY device is returned.
+
+ Possible patterns: UUID 128 bit as string
+ Profile short names, e.g: spp, dun
+ RFCOMM channel as string, 1-30
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.InProgress
+ org.bluez.Error.ConnectionAttemptFailed
+ org.bluez.Error.NotSupported
+
+ void Disconnect(string device)
+
+ Disconnect a RFCOMM TTY device that has been
+ created by Connect method.
+
+ To abort a connection attempt in case of errors or
+ timeouts in the client it is fine to call this method.
+
+ In that case one of patterns of the Connect method should
+ be suplied instead of the TTY device.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.DoesNotExist
diff --git a/doc/service-api.txt b/doc/service-api.txt
new file mode 100644
index 0000000..5c8c7f3
--- /dev/null
+++ b/doc/service-api.txt
@@ -0,0 +1,62 @@
+BlueZ D-Bus Adapter API description
+***********************************
+
+Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+Copyright (C) 2005-2006 Johan Hedberg <johan.hedberg@nokia.com>
+Copyright (C) 2005-2006 Claudio Takahasi <claudio.takahasi@indt.org.br>
+Copyright (C) 2006-2007 Luiz von Dentz <luiz.dentz@indt.org.br>
+
+
+Service hierarchy
+=================
+
+Service org.bluez
+Interface org.bluez.Service
+Object path [variable prefix]/{hci0,hci1,...}
+
+Methods uint32 AddRecord(string record)
+
+ Adds a new service record from the XML description
+ and returns the assigned record handle.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.Failed
+
+ void UpdateRecord(uint32 handle, string record)
+
+ Updates a given service record provided in the
+ XML format.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.NotAvailable
+ org.bluez.Error.Failed
+
+ void RemoveRecord(uint32 handle)
+
+ Remove a service record identified by its handle.
+
+ It is only possible to remove service records that
+ where added by the current connection.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.NotAuthorized
+ org.bluez.Error.DoesNotExist
+ org.bluez.Error.Failed
+
+ void RequestAuthorization(string address, uint32 handle)
+
+ Request an authorization for an incoming connection
+ for a specific service record. The service record
+ needs to be registered via AddRecord first.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.NotAuthorized
+ org.bluez.Error.DoesNotExist
+ org.bluez.Error.Failed
+
+ void CancelAuthorization()
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.NotAuthorized
+ org.bluez.Error.DoesNotExist
+ org.bluez.Error.Failed
diff --git a/doc/version.xml.in b/doc/version.xml.in
new file mode 100644
index 0000000..d78bda9
--- /dev/null
+++ b/doc/version.xml.in
@@ -0,0 +1 @@
+@VERSION@
diff --git a/gdbus/gdbus.h b/gdbus/gdbus.h
new file mode 100644
index 0000000..a0583e6
--- /dev/null
+++ b/gdbus/gdbus.h
@@ -0,0 +1,177 @@
+/*
+ *
+ * D-Bus helper library
+ *
+ * Copyright (C) 2004-2011 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
+ *
+ */
+
+#ifndef __GDBUS_H
+#define __GDBUS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <dbus/dbus.h>
+#include <glib.h>
+
+typedef void (* GDBusWatchFunction) (DBusConnection *connection,
+ void *user_data);
+
+typedef gboolean (* GDBusSignalFunction) (DBusConnection *connection,
+ DBusMessage *message, void *user_data);
+
+DBusConnection *g_dbus_setup_bus(DBusBusType type, const char *name,
+ DBusError *error);
+
+DBusConnection *g_dbus_setup_private(DBusBusType type, const char *name,
+ DBusError *error);
+
+gboolean g_dbus_request_name(DBusConnection *connection, const char *name,
+ DBusError *error);
+
+gboolean g_dbus_set_disconnect_function(DBusConnection *connection,
+ GDBusWatchFunction function,
+ void *user_data, DBusFreeFunction destroy);
+
+typedef void (* GDBusDestroyFunction) (void *user_data);
+
+typedef DBusMessage * (* GDBusMethodFunction) (DBusConnection *connection,
+ DBusMessage *message, void *user_data);
+
+typedef guint32 GDBusPendingReply;
+
+typedef void (* GDBusSecurityFunction) (DBusConnection *connection,
+ const char *action,
+ gboolean interaction,
+ GDBusPendingReply pending);
+
+typedef enum {
+ G_DBUS_METHOD_FLAG_DEPRECATED = (1 << 0),
+ G_DBUS_METHOD_FLAG_NOREPLY = (1 << 1),
+ G_DBUS_METHOD_FLAG_ASYNC = (1 << 2),
+} GDBusMethodFlags;
+
+typedef enum {
+ G_DBUS_SIGNAL_FLAG_DEPRECATED = (1 << 0),
+} GDBusSignalFlags;
+
+typedef enum {
+ G_DBUS_PROPERTY_FLAG_DEPRECATED = (1 << 0),
+} GDBusPropertyFlags;
+
+typedef enum {
+ G_DBUS_SECURITY_FLAG_DEPRECATED = (1 << 0),
+ G_DBUS_SECURITY_FLAG_BUILTIN = (1 << 1),
+ G_DBUS_SECURITY_FLAG_ALLOW_INTERACTION = (1 << 2),
+} GDBusSecurityFlags;
+
+typedef struct {
+ const char *name;
+ const char *signature;
+ const char *reply;
+ GDBusMethodFunction function;
+ GDBusMethodFlags flags;
+ unsigned int privilege;
+} GDBusMethodTable;
+
+typedef struct {
+ const char *name;
+ const char *signature;
+ GDBusSignalFlags flags;
+} GDBusSignalTable;
+
+typedef struct {
+ const char *name;
+ const char *type;
+ GDBusPropertyFlags flags;
+} GDBusPropertyTable;
+
+typedef struct {
+ unsigned int privilege;
+ const char *action;
+ GDBusSecurityFlags flags;
+ GDBusSecurityFunction function;
+} GDBusSecurityTable;
+
+gboolean g_dbus_register_interface(DBusConnection *connection,
+ const char *path, const char *name,
+ const GDBusMethodTable *methods,
+ const GDBusSignalTable *signals,
+ const GDBusPropertyTable *properties,
+ void *user_data,
+ GDBusDestroyFunction destroy);
+gboolean g_dbus_unregister_interface(DBusConnection *connection,
+ const char *path, const char *name);
+
+gboolean g_dbus_register_security(const GDBusSecurityTable *security);
+gboolean g_dbus_unregister_security(const GDBusSecurityTable *security);
+
+void g_dbus_pending_success(DBusConnection *connection,
+ GDBusPendingReply pending);
+void g_dbus_pending_error(DBusConnection *connection,
+ GDBusPendingReply pending,
+ const char *name, const char *format, ...)
+ __attribute__((format(printf, 4, 5)));
+void g_dbus_pending_error_valist(DBusConnection *connection,
+ GDBusPendingReply pending, const char *name,
+ const char *format, va_list args);
+
+DBusMessage *g_dbus_create_error(DBusMessage *message, const char *name,
+ const char *format, ...)
+ __attribute__((format(printf, 3, 4)));
+DBusMessage *g_dbus_create_error_valist(DBusMessage *message, const char *name,
+ const char *format, va_list args);
+DBusMessage *g_dbus_create_reply(DBusMessage *message, int type, ...);
+DBusMessage *g_dbus_create_reply_valist(DBusMessage *message,
+ int type, va_list args);
+
+gboolean g_dbus_send_message(DBusConnection *connection, DBusMessage *message);
+gboolean g_dbus_send_reply(DBusConnection *connection,
+ DBusMessage *message, int type, ...);
+gboolean g_dbus_send_reply_valist(DBusConnection *connection,
+ DBusMessage *message, int type, va_list args);
+
+gboolean g_dbus_emit_signal(DBusConnection *connection,
+ const char *path, const char *interface,
+ const char *name, int type, ...);
+gboolean g_dbus_emit_signal_valist(DBusConnection *connection,
+ const char *path, const char *interface,
+ const char *name, int type, va_list args);
+
+guint g_dbus_add_service_watch(DBusConnection *connection, const char *name,
+ GDBusWatchFunction connect,
+ GDBusWatchFunction disconnect,
+ void *user_data, GDBusDestroyFunction destroy);
+guint g_dbus_add_disconnect_watch(DBusConnection *connection, const char *name,
+ GDBusWatchFunction function,
+ void *user_data, GDBusDestroyFunction destroy);
+guint g_dbus_add_signal_watch(DBusConnection *connection,
+ const char *sender, const char *path,
+ const char *interface, const char *member,
+ GDBusSignalFunction function, void *user_data,
+ GDBusDestroyFunction destroy);
+gboolean g_dbus_remove_watch(DBusConnection *connection, guint tag);
+void g_dbus_remove_all_watches(DBusConnection *connection);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GDBUS_H */
diff --git a/gdbus/mainloop.c b/gdbus/mainloop.c
new file mode 100644
index 0000000..8718da0
--- /dev/null
+++ b/gdbus/mainloop.c
@@ -0,0 +1,383 @@
+/*
+ *
+ * D-Bus helper library
+ *
+ * Copyright (C) 2004-2011 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 <glib.h>
+#include <dbus/dbus.h>
+
+#ifdef NEED_DBUS_WATCH_GET_UNIX_FD
+#define dbus_watch_get_unix_fd dbus_watch_get_fd
+#endif
+
+#include "gdbus.h"
+
+#define DISPATCH_TIMEOUT 0
+
+#define info(fmt...)
+#define error(fmt...)
+#define debug(fmt...)
+
+struct timeout_handler {
+ guint id;
+ DBusTimeout *timeout;
+};
+
+struct watch_info {
+ guint id;
+ DBusWatch *watch;
+ DBusConnection *conn;
+};
+
+struct disconnect_data {
+ GDBusWatchFunction function;
+ void *user_data;
+};
+
+static gboolean disconnected_signal(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct disconnect_data *dc_data = data;
+
+ error("Got disconnected from the system message bus");
+
+ dc_data->function(conn, dc_data->user_data);
+
+ dbus_connection_unref(conn);
+
+ return TRUE;
+}
+
+static gboolean message_dispatch(void *data)
+{
+ DBusConnection *conn = data;
+
+ dbus_connection_ref(conn);
+
+ /* Dispatch messages */
+ while (dbus_connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS);
+
+ dbus_connection_unref(conn);
+
+ return FALSE;
+}
+
+static inline void queue_dispatch(DBusConnection *conn,
+ DBusDispatchStatus status)
+{
+ if (status == DBUS_DISPATCH_DATA_REMAINS)
+ g_timeout_add(DISPATCH_TIMEOUT, message_dispatch, conn);
+}
+
+static gboolean watch_func(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+ struct watch_info *info = data;
+ unsigned int flags = 0;
+ DBusDispatchStatus status;
+
+ dbus_connection_ref(info->conn);
+
+ if (cond & G_IO_IN) flags |= DBUS_WATCH_READABLE;
+ if (cond & G_IO_OUT) flags |= DBUS_WATCH_WRITABLE;
+ if (cond & G_IO_HUP) flags |= DBUS_WATCH_HANGUP;
+ if (cond & G_IO_ERR) flags |= DBUS_WATCH_ERROR;
+
+ dbus_watch_handle(info->watch, flags);
+
+ status = dbus_connection_get_dispatch_status(info->conn);
+ queue_dispatch(info->conn, status);
+
+ dbus_connection_unref(info->conn);
+
+ return TRUE;
+}
+
+static void watch_info_free(void *data)
+{
+ struct watch_info *info = data;
+
+ if (info->id > 0) {
+ g_source_remove(info->id);
+ info->id = 0;
+ }
+
+ dbus_connection_unref(info->conn);
+
+ g_free(info);
+}
+
+static dbus_bool_t add_watch(DBusWatch *watch, void *data)
+{
+ DBusConnection *conn = data;
+ GIOCondition cond = G_IO_HUP | G_IO_ERR;
+ GIOChannel *chan;
+ struct watch_info *info;
+ unsigned int flags;
+ int fd;
+
+ if (!dbus_watch_get_enabled(watch))
+ return TRUE;
+
+ info = g_new0(struct watch_info, 1);
+
+ fd = dbus_watch_get_unix_fd(watch);
+ chan = g_io_channel_unix_new(fd);
+
+ info->watch = watch;
+ info->conn = dbus_connection_ref(conn);
+
+ dbus_watch_set_data(watch, info, watch_info_free);
+
+ flags = dbus_watch_get_flags(watch);
+
+ if (flags & DBUS_WATCH_READABLE) cond |= G_IO_IN;
+ if (flags & DBUS_WATCH_WRITABLE) cond |= G_IO_OUT;
+
+ info->id = g_io_add_watch(chan, cond, watch_func, info);
+
+ g_io_channel_unref(chan);
+
+ return TRUE;
+}
+
+static void remove_watch(DBusWatch *watch, void *data)
+{
+ if (dbus_watch_get_enabled(watch))
+ return;
+
+ /* will trigger watch_info_free() */
+ dbus_watch_set_data(watch, NULL, NULL);
+}
+
+static void watch_toggled(DBusWatch *watch, void *data)
+{
+ /* Because we just exit on OOM, enable/disable is
+ * no different from add/remove */
+ if (dbus_watch_get_enabled(watch))
+ add_watch(watch, data);
+ else
+ remove_watch(watch, data);
+}
+
+static gboolean timeout_handler_dispatch(gpointer data)
+{
+ struct timeout_handler *handler = data;
+
+ handler->id = 0;
+
+ /* if not enabled should not be polled by the main loop */
+ if (!dbus_timeout_get_enabled(handler->timeout))
+ return FALSE;
+
+ dbus_timeout_handle(handler->timeout);
+
+ return FALSE;
+}
+
+static void timeout_handler_free(void *data)
+{
+ struct timeout_handler *handler = data;
+
+ if (handler->id > 0) {
+ g_source_remove(handler->id);
+ handler->id = 0;
+ }
+
+ g_free(handler);
+}
+
+static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data)
+{
+ int interval = dbus_timeout_get_interval(timeout);
+ struct timeout_handler *handler;
+
+ if (!dbus_timeout_get_enabled(timeout))
+ return TRUE;
+
+ handler = g_new0(struct timeout_handler, 1);
+
+ handler->timeout = timeout;
+
+ dbus_timeout_set_data(timeout, handler, timeout_handler_free);
+
+ handler->id = g_timeout_add(interval, timeout_handler_dispatch,
+ handler);
+
+ return TRUE;
+}
+
+static void remove_timeout(DBusTimeout *timeout, void *data)
+{
+ /* will trigger timeout_handler_free() */
+ dbus_timeout_set_data(timeout, NULL, NULL);
+}
+
+static void timeout_toggled(DBusTimeout *timeout, void *data)
+{
+ if (dbus_timeout_get_enabled(timeout))
+ add_timeout(timeout, data);
+ else
+ remove_timeout(timeout, data);
+}
+
+static void dispatch_status(DBusConnection *conn,
+ DBusDispatchStatus status, void *data)
+{
+ if (!dbus_connection_get_is_connected(conn))
+ return;
+
+ queue_dispatch(conn, status);
+}
+
+static inline void setup_dbus_with_main_loop(DBusConnection *conn)
+{
+ dbus_connection_set_watch_functions(conn, add_watch, remove_watch,
+ watch_toggled, conn, NULL);
+
+ dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout,
+ timeout_toggled, NULL, NULL);
+
+ dbus_connection_set_dispatch_status_function(conn, dispatch_status,
+ NULL, NULL);
+}
+
+static gboolean setup_bus(DBusConnection *conn, const char *name,
+ DBusError *error)
+{
+ gboolean result;
+ DBusDispatchStatus status;
+
+ if (name != NULL) {
+ result = g_dbus_request_name(conn, name, error);
+
+ if (error != NULL) {
+ if (dbus_error_is_set(error) == TRUE)
+ return FALSE;
+ }
+
+ if (result == FALSE)
+ return FALSE;
+ }
+
+ setup_dbus_with_main_loop(conn);
+
+ status = dbus_connection_get_dispatch_status(conn);
+ queue_dispatch(conn, status);
+
+ return TRUE;
+}
+
+DBusConnection *g_dbus_setup_bus(DBusBusType type, const char *name,
+ DBusError *error)
+{
+ DBusConnection *conn;
+
+ conn = dbus_bus_get(type, error);
+
+ if (error != NULL) {
+ if (dbus_error_is_set(error) == TRUE)
+ return NULL;
+ }
+
+ if (conn == NULL)
+ return NULL;
+
+ if (setup_bus(conn, name, error) == FALSE) {
+ dbus_connection_unref(conn);
+ return NULL;
+ }
+
+ return conn;
+}
+
+DBusConnection *g_dbus_setup_private(DBusBusType type, const char *name,
+ DBusError *error)
+{
+ DBusConnection *conn;
+
+ conn = dbus_bus_get_private(type, error);
+
+ if (error != NULL) {
+ if (dbus_error_is_set(error) == TRUE)
+ return NULL;
+ }
+
+ if (conn == NULL)
+ return NULL;
+
+ if (setup_bus(conn, name, error) == FALSE) {
+ dbus_connection_unref(conn);
+ return NULL;
+ }
+
+ return conn;
+}
+
+gboolean g_dbus_request_name(DBusConnection *connection, const char *name,
+ DBusError *error)
+{
+ int result;
+
+ result = dbus_bus_request_name(connection, name,
+ DBUS_NAME_FLAG_DO_NOT_QUEUE, error);
+
+ if (error != NULL) {
+ if (dbus_error_is_set(error) == TRUE)
+ return FALSE;
+ }
+
+ if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+ if (error != NULL)
+ dbus_set_error(error, name, "Name already in use");
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean g_dbus_set_disconnect_function(DBusConnection *connection,
+ GDBusWatchFunction function,
+ void *user_data, DBusFreeFunction destroy)
+{
+ struct disconnect_data *dc_data;
+
+ dc_data = g_new0(struct disconnect_data, 1);
+
+ dc_data->function = function;
+ dc_data->user_data = user_data;
+
+ dbus_connection_set_exit_on_disconnect(connection, FALSE);
+
+ if (g_dbus_add_signal_watch(connection, NULL, NULL,
+ DBUS_INTERFACE_LOCAL, "Disconnected",
+ disconnected_signal, dc_data, g_free) == 0) {
+ error("Failed to add watch for D-Bus Disconnected signal");
+ g_free(dc_data);
+ return FALSE;
+ }
+
+ return TRUE;
+}
diff --git a/gdbus/object.c b/gdbus/object.c
new file mode 100644
index 0000000..eaa2e1a
--- /dev/null
+++ b/gdbus/object.c
@@ -0,0 +1,869 @@
+/*
+ *
+ * D-Bus helper library
+ *
+ * Copyright (C) 2004-2011 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 <string.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "gdbus.h"
+
+#define info(fmt...)
+#define error(fmt...)
+#define debug(fmt...)
+
+struct generic_data {
+ unsigned int refcount;
+ GSList *interfaces;
+ char *introspect;
+};
+
+struct interface_data {
+ char *name;
+ const GDBusMethodTable *methods;
+ const GDBusSignalTable *signals;
+ const GDBusPropertyTable *properties;
+ void *user_data;
+ GDBusDestroyFunction destroy;
+};
+
+struct security_data {
+ GDBusPendingReply pending;
+ DBusMessage *message;
+ const GDBusMethodTable *method;
+ void *iface_user_data;
+};
+
+static void print_arguments(GString *gstr, const char *sig,
+ const char *direction)
+{
+ int i;
+
+ for (i = 0; sig[i]; i++) {
+ char type[32];
+ int struct_level, dict_level;
+ unsigned int len;
+ gboolean complete;
+
+ complete = FALSE;
+ struct_level = dict_level = 0;
+ memset(type, 0, sizeof(type));
+
+ /* Gather enough data to have a single complete type */
+ for (len = 0; len < (sizeof(type) - 1) && sig[i]; len++, i++) {
+ switch (sig[i]){
+ case '(':
+ struct_level++;
+ break;
+ case ')':
+ struct_level--;
+ if (struct_level <= 0 && dict_level <= 0)
+ complete = TRUE;
+ break;
+ case '{':
+ dict_level++;
+ break;
+ case '}':
+ dict_level--;
+ if (struct_level <= 0 && dict_level <= 0)
+ complete = TRUE;
+ break;
+ case 'a':
+ break;
+ default:
+ if (struct_level <= 0 && dict_level <= 0)
+ complete = TRUE;
+ break;
+ }
+
+ type[len] = sig[i];
+
+ if (complete)
+ break;
+ }
+
+
+ if (direction)
+ g_string_append_printf(gstr,
+ "\t\t\t<arg type=\"%s\" direction=\"%s\"/>\n",
+ type, direction);
+ else
+ g_string_append_printf(gstr,
+ "\t\t\t<arg type=\"%s\"/>\n",
+ type);
+ }
+}
+
+static void generate_interface_xml(GString *gstr, struct interface_data *iface)
+{
+ const GDBusMethodTable *method;
+ const GDBusSignalTable *signal;
+
+ for (method = iface->methods; method && method->name; method++) {
+ if (!strlen(method->signature) && !strlen(method->reply))
+ g_string_append_printf(gstr, "\t\t<method name=\"%s\"/>\n",
+ method->name);
+ else {
+ g_string_append_printf(gstr, "\t\t<method name=\"%s\">\n",
+ method->name);
+ print_arguments(gstr, method->signature, "in");
+ print_arguments(gstr, method->reply, "out");
+ g_string_append_printf(gstr, "\t\t</method>\n");
+ }
+ }
+
+ for (signal = iface->signals; signal && signal->name; signal++) {
+ if (!strlen(signal->signature))
+ g_string_append_printf(gstr, "\t\t<signal name=\"%s\"/>\n",
+ signal->name);
+ else {
+ g_string_append_printf(gstr, "\t\t<signal name=\"%s\">\n",
+ signal->name);
+ print_arguments(gstr, signal->signature, NULL);
+ g_string_append_printf(gstr, "\t\t</signal>\n");
+ }
+ }
+}
+
+static void generate_introspection_xml(DBusConnection *conn,
+ struct generic_data *data, const char *path)
+{
+ GSList *list;
+ GString *gstr;
+ char **children;
+ int i;
+
+ g_free(data->introspect);
+
+ gstr = g_string_new(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE);
+
+ g_string_append_printf(gstr, "<node>\n");
+
+ for (list = data->interfaces; list; list = list->next) {
+ struct interface_data *iface = list->data;
+
+ g_string_append_printf(gstr, "\t<interface name=\"%s\">\n",
+ iface->name);
+
+ generate_interface_xml(gstr, iface);
+
+ g_string_append_printf(gstr, "\t</interface>\n");
+ }
+
+ if (!dbus_connection_list_registered(conn, path, &children))
+ goto done;
+
+ for (i = 0; children[i]; i++)
+ g_string_append_printf(gstr, "\t<node name=\"%s\"/>\n",
+ children[i]);
+
+ dbus_free_string_array(children);
+
+done:
+ g_string_append_printf(gstr, "</node>\n");
+
+ data->introspect = g_string_free(gstr, FALSE);
+}
+
+static DBusMessage *introspect(DBusConnection *connection,
+ DBusMessage *message, void *user_data)
+{
+ struct generic_data *data = user_data;
+ DBusMessage *reply;
+
+ if (!dbus_message_has_signature(message, DBUS_TYPE_INVALID_AS_STRING)) {
+ error("Unexpected signature to introspect call");
+ return NULL;
+ }
+
+ if (data->introspect == NULL)
+ generate_introspection_xml(connection, data,
+ dbus_message_get_path(message));
+
+ reply = dbus_message_new_method_return(message);
+ if (reply == NULL)
+ return NULL;
+
+ dbus_message_append_args(reply, DBUS_TYPE_STRING, &data->introspect,
+ DBUS_TYPE_INVALID);
+
+ return reply;
+}
+
+static DBusHandlerResult process_message(DBusConnection *connection,
+ DBusMessage *message, const GDBusMethodTable *method,
+ void *iface_user_data)
+{
+ DBusMessage *reply;
+
+ reply = method->function(connection, message, iface_user_data);
+
+ if (method->flags & G_DBUS_METHOD_FLAG_NOREPLY) {
+ if (reply != NULL)
+ dbus_message_unref(reply);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ if (method->flags & G_DBUS_METHOD_FLAG_ASYNC) {
+ if (reply == NULL)
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ if (reply == NULL)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ dbus_connection_send(connection, reply, NULL);
+ dbus_message_unref(reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static GDBusPendingReply next_pending = 1;
+static GSList *pending_security = NULL;
+
+static const GDBusSecurityTable *security_table = NULL;
+
+void g_dbus_pending_success(DBusConnection *connection,
+ GDBusPendingReply pending)
+{
+ GSList *list;
+
+ for (list = pending_security; list; list = list->next) {
+ struct security_data *secdata = list->data;
+ DBusHandlerResult result;
+
+ if (secdata->pending != pending)
+ continue;
+
+ pending_security = g_slist_remove(pending_security, secdata);
+
+ result = process_message(connection, secdata->message,
+ secdata->method, secdata->iface_user_data);
+
+ dbus_message_unref(secdata->message);
+ g_free(secdata);
+ return;
+ }
+}
+
+void g_dbus_pending_error_valist(DBusConnection *connection,
+ GDBusPendingReply pending, const char *name,
+ const char *format, va_list args)
+{
+ GSList *list;
+
+ for (list = pending_security; list; list = list->next) {
+ struct security_data *secdata = list->data;
+ DBusMessage *reply;
+
+ if (secdata->pending != pending)
+ continue;
+
+ pending_security = g_slist_remove(pending_security, secdata);
+
+ reply = g_dbus_create_error_valist(secdata->message,
+ name, format, args);
+ if (reply != NULL) {
+ dbus_connection_send(connection, reply, NULL);
+ dbus_message_unref(reply);
+ }
+
+ dbus_message_unref(secdata->message);
+ g_free(secdata);
+ return;
+ }
+}
+
+void g_dbus_pending_error(DBusConnection *connection,
+ GDBusPendingReply pending,
+ const char *name, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+
+ g_dbus_pending_error_valist(connection, pending, name, format, args);
+
+ va_end(args);
+}
+
+int polkit_check_authorization(DBusConnection *conn,
+ const char *action, gboolean interaction,
+ void (*function) (dbus_bool_t authorized,
+ void *user_data),
+ void *user_data, int timeout);
+
+struct builtin_security_data {
+ DBusConnection *conn;
+ GDBusPendingReply pending;
+};
+
+static void builtin_security_result(dbus_bool_t authorized, void *user_data)
+{
+ struct builtin_security_data *data = user_data;
+
+ if (authorized == TRUE)
+ g_dbus_pending_success(data->conn, data->pending);
+ else
+ g_dbus_pending_error(data->conn, data->pending,
+ DBUS_ERROR_AUTH_FAILED, NULL);
+
+ g_free(data);
+}
+
+static void builtin_security_function(DBusConnection *conn,
+ const char *action,
+ gboolean interaction,
+ GDBusPendingReply pending)
+{
+ struct builtin_security_data *data;
+
+ data = g_new0(struct builtin_security_data, 1);
+ data->conn = conn;
+ data->pending = pending;
+
+ if (polkit_check_authorization(conn, action, interaction,
+ builtin_security_result, data, 30000) < 0)
+ g_dbus_pending_error(conn, pending, NULL, NULL);
+}
+
+static gboolean check_privilege(DBusConnection *conn, DBusMessage *msg,
+ const GDBusMethodTable *method, void *iface_user_data)
+{
+ const GDBusSecurityTable *security;
+
+ for (security = security_table; security && security->privilege;
+ security++) {
+ struct security_data *secdata;
+ gboolean interaction;
+
+ if (security->privilege != method->privilege)
+ continue;
+
+ secdata = g_new(struct security_data, 1);
+ secdata->pending = next_pending++;
+ secdata->message = dbus_message_ref(msg);
+ secdata->method = method;
+ secdata->iface_user_data = iface_user_data;
+
+ pending_security = g_slist_prepend(pending_security, secdata);
+
+ if (security->flags & G_DBUS_SECURITY_FLAG_ALLOW_INTERACTION)
+ interaction = TRUE;
+ else
+ interaction = FALSE;
+
+ if (!(security->flags & G_DBUS_SECURITY_FLAG_BUILTIN) &&
+ security->function)
+ security->function(conn, security->action,
+ interaction, secdata->pending);
+ else
+ builtin_security_function(conn, security->action,
+ interaction, secdata->pending);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void generic_unregister(DBusConnection *connection, void *user_data)
+{
+ struct generic_data *data = user_data;
+
+ g_free(data->introspect);
+ g_free(data);
+}
+
+static struct interface_data *find_interface(GSList *interfaces,
+ const char *name)
+{
+ GSList *list;
+
+ if (name == NULL)
+ return NULL;
+
+ for (list = interfaces; list; list = list->next) {
+ struct interface_data *iface = list->data;
+ if (!strcmp(name, iface->name))
+ return iface;
+ }
+
+ return NULL;
+}
+
+static DBusHandlerResult generic_message(DBusConnection *connection,
+ DBusMessage *message, void *user_data)
+{
+ struct generic_data *data = user_data;
+ struct interface_data *iface;
+ const GDBusMethodTable *method;
+ const char *interface;
+
+ interface = dbus_message_get_interface(message);
+
+ iface = find_interface(data->interfaces, interface);
+ if (iface == NULL)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ for (method = iface->methods; method &&
+ method->name && method->function; method++) {
+ if (dbus_message_is_method_call(message, iface->name,
+ method->name) == FALSE)
+ continue;
+
+ if (dbus_message_has_signature(message,
+ method->signature) == FALSE)
+ continue;
+
+ if (check_privilege(connection, message, method,
+ iface->user_data) == TRUE)
+ return DBUS_HANDLER_RESULT_HANDLED;
+
+ return process_message(connection, message, method,
+ iface->user_data);
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusObjectPathVTable generic_table = {
+ .unregister_function = generic_unregister,
+ .message_function = generic_message,
+};
+
+static void invalidate_parent_data(DBusConnection *conn, const char *child_path)
+{
+ struct generic_data *data = NULL;
+ char *parent_path, *slash;
+
+ parent_path = g_strdup(child_path);
+ slash = strrchr(parent_path, '/');
+ if (slash == NULL)
+ goto done;
+
+ if (slash == parent_path && parent_path[1] != '\0')
+ parent_path[1] = '\0';
+ else
+ *slash = '\0';
+
+ if (!strlen(parent_path))
+ goto done;
+
+ if (dbus_connection_get_object_path_data(conn, parent_path,
+ (void *) &data) == FALSE) {
+ goto done;
+ }
+
+ invalidate_parent_data(conn, parent_path);
+
+ if (data == NULL)
+ goto done;
+
+ g_free(data->introspect);
+ data->introspect = NULL;
+
+done:
+ g_free(parent_path);
+}
+
+static GDBusMethodTable introspect_methods[] = {
+ { "Introspect", "", "s", introspect },
+ { }
+};
+
+static void add_interface(struct generic_data *data, const char *name,
+ const GDBusMethodTable *methods,
+ const GDBusSignalTable *signals,
+ const GDBusPropertyTable *properties,
+ void *user_data,
+ GDBusDestroyFunction destroy)
+{
+ struct interface_data *iface;
+
+ iface = g_new0(struct interface_data, 1);
+ iface->name = g_strdup(name);
+ iface->methods = methods;
+ iface->signals = signals;
+ iface->properties = properties;
+ iface->user_data = user_data;
+ iface->destroy = destroy;
+
+ data->interfaces = g_slist_append(data->interfaces, iface);
+}
+
+static struct generic_data *object_path_ref(DBusConnection *connection,
+ const char *path)
+{
+ struct generic_data *data;
+
+ if (dbus_connection_get_object_path_data(connection, path,
+ (void *) &data) == TRUE) {
+ if (data != NULL) {
+ data->refcount++;
+ return data;
+ }
+ }
+
+ data = g_new0(struct generic_data, 1);
+ data->refcount = 1;
+
+ data->introspect = g_strdup(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE "<node></node>");
+
+ if (!dbus_connection_register_object_path(connection, path,
+ &generic_table, data)) {
+ g_free(data->introspect);
+ g_free(data);
+ return NULL;
+ }
+
+ invalidate_parent_data(connection, path);
+
+ add_interface(data, DBUS_INTERFACE_INTROSPECTABLE,
+ introspect_methods, NULL, NULL, data, NULL);
+
+ return data;
+}
+
+static gboolean remove_interface(struct generic_data *data, const char *name)
+{
+ struct interface_data *iface;
+
+ iface = find_interface(data->interfaces, name);
+ if (iface == NULL)
+ return FALSE;
+
+ data->interfaces = g_slist_remove(data->interfaces, iface);
+
+ if (iface->destroy)
+ iface->destroy(iface->user_data);
+
+ g_free(iface->name);
+ g_free(iface);
+
+ return TRUE;
+}
+
+static void object_path_unref(DBusConnection *connection, const char *path)
+{
+ struct generic_data *data = NULL;
+
+ if (dbus_connection_get_object_path_data(connection, path,
+ (void *) &data) == FALSE)
+ return;
+
+ if (data == NULL)
+ return;
+
+ data->refcount--;
+
+ if (data->refcount > 0)
+ return;
+
+ remove_interface(data, DBUS_INTERFACE_INTROSPECTABLE);
+
+ invalidate_parent_data(connection, path);
+
+ dbus_connection_unregister_object_path(connection, path);
+}
+
+static gboolean check_signal(DBusConnection *conn, const char *path,
+ const char *interface, const char *name,
+ const char **args)
+{
+ struct generic_data *data = NULL;
+ struct interface_data *iface;
+ const GDBusSignalTable *signal;
+
+ *args = NULL;
+ if (!dbus_connection_get_object_path_data(conn, path,
+ (void *) &data) || data == NULL) {
+ error("dbus_connection_emit_signal: path %s isn't registered",
+ path);
+ return FALSE;
+ }
+
+ iface = find_interface(data->interfaces, interface);
+ if (iface == NULL) {
+ error("dbus_connection_emit_signal: %s does not implement %s",
+ path, interface);
+ return FALSE;
+ }
+
+ for (signal = iface->signals; signal && signal->name; signal++) {
+ if (!strcmp(signal->name, name)) {
+ *args = signal->signature;
+ break;
+ }
+ }
+
+ if (*args == NULL) {
+ error("No signal named %s on interface %s", name, interface);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static dbus_bool_t emit_signal_valist(DBusConnection *conn,
+ const char *path,
+ const char *interface,
+ const char *name,
+ int first,
+ va_list var_args)
+{
+ DBusMessage *signal;
+ dbus_bool_t ret;
+ const char *signature, *args;
+
+ if (!check_signal(conn, path, interface, name, &args))
+ return FALSE;
+
+ signal = dbus_message_new_signal(path, interface, name);
+ if (signal == NULL) {
+ error("Unable to allocate new %s.%s signal", interface, name);
+ return FALSE;
+ }
+
+ ret = dbus_message_append_args_valist(signal, first, var_args);
+ if (!ret)
+ goto fail;
+
+ signature = dbus_message_get_signature(signal);
+ if (strcmp(args, signature) != 0) {
+ error("%s.%s: expected signature'%s' but got '%s'",
+ interface, name, args, signature);
+ ret = FALSE;
+ goto fail;
+ }
+
+ ret = dbus_connection_send(conn, signal, NULL);
+
+fail:
+ dbus_message_unref(signal);
+
+ return ret;
+}
+
+gboolean g_dbus_register_interface(DBusConnection *connection,
+ const char *path, const char *name,
+ const GDBusMethodTable *methods,
+ const GDBusSignalTable *signals,
+ const GDBusPropertyTable *properties,
+ void *user_data,
+ GDBusDestroyFunction destroy)
+{
+ struct generic_data *data;
+
+ data = object_path_ref(connection, path);
+ if (data == NULL)
+ return FALSE;
+
+ if (find_interface(data->interfaces, name)) {
+ object_path_unref(connection, path);
+ return FALSE;
+ }
+
+ add_interface(data, name, methods, signals,
+ properties, user_data, destroy);
+
+ g_free(data->introspect);
+ data->introspect = NULL;
+
+ return TRUE;
+}
+
+gboolean g_dbus_unregister_interface(DBusConnection *connection,
+ const char *path, const char *name)
+{
+ struct generic_data *data = NULL;
+
+ if (path == NULL)
+ return FALSE;
+
+ if (dbus_connection_get_object_path_data(connection, path,
+ (void *) &data) == FALSE)
+ return FALSE;
+
+ if (data == NULL)
+ return FALSE;
+
+ if (remove_interface(data, name) == FALSE)
+ return FALSE;
+
+ g_free(data->introspect);
+ data->introspect = NULL;
+
+ object_path_unref(connection, path);
+
+ return TRUE;
+}
+
+gboolean g_dbus_register_security(const GDBusSecurityTable *security)
+{
+ if (security_table != NULL)
+ return FALSE;
+
+ security_table = security;
+
+ return TRUE;
+}
+
+gboolean g_dbus_unregister_security(const GDBusSecurityTable *security)
+{
+ security_table = NULL;
+
+ return TRUE;
+}
+
+DBusMessage *g_dbus_create_error_valist(DBusMessage *message, const char *name,
+ const char *format, va_list args)
+{
+ char str[1024];
+
+ vsnprintf(str, sizeof(str), format, args);
+
+ return dbus_message_new_error(message, name, str);
+}
+
+DBusMessage *g_dbus_create_error(DBusMessage *message, const char *name,
+ const char *format, ...)
+{
+ va_list args;
+ DBusMessage *reply;
+
+ va_start(args, format);
+
+ reply = g_dbus_create_error_valist(message, name, format, args);
+
+ va_end(args);
+
+ return reply;
+}
+
+DBusMessage *g_dbus_create_reply_valist(DBusMessage *message,
+ int type, va_list args)
+{
+ DBusMessage *reply;
+
+ reply = dbus_message_new_method_return(message);
+ if (reply == NULL)
+ return NULL;
+
+ if (dbus_message_append_args_valist(reply, type, args) == FALSE) {
+ dbus_message_unref(reply);
+ return NULL;
+ }
+
+ return reply;
+}
+
+DBusMessage *g_dbus_create_reply(DBusMessage *message, int type, ...)
+{
+ va_list args;
+ DBusMessage *reply;
+
+ va_start(args, type);
+
+ reply = g_dbus_create_reply_valist(message, type, args);
+
+ va_end(args);
+
+ return reply;
+}
+
+gboolean g_dbus_send_message(DBusConnection *connection, DBusMessage *message)
+{
+ dbus_bool_t result;
+
+ if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL)
+ dbus_message_set_no_reply(message, TRUE);
+
+ result = dbus_connection_send(connection, message, NULL);
+
+ dbus_message_unref(message);
+
+ return result;
+}
+
+gboolean g_dbus_send_reply_valist(DBusConnection *connection,
+ DBusMessage *message, int type, va_list args)
+{
+ DBusMessage *reply;
+
+ reply = dbus_message_new_method_return(message);
+ if (reply == NULL)
+ return FALSE;
+
+ if (dbus_message_append_args_valist(reply, type, args) == FALSE) {
+ dbus_message_unref(reply);
+ return FALSE;
+ }
+
+ return g_dbus_send_message(connection, reply);
+}
+
+gboolean g_dbus_send_reply(DBusConnection *connection,
+ DBusMessage *message, int type, ...)
+{
+ va_list args;
+ gboolean result;
+
+ va_start(args, type);
+
+ result = g_dbus_send_reply_valist(connection, message, type, args);
+
+ va_end(args);
+
+ return result;
+}
+
+gboolean g_dbus_emit_signal(DBusConnection *connection,
+ const char *path, const char *interface,
+ const char *name, int type, ...)
+{
+ va_list args;
+ gboolean result;
+
+ va_start(args, type);
+
+ result = emit_signal_valist(connection, path, interface,
+ name, type, args);
+
+ va_end(args);
+
+ return result;
+}
+
+gboolean g_dbus_emit_signal_valist(DBusConnection *connection,
+ const char *path, const char *interface,
+ const char *name, int type, va_list args)
+{
+ return emit_signal_valist(connection, path, interface,
+ name, type, args);
+}
diff --git a/gdbus/polkit.c b/gdbus/polkit.c
new file mode 100644
index 0000000..9e95fa3
--- /dev/null
+++ b/gdbus/polkit.c
@@ -0,0 +1,202 @@
+/*
+ *
+ * D-Bus helper library
+ *
+ * Copyright (C) 2004-2011 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 <errno.h>
+
+#include <dbus/dbus.h>
+
+#include <glib.h>
+
+int polkit_check_authorization(DBusConnection *conn,
+ const char *action, gboolean interaction,
+ void (*function) (dbus_bool_t authorized,
+ void *user_data),
+ void *user_data, int timeout);
+
+static void add_dict_with_string_value(DBusMessageIter *iter,
+ const char *key, const char *str)
+{
+ DBusMessageIter dict, entry, value;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+ dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
+ NULL, &entry);
+
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+ dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
+ DBUS_TYPE_STRING_AS_STRING, &value);
+ dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &str);
+ dbus_message_iter_close_container(&entry, &value);
+
+ dbus_message_iter_close_container(&dict, &entry);
+ dbus_message_iter_close_container(iter, &dict);
+}
+
+static void add_empty_string_dict(DBusMessageIter *iter)
+{
+ DBusMessageIter dict;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ dbus_message_iter_close_container(iter, &dict);
+}
+
+static void add_arguments(DBusConnection *conn, DBusMessageIter *iter,
+ const char *action, dbus_uint32_t flags)
+{
+ const char *busname = dbus_bus_get_unique_name(conn);
+ const char *kind = "system-bus-name";
+ const char *cancel = "";
+ DBusMessageIter subject;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT,
+ NULL, &subject);
+ dbus_message_iter_append_basic(&subject, DBUS_TYPE_STRING, &kind);
+ add_dict_with_string_value(&subject, "name", busname);
+ dbus_message_iter_close_container(iter, &subject);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &action);
+ add_empty_string_dict(iter);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &flags);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &cancel);
+}
+
+static dbus_bool_t parse_result(DBusMessageIter *iter)
+{
+ DBusMessageIter result;
+ dbus_bool_t authorized, challenge;
+
+ dbus_message_iter_recurse(iter, &result);
+
+ dbus_message_iter_get_basic(&result, &authorized);
+ dbus_message_iter_get_basic(&result, &challenge);
+
+ return authorized;
+}
+
+struct authorization_data {
+ void (*function) (dbus_bool_t authorized, void *user_data);
+ void *user_data;
+};
+
+static void authorization_reply(DBusPendingCall *call, void *user_data)
+{
+ struct authorization_data *data = user_data;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ dbus_bool_t authorized = FALSE;
+
+ reply = dbus_pending_call_steal_reply(call);
+
+ if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
+ goto done;
+
+ if (dbus_message_has_signature(reply, "(bba{ss})") == FALSE)
+ goto done;
+
+ dbus_message_iter_init(reply, &iter);
+
+ authorized = parse_result(&iter);
+
+done:
+ if (data->function != NULL)
+ data->function(authorized, data->user_data);
+
+ dbus_message_unref(reply);
+
+ dbus_pending_call_unref(call);
+}
+
+#define AUTHORITY_DBUS "org.freedesktop.PolicyKit1"
+#define AUTHORITY_INTF "org.freedesktop.PolicyKit1.Authority"
+#define AUTHORITY_PATH "/org/freedesktop/PolicyKit1/Authority"
+
+int polkit_check_authorization(DBusConnection *conn,
+ const char *action, gboolean interaction,
+ void (*function) (dbus_bool_t authorized,
+ void *user_data),
+ void *user_data, int timeout)
+{
+ struct authorization_data *data;
+ DBusMessage *msg;
+ DBusMessageIter iter;
+ DBusPendingCall *call;
+ dbus_uint32_t flags = 0x00000000;
+
+ if (conn == NULL)
+ return -EINVAL;
+
+ data = dbus_malloc0(sizeof(*data));
+ if (data == NULL)
+ return -ENOMEM;
+
+ msg = dbus_message_new_method_call(AUTHORITY_DBUS, AUTHORITY_PATH,
+ AUTHORITY_INTF, "CheckAuthorization");
+ if (msg == NULL) {
+ dbus_free(data);
+ return -ENOMEM;
+ }
+
+ if (interaction == TRUE)
+ flags |= 0x00000001;
+
+ if (action == NULL)
+ action = "org.freedesktop.policykit.exec";
+
+ dbus_message_iter_init_append(msg, &iter);
+ add_arguments(conn, &iter, action, flags);
+
+ if (dbus_connection_send_with_reply(conn, msg,
+ &call, timeout) == FALSE) {
+ dbus_message_unref(msg);
+ dbus_free(data);
+ return -EIO;
+ }
+
+ if (call == NULL) {
+ dbus_message_unref(msg);
+ dbus_free(data);
+ return -EIO;
+ }
+
+ data->function = function;
+ data->user_data = user_data;
+
+ dbus_pending_call_set_notify(call, authorization_reply,
+ data, dbus_free);
+
+ dbus_message_unref(msg);
+
+ return 0;
+}
diff --git a/gdbus/watch.c b/gdbus/watch.c
new file mode 100644
index 0000000..fba58c3
--- /dev/null
+++ b/gdbus/watch.c
@@ -0,0 +1,748 @@
+/*
+ *
+ * D-Bus helper library
+ *
+ * Copyright (C) 2004-2011 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 <string.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "gdbus.h"
+
+#define info(fmt...)
+#define error(fmt...)
+#define debug(fmt...)
+
+static DBusHandlerResult message_filter(DBusConnection *connection,
+ DBusMessage *message, void *user_data);
+
+static guint listener_id = 0;
+static GSList *listeners = NULL;
+
+struct service_data {
+ DBusConnection *conn;
+ DBusPendingCall *call;
+ char *name;
+ const char *owner;
+ guint id;
+ struct filter_callback *callback;
+};
+
+struct filter_callback {
+ GDBusWatchFunction conn_func;
+ GDBusWatchFunction disc_func;
+ GDBusSignalFunction signal_func;
+ GDBusDestroyFunction destroy_func;
+ struct service_data *data;
+ void *user_data;
+ guint id;
+};
+
+struct filter_data {
+ DBusConnection *connection;
+ DBusHandleMessageFunction handle_func;
+ char *name;
+ char *owner;
+ char *path;
+ char *interface;
+ char *member;
+ char *argument;
+ GSList *callbacks;
+ GSList *processed;
+ guint name_watch;
+ gboolean lock;
+ gboolean registered;
+};
+
+static struct filter_data *filter_data_find(DBusConnection *connection,
+ const char *name,
+ const char *owner,
+ const char *path,
+ const char *interface,
+ const char *member,
+ const char *argument)
+{
+ GSList *current;
+
+ for (current = listeners;
+ current != NULL; current = current->next) {
+ struct filter_data *data = current->data;
+
+ if (connection != data->connection)
+ continue;
+
+ if (name && data->name &&
+ g_str_equal(name, data->name) == FALSE)
+ continue;
+
+ if (owner && data->owner &&
+ g_str_equal(owner, data->owner) == FALSE)
+ continue;
+
+ if (path && data->path &&
+ g_str_equal(path, data->path) == FALSE)
+ continue;
+
+ if (interface && data->interface &&
+ g_str_equal(interface, data->interface) == FALSE)
+ continue;
+
+ if (member && data->member &&
+ g_str_equal(member, data->member) == FALSE)
+ continue;
+
+ if (argument && data->argument &&
+ g_str_equal(argument, data->argument) == FALSE)
+ continue;
+
+ return data;
+ }
+
+ return NULL;
+}
+
+static void format_rule(struct filter_data *data, char *rule, size_t size)
+{
+ const char *sender;
+ int offset;
+
+ offset = snprintf(rule, size, "type='signal'");
+ sender = data->name ? : data->owner;
+
+ if (sender)
+ offset += snprintf(rule + offset, size - offset,
+ ",sender='%s'", sender);
+ if (data->path)
+ offset += snprintf(rule + offset, size - offset,
+ ",path='%s'", data->path);
+ if (data->interface)
+ offset += snprintf(rule + offset, size - offset,
+ ",interface='%s'", data->interface);
+ if (data->member)
+ offset += snprintf(rule + offset, size - offset,
+ ",member='%s'", data->member);
+ if (data->argument)
+ snprintf(rule + offset, size - offset,
+ ",arg0='%s'", data->argument);
+}
+
+static gboolean add_match(struct filter_data *data,
+ DBusHandleMessageFunction filter)
+{
+ DBusError err;
+ char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH];
+
+ format_rule(data, rule, sizeof(rule));
+ dbus_error_init(&err);
+
+ dbus_bus_add_match(data->connection, rule, &err);
+ if (dbus_error_is_set(&err)) {
+ error("Adding match rule \"%s\" failed: %s", rule,
+ err.message);
+ dbus_error_free(&err);
+ return FALSE;
+ }
+
+ data->handle_func = filter;
+ data->registered = TRUE;
+
+ return TRUE;
+}
+
+static gboolean remove_match(struct filter_data *data)
+{
+ DBusError err;
+ char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH];
+
+ format_rule(data, rule, sizeof(rule));
+
+ dbus_error_init(&err);
+
+ dbus_bus_remove_match(data->connection, rule, &err);
+ if (dbus_error_is_set(&err)) {
+ error("Removing owner match rule for %s failed: %s",
+ rule, err.message);
+ dbus_error_free(&err);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static struct filter_data *filter_data_get(DBusConnection *connection,
+ DBusHandleMessageFunction filter,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member,
+ const char *argument)
+{
+ struct filter_data *data;
+ const char *name = NULL, *owner = NULL;
+
+ if (filter_data_find(connection, NULL, NULL, NULL, NULL, NULL, NULL) == NULL) {
+ if (!dbus_connection_add_filter(connection,
+ message_filter, NULL, NULL)) {
+ error("dbus_connection_add_filter() failed");
+ return NULL;
+ }
+ }
+
+ if (sender == NULL)
+ goto proceed;
+
+ if (sender[0] == ':')
+ owner = sender;
+ else
+ name = sender;
+
+proceed:
+ data = filter_data_find(connection, name, owner, path, interface,
+ member, argument);
+ if (data)
+ return data;
+
+ data = g_new0(struct filter_data, 1);
+
+ data->connection = dbus_connection_ref(connection);
+ data->name = name ? g_strdup(name) : NULL;
+ data->owner = owner ? g_strdup(owner) : NULL;
+ data->path = g_strdup(path);
+ data->interface = g_strdup(interface);
+ data->member = g_strdup(member);
+ data->argument = g_strdup(argument);
+
+ if (!add_match(data, filter)) {
+ g_free(data);
+ return NULL;
+ }
+
+ listeners = g_slist_append(listeners, data);
+
+ return data;
+}
+
+static struct filter_callback *filter_data_find_callback(
+ struct filter_data *data,
+ guint id)
+{
+ GSList *l;
+
+ for (l = data->callbacks; l; l = l->next) {
+ struct filter_callback *cb = l->data;
+ if (cb->id == id)
+ return cb;
+ }
+ for (l = data->processed; l; l = l->next) {
+ struct filter_callback *cb = l->data;
+ if (cb->id == id)
+ return cb;
+ }
+
+ return NULL;
+}
+
+static void filter_data_free(struct filter_data *data)
+{
+ GSList *l;
+
+ for (l = data->callbacks; l != NULL; l = l->next)
+ g_free(l->data);
+
+ g_slist_free(data->callbacks);
+ g_dbus_remove_watch(data->connection, data->name_watch);
+ g_free(data->name);
+ g_free(data->owner);
+ g_free(data->path);
+ g_free(data->interface);
+ g_free(data->member);
+ g_free(data->argument);
+ dbus_connection_unref(data->connection);
+ g_free(data);
+}
+
+static void filter_data_call_and_free(struct filter_data *data)
+{
+ GSList *l;
+
+ for (l = data->callbacks; l != NULL; l = l->next) {
+ struct filter_callback *cb = l->data;
+ if (cb->disc_func)
+ cb->disc_func(data->connection, cb->user_data);
+ if (cb->destroy_func)
+ cb->destroy_func(cb->user_data);
+ g_free(cb);
+ }
+
+ filter_data_free(data);
+}
+
+static struct filter_callback *filter_data_add_callback(
+ struct filter_data *data,
+ GDBusWatchFunction connect,
+ GDBusWatchFunction disconnect,
+ GDBusSignalFunction signal,
+ GDBusDestroyFunction destroy,
+ void *user_data)
+{
+ struct filter_callback *cb = NULL;
+
+ cb = g_new0(struct filter_callback, 1);
+
+ cb->conn_func = connect;
+ cb->disc_func = disconnect;
+ cb->signal_func = signal;
+ cb->destroy_func = destroy;
+ cb->user_data = user_data;
+ cb->id = ++listener_id;
+
+ if (data->lock)
+ data->processed = g_slist_append(data->processed, cb);
+ else
+ data->callbacks = g_slist_append(data->callbacks, cb);
+
+ return cb;
+}
+
+static void service_data_free(struct service_data *data)
+{
+ struct filter_callback *callback = data->callback;
+
+ dbus_connection_unref(data->conn);
+
+ if (data->call)
+ dbus_pending_call_unref(data->call);
+
+ if (data->id)
+ g_source_remove(data->id);
+
+ g_free(data->name);
+ g_free(data);
+
+ callback->data = NULL;
+}
+
+static gboolean filter_data_remove_callback(struct filter_data *data,
+ struct filter_callback *cb)
+{
+ DBusConnection *connection;
+
+ data->callbacks = g_slist_remove(data->callbacks, cb);
+ data->processed = g_slist_remove(data->processed, cb);
+
+ /* Cancel pending operations */
+ if (cb->data) {
+ if (cb->data->call)
+ dbus_pending_call_cancel(cb->data->call);
+ service_data_free(cb->data);
+ }
+
+ if (cb->destroy_func)
+ cb->destroy_func(cb->user_data);
+
+ g_free(cb);
+
+ /* Don't remove the filter if other callbacks exist or data is lock
+ * processing callbacks */
+ if (data->callbacks || data->lock)
+ return TRUE;
+
+ if (data->registered && !remove_match(data))
+ return FALSE;
+
+ connection = dbus_connection_ref(data->connection);
+ listeners = g_slist_remove(listeners, data);
+ filter_data_free(data);
+
+ /* Remove filter if there are no listeners left for the connection */
+ data = filter_data_find(connection, NULL, NULL, NULL, NULL, NULL,
+ NULL);
+ if (data == NULL)
+ dbus_connection_remove_filter(connection, message_filter,
+ NULL);
+
+ dbus_connection_unref(connection);
+
+ return TRUE;
+}
+
+static DBusHandlerResult signal_filter(DBusConnection *connection,
+ DBusMessage *message, void *user_data)
+{
+ struct filter_data *data = user_data;
+ struct filter_callback *cb;
+
+ while (data->callbacks) {
+ cb = data->callbacks->data;
+
+ if (cb->signal_func && !cb->signal_func(connection, message,
+ cb->user_data)) {
+ filter_data_remove_callback(data, cb);
+ continue;
+ }
+
+ /* Check if the watch was removed/freed by the callback
+ * function */
+ if (!g_slist_find(data->callbacks, cb))
+ continue;
+
+ data->callbacks = g_slist_remove(data->callbacks, cb);
+ data->processed = g_slist_append(data->processed, cb);
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static void update_name_cache(const char *name, const char *owner)
+{
+ GSList *l;
+
+ for (l = listeners; l != NULL; l = l->next) {
+ struct filter_data *data = l->data;
+
+ if (g_strcmp0(data->name, name) != 0)
+ continue;
+
+ g_free(data->owner);
+ data->owner = g_strdup(owner);
+ }
+}
+
+static const char *check_name_cache(const char *name)
+{
+ GSList *l;
+
+ for (l = listeners; l != NULL; l = l->next) {
+ struct filter_data *data = l->data;
+
+ if (g_strcmp0(data->name, name) != 0)
+ continue;
+
+ return data->owner;
+ }
+
+ return NULL;
+}
+
+static DBusHandlerResult service_filter(DBusConnection *connection,
+ DBusMessage *message, void *user_data)
+{
+ struct filter_data *data = user_data;
+ struct filter_callback *cb;
+ char *name, *old, *new;
+
+ if (!dbus_message_get_args(message, NULL,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_STRING, &old,
+ DBUS_TYPE_STRING, &new,
+ DBUS_TYPE_INVALID)) {
+ error("Invalid arguments for NameOwnerChanged signal");
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ update_name_cache(name, new);
+
+ while (data->callbacks) {
+ cb = data->callbacks->data;
+
+ if (*new == '\0') {
+ if (cb->disc_func)
+ cb->disc_func(connection, cb->user_data);
+ } else {
+ if (cb->conn_func)
+ cb->conn_func(connection, cb->user_data);
+ }
+
+ /* Check if the watch was removed/freed by the callback
+ * function */
+ if (!g_slist_find(data->callbacks, cb))
+ continue;
+
+ /* Only auto remove if it is a bus name watch */
+ if (data->argument[0] == ':' &&
+ (cb->conn_func == NULL || cb->disc_func == NULL)) {
+ filter_data_remove_callback(data, cb);
+ continue;
+ }
+
+ data->callbacks = g_slist_remove(data->callbacks, cb);
+ data->processed = g_slist_append(data->processed, cb);
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+
+static DBusHandlerResult message_filter(DBusConnection *connection,
+ DBusMessage *message, void *user_data)
+{
+ struct filter_data *data;
+ const char *sender, *path, *iface, *member, *arg = NULL;
+
+ /* Only filter signals */
+ if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ sender = dbus_message_get_sender(message);
+ path = dbus_message_get_path(message);
+ iface = dbus_message_get_interface(message);
+ member = dbus_message_get_member(message);
+ dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg, DBUS_TYPE_INVALID);
+
+ /* Sender is always bus name */
+ data = filter_data_find(connection, NULL, sender, path, iface, member,
+ arg);
+ if (data == NULL) {
+ error("Got %s.%s signal which has no listeners", iface, member);
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ if (data->handle_func) {
+ data->lock = TRUE;
+
+ data->handle_func(connection, message, data);
+
+ data->callbacks = data->processed;
+ data->processed = NULL;
+ data->lock = FALSE;
+ }
+
+ if (data->callbacks)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ remove_match(data);
+
+ listeners = g_slist_remove(listeners, data);
+ filter_data_free(data);
+
+ /* Remove filter if there no listener left for the connection */
+ data = filter_data_find(connection, NULL, NULL, NULL, NULL, NULL,
+ NULL);
+ if (data == NULL)
+ dbus_connection_remove_filter(connection, message_filter,
+ NULL);
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static gboolean update_service(void *user_data)
+{
+ struct service_data *data = user_data;
+ struct filter_callback *cb = data->callback;
+
+ update_name_cache(data->name, data->owner);
+ if (cb->conn_func)
+ cb->conn_func(data->conn, cb->user_data);
+
+ service_data_free(data);
+
+ return FALSE;
+}
+
+static void service_reply(DBusPendingCall *call, void *user_data)
+{
+ struct service_data *data = user_data;
+ DBusMessage *reply;
+ DBusError err;
+
+ reply = dbus_pending_call_steal_reply(call);
+ if (reply == NULL)
+ return;
+
+ dbus_error_init(&err);
+
+ if (dbus_set_error_from_message(&err, reply))
+ goto fail;
+
+ if (dbus_message_get_args(reply, &err,
+ DBUS_TYPE_STRING, &data->owner,
+ DBUS_TYPE_INVALID) == FALSE)
+ goto fail;
+
+ update_service(data);
+
+ goto done;
+
+fail:
+ error("%s", err.message);
+ dbus_error_free(&err);
+ service_data_free(data);
+done:
+ dbus_message_unref(reply);
+}
+
+static void check_service(DBusConnection *connection,
+ const char *name,
+ struct filter_callback *callback)
+{
+ DBusMessage *message;
+ struct service_data *data;
+
+ data = g_try_malloc0(sizeof(*data));
+ if (data == NULL) {
+ error("Can't allocate data structure");
+ return;
+ }
+
+ data->conn = dbus_connection_ref(connection);
+ data->name = g_strdup(name);
+ data->callback = callback;
+ callback->data = data;
+
+ data->owner = check_name_cache(name);
+ if (data->owner != NULL) {
+ data->id = g_idle_add(update_service, data);
+ return;
+ }
+
+ message = dbus_message_new_method_call(DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "GetNameOwner");
+ if (message == NULL) {
+ error("Can't allocate new message");
+ g_free(data);
+ return;
+ }
+
+ dbus_message_append_args(message, DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID);
+
+ if (dbus_connection_send_with_reply(connection, message,
+ &data->call, -1) == FALSE) {
+ error("Failed to execute method call");
+ g_free(data);
+ goto done;
+ }
+
+ if (data->call == NULL) {
+ error("D-Bus connection not available");
+ g_free(data);
+ goto done;
+ }
+
+ dbus_pending_call_set_notify(data->call, service_reply, data, NULL);
+
+done:
+ dbus_message_unref(message);
+}
+
+guint g_dbus_add_service_watch(DBusConnection *connection, const char *name,
+ GDBusWatchFunction connect,
+ GDBusWatchFunction disconnect,
+ void *user_data, GDBusDestroyFunction destroy)
+{
+ struct filter_data *data;
+ struct filter_callback *cb;
+
+ if (name == NULL)
+ return 0;
+
+ data = filter_data_get(connection, service_filter, NULL, NULL,
+ DBUS_INTERFACE_DBUS, "NameOwnerChanged",
+ name);
+ if (data == NULL)
+ return 0;
+
+ cb = filter_data_add_callback(data, connect, disconnect, NULL, NULL,
+ user_data);
+ if (cb == NULL)
+ return 0;
+
+ if (connect)
+ check_service(connection, name, cb);
+
+ return cb->id;
+}
+
+guint g_dbus_add_disconnect_watch(DBusConnection *connection, const char *name,
+ GDBusWatchFunction func,
+ void *user_data, GDBusDestroyFunction destroy)
+{
+ return g_dbus_add_service_watch(connection, name, NULL, func,
+ user_data, destroy);
+}
+
+guint g_dbus_add_signal_watch(DBusConnection *connection,
+ const char *sender, const char *path,
+ const char *interface, const char *member,
+ GDBusSignalFunction function, void *user_data,
+ GDBusDestroyFunction destroy)
+{
+ struct filter_data *data;
+ struct filter_callback *cb;
+
+ data = filter_data_get(connection, signal_filter, sender, path,
+ interface, member, NULL);
+ if (data == NULL)
+ return 0;
+
+ cb = filter_data_add_callback(data, NULL, NULL, function, destroy,
+ user_data);
+ if (cb == NULL)
+ return 0;
+
+ if (data->name != NULL && data->name_watch == 0)
+ data->name_watch = g_dbus_add_service_watch(connection,
+ data->name, NULL,
+ NULL, NULL, NULL);
+
+ return cb->id;
+}
+
+gboolean g_dbus_remove_watch(DBusConnection *connection, guint id)
+{
+ struct filter_data *data;
+ struct filter_callback *cb;
+ GSList *ldata;
+
+ if (id == 0)
+ return FALSE;
+
+ for (ldata = listeners; ldata; ldata = ldata->next) {
+ data = ldata->data;
+
+ cb = filter_data_find_callback(data, id);
+ if (cb) {
+ filter_data_remove_callback(data, cb);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+void g_dbus_remove_all_watches(DBusConnection *connection)
+{
+ struct filter_data *data;
+
+ while ((data = filter_data_find(connection, NULL, NULL, NULL, NULL,
+ NULL, NULL))) {
+ listeners = g_slist_remove(listeners, data);
+ filter_data_call_and_free(data);
+ }
+
+ dbus_connection_remove_filter(connection, message_filter, NULL);
+}
diff --git a/health/hdp.c b/health/hdp.c
new file mode 100644
index 0000000..7fed483
--- /dev/null
+++ b/health/hdp.c
@@ -0,0 +1,2223 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ * Authors:
+ * Santiago Carot Nemesio <sancane at gmail.com>
+ * Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
+ *
+ * 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
+ *
+ */
+
+#include <gdbus.h>
+
+#include "log.h"
+#include "error.h"
+#include <stdlib.h>
+#include <stdint.h>
+#include <hdp_types.h>
+#include <hdp_util.h>
+#include <adapter.h>
+#include <device.h>
+#include <hdp.h>
+#include <mcap.h>
+#include <btio.h>
+#include <mcap_lib.h>
+#include <l2cap.h>
+#include <sdpd.h>
+#include "../src/dbus-common.h"
+#include <unistd.h>
+
+#ifndef DBUS_TYPE_UNIX_FD
+ #define DBUS_TYPE_UNIX_FD -1
+#endif
+
+#define ECHO_TIMEOUT 1 /* second */
+#define HDP_ECHO_LEN 15
+
+static DBusConnection *connection = NULL;
+
+static GSList *applications = NULL;
+static GSList *devices = NULL;
+static uint8_t next_app_id = HDP_MDEP_INITIAL;
+
+static GSList *adapters;
+
+static gboolean update_adapter(struct hdp_adapter *adapter);
+static struct hdp_device *create_health_device(DBusConnection *conn,
+ struct btd_device *device);
+static void free_echo_data(struct hdp_echo_data *edata);
+
+struct hdp_create_dc {
+ DBusConnection *conn;
+ DBusMessage *msg;
+ struct hdp_application *app;
+ struct hdp_device *dev;
+ uint8_t config;
+ uint8_t mdep;
+ guint ref;
+ mcap_mdl_operation_cb cb;
+};
+
+struct hdp_tmp_dc_data {
+ DBusConnection *conn;
+ DBusMessage *msg;
+ struct hdp_channel *hdp_chann;
+ guint ref;
+ mcap_mdl_operation_cb cb;
+};
+
+struct hdp_echo_data {
+ gboolean echo_done; /* Is a echo was already done */
+ gpointer buf; /* echo packet sent */
+ uint tid; /* echo timeout */
+};
+
+static struct hdp_channel *hdp_channel_ref(struct hdp_channel *chan)
+{
+ if (!chan)
+ return NULL;
+
+ chan->ref++;
+
+ DBG("health_channel_ref(%p): ref=%d", chan, chan->ref);
+ return chan;
+}
+
+static void free_health_channel(struct hdp_channel *chan)
+{
+ if (chan->mdep == HDP_MDEP_ECHO) {
+ free_echo_data(chan->edata);
+ chan->edata = NULL;
+ }
+
+ mcap_mdl_unref(chan->mdl);
+ hdp_application_unref(chan->app);
+ health_device_unref(chan->dev);
+ g_free(chan->path);
+ g_free(chan);
+}
+
+static void hdp_channel_unref(struct hdp_channel *chan)
+{
+ if (!chan)
+ return;
+
+ chan->ref --;
+ DBG("health_channel_unref(%p): ref=%d", chan, chan->ref);
+
+ if (chan->ref > 0)
+ return;
+
+ free_health_channel(chan);
+}
+
+static void free_hdp_create_dc(struct hdp_create_dc *dc_data)
+{
+ dbus_message_unref(dc_data->msg);
+ dbus_connection_unref(dc_data->conn);
+ hdp_application_unref(dc_data->app);
+ health_device_unref(dc_data->dev);
+
+ g_free(dc_data);
+}
+
+static struct hdp_create_dc *hdp_create_data_ref(struct hdp_create_dc *dc_data)
+{
+ dc_data->ref++;
+
+ DBG("hdp_create_data_ref(%p): ref=%d", dc_data, dc_data->ref);
+
+ return dc_data;
+}
+
+static void hdp_create_data_unref(struct hdp_create_dc *dc_data)
+{
+ dc_data->ref--;
+
+ DBG("hdp_create_data_unref(%p): ref=%d", dc_data, dc_data->ref);
+
+ if (dc_data->ref > 0)
+ return;
+
+ free_hdp_create_dc(dc_data);
+}
+
+static void free_hdp_conn_dc(struct hdp_tmp_dc_data *data)
+{
+ dbus_message_unref(data->msg);
+ dbus_connection_unref(data->conn);
+ hdp_channel_unref(data->hdp_chann);
+
+ g_free(data);
+}
+
+static struct hdp_tmp_dc_data *hdp_tmp_dc_data_ref(struct hdp_tmp_dc_data *data)
+{
+ data->ref++;
+
+ DBG("hdp_conn_data_ref(%p): ref=%d", data, data->ref);
+
+ return data;
+}
+
+static void hdp_tmp_dc_data_unref(struct hdp_tmp_dc_data *data)
+{
+ data->ref--;
+
+ DBG("hdp_conn_data_unref(%p): ref=%d", data, data->ref);
+
+ if (data->ref > 0)
+ return;
+
+ free_hdp_conn_dc(data);
+}
+
+static int cmp_app_id(gconstpointer a, gconstpointer b)
+{
+ const struct hdp_application *app = a;
+ const uint8_t *id = b;
+
+ return app->id - *id;
+}
+
+static int cmp_adapter(gconstpointer a, gconstpointer b)
+{
+ const struct hdp_adapter *hdp_adapter = a;
+ const struct btd_adapter *adapter = b;
+
+ if (hdp_adapter->btd_adapter == adapter)
+ return 0;
+
+ return -1;
+}
+
+static int cmp_device(gconstpointer a, gconstpointer b)
+{
+ const struct hdp_device *hdp_device = a;
+ const struct btd_device *device = b;
+
+ if (hdp_device->dev == device)
+ return 0;
+
+ return -1;
+}
+
+static gint cmp_dev_addr(gconstpointer a, gconstpointer dst)
+{
+ const struct hdp_device *device = a;
+ bdaddr_t addr;
+
+ device_get_address(device->dev, &addr);
+ return bacmp(&addr, dst);
+}
+
+static gint cmp_dev_mcl(gconstpointer a, gconstpointer mcl)
+{
+ const struct hdp_device *device = a;
+
+ if (mcl == device->mcl)
+ return 0;
+ return -1;
+}
+
+static gint cmp_chan_mdlid(gconstpointer a, gconstpointer b)
+{
+ const struct hdp_channel *chan = a;
+ const uint16_t *mdlid = b;
+
+ return chan->mdlid - *mdlid;
+}
+
+static gint cmp_chan_path(gconstpointer a, gconstpointer b)
+{
+ const struct hdp_channel *chan = a;
+ const char *path = b;
+
+ return g_ascii_strcasecmp(chan->path, path);
+}
+
+static gint cmp_chan_mdl(gconstpointer a, gconstpointer mdl)
+{
+ const struct hdp_channel *chan = a;
+
+ if (chan->mdl == mdl)
+ return 0;
+ return -1;
+}
+
+static uint8_t get_app_id()
+{
+ uint8_t id = next_app_id;
+
+ do {
+ GSList *l = g_slist_find_custom(applications, &id, cmp_app_id);
+
+ if (!l) {
+ next_app_id = (id % HDP_MDEP_FINAL) + 1;
+ return id;
+ } else
+ id = (id % HDP_MDEP_FINAL) + 1;
+ } while (id != next_app_id);
+
+ /* No more ids available */
+ return 0;
+}
+
+static int cmp_app(gconstpointer a, gconstpointer b)
+{
+ const struct hdp_application *app = a;
+
+ return g_strcmp0(app->path, b);
+}
+
+static gboolean set_app_path(struct hdp_application *app)
+{
+ app->id = get_app_id();
+ if (!app->id)
+ return FALSE;
+ app->path = g_strdup_printf(MANAGER_PATH "/health_app_%d", app->id);
+
+ return TRUE;
+};
+
+static void device_unref_mcl(struct hdp_device *hdp_device)
+{
+ if (!hdp_device->mcl)
+ return;
+
+ mcap_close_mcl(hdp_device->mcl, FALSE);
+ mcap_mcl_unref(hdp_device->mcl);
+ hdp_device->mcl = NULL;
+ hdp_device->mcl_conn = FALSE;
+}
+
+static void free_health_device(struct hdp_device *device)
+{
+ if (device->conn) {
+ dbus_connection_unref(device->conn);
+ device->conn = NULL;
+ }
+
+ if (device->dev) {
+ btd_device_unref(device->dev);
+ device->dev = NULL;
+ }
+
+ device_unref_mcl(device);
+
+ g_free(device);
+}
+
+static void remove_application(struct hdp_application *app)
+{
+ DBG("Application %s deleted", app->path);
+ hdp_application_unref(app);
+
+ g_slist_foreach(adapters, (GFunc) update_adapter, NULL);
+}
+
+static void client_disconnected(DBusConnection *conn, void *user_data)
+{
+ struct hdp_application *app = user_data;
+
+ DBG("Client disconnected from the bus, deleting hdp application");
+ applications = g_slist_remove(applications, app);
+
+ app->dbus_watcher = 0; /* Watcher shouldn't be freed in this case */
+ remove_application(app);
+}
+
+static DBusMessage *manager_create_application(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct hdp_application *app;
+ const char *name;
+ DBusMessageIter iter;
+ GError *err = NULL;
+
+ dbus_message_iter_init(msg, &iter);
+ app = hdp_get_app_config(&iter, &err);
+ if (err) {
+ g_error_free(err);
+ return btd_error_invalid_args(msg);
+ }
+
+ name = dbus_message_get_sender(msg);
+ if (!name) {
+ hdp_application_unref(app);
+ return g_dbus_create_error(msg,
+ ERROR_INTERFACE ".HealthError",
+ "Can't get sender name");
+ }
+
+ if (!set_app_path(app)) {
+ hdp_application_unref(app);
+ return g_dbus_create_error(msg,
+ ERROR_INTERFACE ".HealthError",
+ "Can't get a valid id for the application");
+ }
+
+ app->oname = g_strdup(name);
+ app->conn = dbus_connection_ref(conn);
+
+ applications = g_slist_prepend(applications, app);
+
+ app->dbus_watcher = g_dbus_add_disconnect_watch(conn, name,
+ client_disconnected, app, NULL);
+ g_slist_foreach(adapters, (GFunc) update_adapter, NULL);
+
+ DBG("Health application created with id %s", app->path);
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_OBJECT_PATH, &app->path,
+ DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *manager_destroy_application(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ const char *path;
+ struct hdp_application *app;
+ GSList *l;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ l = g_slist_find_custom(applications, path, cmp_app);
+
+ if (!l)
+ return g_dbus_create_error(msg,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call, "
+ "no such application");
+
+ app = l->data;
+ applications = g_slist_remove(applications, app);
+
+ remove_application(app);
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static void manager_path_unregister(gpointer data)
+{
+ g_slist_foreach(applications, (GFunc) hdp_application_unref, NULL);
+
+ g_slist_free(applications);
+ applications = NULL;
+
+ g_slist_foreach(adapters, (GFunc) update_adapter, NULL);
+}
+
+static GDBusMethodTable health_manager_methods[] = {
+ {"CreateApplication", "a{sv}", "o", manager_create_application},
+ {"DestroyApplication", "o", "", manager_destroy_application},
+ { NULL }
+};
+
+static DBusMessage *channel_get_properties(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct hdp_channel *chan = user_data;
+ DBusMessageIter iter, dict;
+ DBusMessage *reply;
+ const char *path;
+ char *type;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ path = device_get_path(chan->dev->dev);
+ dict_append_entry(&dict, "Device", DBUS_TYPE_OBJECT_PATH, &path);
+
+ path = chan->app->path;
+ dict_append_entry(&dict, "Application", DBUS_TYPE_OBJECT_PATH, &path);
+
+ if (chan->config == HDP_RELIABLE_DC)
+ type = g_strdup("Reliable");
+ else
+ type = g_strdup("Streaming");
+
+ dict_append_entry(&dict, "Type", DBUS_TYPE_STRING, &type);
+
+ g_free(type);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static void hdp_tmp_dc_data_destroy(gpointer data)
+{
+ struct hdp_tmp_dc_data *hdp_conn = data;
+
+ hdp_tmp_dc_data_unref(hdp_conn);
+}
+
+static void abort_mdl_cb(GError *err, gpointer data)
+{
+ if (err)
+ error("Aborting error: %s", err->message);
+}
+
+static void hdp_mdl_reconn_cb(struct mcap_mdl *mdl, GError *err, gpointer data)
+{
+ struct hdp_tmp_dc_data *dc_data = data;
+ DBusMessage *reply;
+ int fd;
+
+ if (err) {
+ struct hdp_channel *chan = dc_data->hdp_chann;
+ GError *gerr = NULL;
+
+ error("%s", err->message);
+ reply = g_dbus_create_error(dc_data->msg,
+ ERROR_INTERFACE ".HealthError",
+ "Cannot reconnect: %s", err->message);
+ g_dbus_send_message(dc_data->conn, reply);
+
+ /* Send abort request because remote side */
+ /* is now in PENDING state */
+ if (!mcap_mdl_abort(chan->mdl, abort_mdl_cb, NULL, NULL,
+ &gerr)) {
+ error("%s", gerr->message);
+ g_error_free(gerr);
+ }
+ return;
+ }
+
+ fd = mcap_mdl_get_fd(dc_data->hdp_chann->mdl);
+ if (fd < 0) {
+ reply = g_dbus_create_error(dc_data->msg,
+ ERROR_INTERFACE ".HealthError",
+ "Cannot get file descriptor");
+ g_dbus_send_message(dc_data->conn, reply);
+ return;
+ }
+
+ reply = g_dbus_create_reply(dc_data->msg, DBUS_TYPE_UNIX_FD,
+ &fd, DBUS_TYPE_INVALID);
+ g_dbus_send_message(dc_data->conn, reply);
+
+ g_dbus_emit_signal(dc_data->conn,
+ device_get_path(dc_data->hdp_chann->dev->dev),
+ HEALTH_DEVICE, "ChannelConnected",
+ DBUS_TYPE_OBJECT_PATH, &dc_data->hdp_chann->path,
+ DBUS_TYPE_INVALID);
+}
+
+static void hdp_get_dcpsm_cb(uint16_t dcpsm, gpointer user_data, GError *err)
+{
+ struct hdp_tmp_dc_data *hdp_conn = user_data;
+ struct hdp_channel *hdp_chann = hdp_conn->hdp_chann;
+ GError *gerr = NULL;
+ uint8_t mode;
+
+ if (err) {
+ hdp_conn->cb(hdp_chann->mdl, err, hdp_conn);
+ return;
+ }
+
+ if (hdp_chann->config == HDP_RELIABLE_DC)
+ mode = L2CAP_MODE_ERTM;
+ else
+ mode = L2CAP_MODE_STREAMING;
+
+ if (mcap_connect_mdl(hdp_chann->mdl, mode, dcpsm, hdp_conn->cb,
+ hdp_tmp_dc_data_ref(hdp_conn),
+ hdp_tmp_dc_data_destroy, &gerr))
+ return;
+
+ hdp_tmp_dc_data_unref(hdp_conn);
+ hdp_conn->cb(hdp_chann->mdl, err, hdp_conn);
+ g_error_free(gerr);
+ gerr = NULL;
+}
+
+static void device_reconnect_mdl_cb(struct mcap_mdl *mdl, GError *err,
+ gpointer data)
+{
+ struct hdp_tmp_dc_data *dc_data = data;
+ GError *gerr = NULL;
+ DBusMessage *reply;
+
+ if (err) {
+ reply = g_dbus_create_error(dc_data->msg,
+ ERROR_INTERFACE ".HealthError",
+ "Cannot reconnect: %s", err->message);
+ g_dbus_send_message(dc_data->conn, reply);
+ return;
+ }
+
+ dc_data->cb = hdp_mdl_reconn_cb;
+
+ if (hdp_get_dcpsm(dc_data->hdp_chann->dev, hdp_get_dcpsm_cb,
+ hdp_tmp_dc_data_ref(dc_data),
+ hdp_tmp_dc_data_destroy, &gerr))
+ return;
+
+ error("%s", gerr->message);
+
+ reply = g_dbus_create_error(dc_data->msg,
+ ERROR_INTERFACE ".HealthError",
+ "Cannot reconnect: %s", gerr->message);
+ g_dbus_send_message(dc_data->conn, reply);
+ hdp_tmp_dc_data_unref(dc_data);
+ g_error_free(gerr);
+ gerr = NULL;
+
+ /* Send abort request because remote side is now in PENDING state */
+ if (!mcap_mdl_abort(mdl, abort_mdl_cb, NULL, NULL, &gerr)) {
+ error("%s", gerr->message);
+ g_error_free(gerr);
+ }
+}
+
+static DBusMessage *channel_acquire_continue(struct hdp_tmp_dc_data *data,
+ GError *err)
+{
+ DBusMessage *reply;
+ GError *gerr = NULL;
+ int fd;
+
+ if (err) {
+ return g_dbus_create_error(data->msg,
+ ERROR_INTERFACE ".HealthError",
+ "%s", err->message);
+ }
+
+ fd = mcap_mdl_get_fd(data->hdp_chann->mdl);
+ if (fd >= 0)
+ return g_dbus_create_reply(data->msg, DBUS_TYPE_UNIX_FD, &fd,
+ DBUS_TYPE_INVALID);
+
+ hdp_tmp_dc_data_ref(data);
+ if (mcap_reconnect_mdl(data->hdp_chann->mdl, device_reconnect_mdl_cb,
+ data, hdp_tmp_dc_data_destroy, &gerr))
+ return NULL;
+
+ hdp_tmp_dc_data_unref(data);
+ reply = g_dbus_create_error(data->msg, ERROR_INTERFACE ".HealthError",
+ "Cannot reconnect: %s", gerr->message);
+ g_error_free(gerr);
+
+ return reply;
+}
+
+static void channel_acquire_cb(gpointer data, GError *err)
+{
+ struct hdp_tmp_dc_data *dc_data = data;
+ DBusMessage *reply;
+
+ reply = channel_acquire_continue(data, err);
+
+ if (reply)
+ g_dbus_send_message(dc_data->conn, reply);
+}
+
+static DBusMessage *channel_acquire(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct hdp_channel *chan = user_data;
+ struct hdp_tmp_dc_data *dc_data;
+ GError *gerr = NULL;
+ DBusMessage *reply;
+
+ dc_data = g_new0(struct hdp_tmp_dc_data, 1);
+ dc_data->conn = dbus_connection_ref(conn);
+ dc_data->msg = dbus_message_ref(msg);
+ dc_data->hdp_chann = hdp_channel_ref(chan);
+
+ if (chan->dev->mcl_conn) {
+ reply = channel_acquire_continue(hdp_tmp_dc_data_ref(dc_data),
+ NULL);
+ hdp_tmp_dc_data_unref(dc_data);
+ return reply;
+ }
+
+ if (hdp_establish_mcl(chan->dev, channel_acquire_cb,
+ hdp_tmp_dc_data_ref(dc_data),
+ hdp_tmp_dc_data_destroy, &gerr))
+ return NULL;
+
+ reply = g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError",
+ "%s", gerr->message);
+ hdp_tmp_dc_data_unref(dc_data);
+ g_error_free(gerr);
+
+ return reply;
+}
+
+static void close_mdl(struct hdp_channel *hdp_chann)
+{
+ int fd;
+
+ fd = mcap_mdl_get_fd(hdp_chann->mdl);
+ if (fd < 0)
+ return;
+
+ close(fd);
+}
+
+static DBusMessage *channel_release(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct hdp_channel *hdp_chann = user_data;
+
+ close_mdl(hdp_chann);
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static void free_echo_data(struct hdp_echo_data *edata)
+{
+ if (!edata)
+ return;
+
+ if (edata->tid)
+ g_source_remove(edata->tid);
+
+ if (edata->buf)
+ g_free(edata->buf);
+
+
+ g_free(edata);
+}
+
+static void health_channel_destroy(void *data)
+{
+ struct hdp_channel *hdp_chan = data;
+ struct hdp_device *dev = hdp_chan->dev;
+
+ DBG("Destroy Health Channel %s", hdp_chan->path);
+ if (!g_slist_find(dev->channels, hdp_chan))
+ goto end;
+
+ dev->channels = g_slist_remove(dev->channels, hdp_chan);
+
+ if (hdp_chan->mdep != HDP_MDEP_ECHO)
+ g_dbus_emit_signal(dev->conn, device_get_path(dev->dev),
+ HEALTH_DEVICE, "ChannelDeleted",
+ DBUS_TYPE_OBJECT_PATH, &hdp_chan->path,
+ DBUS_TYPE_INVALID);
+
+ if (hdp_chan == dev->fr) {
+ char *empty_path;
+
+ hdp_channel_unref(dev->fr);
+ dev->fr = NULL;
+ empty_path = "/";
+ emit_property_changed(dev->conn, device_get_path(dev->dev),
+ HEALTH_DEVICE, "MainChannel",
+ DBUS_TYPE_OBJECT_PATH, &empty_path);
+ }
+
+end:
+ hdp_channel_unref(hdp_chan);
+}
+
+static GDBusMethodTable health_channels_methods[] = {
+ {"GetProperties","", "a{sv}", channel_get_properties },
+ {"Acquire", "", "h", channel_acquire,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ {"Release", "", "", channel_release },
+ { NULL }
+};
+
+static struct hdp_channel *create_channel(struct hdp_device *dev,
+ uint8_t config,
+ struct mcap_mdl *mdl,
+ uint16_t mdlid,
+ struct hdp_application *app,
+ GError **err)
+{
+ struct hdp_channel *hdp_chann;
+
+ if (!dev)
+ return NULL;
+
+ hdp_chann = g_new0(struct hdp_channel, 1);
+ hdp_chann->config = config;
+ hdp_chann->dev = health_device_ref(dev);
+ hdp_chann->mdlid = mdlid;
+
+ if (mdl)
+ hdp_chann->mdl = mcap_mdl_ref(mdl);
+
+ if (app) {
+ hdp_chann->mdep = app->id;
+ hdp_chann->app = hdp_application_ref(app);
+ } else
+ hdp_chann->edata = g_new0(struct hdp_echo_data, 1);
+
+ hdp_chann->path = g_strdup_printf("%s/chan%d",
+ device_get_path(hdp_chann->dev->dev),
+ hdp_chann->mdlid);
+
+ dev->channels = g_slist_append(dev->channels,
+ hdp_channel_ref(hdp_chann));
+
+ if (hdp_chann->mdep == HDP_MDEP_ECHO)
+ return hdp_channel_ref(hdp_chann);
+
+ if (!g_dbus_register_interface(dev->conn, hdp_chann->path,
+ HEALTH_CHANNEL,
+ health_channels_methods, NULL, NULL,
+ hdp_chann, health_channel_destroy)) {
+ g_set_error(err, HDP_ERROR, HDP_UNSPECIFIED_ERROR,
+ "Can't register the channel interface");
+ health_channel_destroy(hdp_chann);
+ return NULL;
+ }
+
+ return hdp_channel_ref(hdp_chann);
+}
+
+static void remove_channels(struct hdp_device *dev)
+{
+ struct hdp_channel *chan;
+ char *path;
+
+ while (dev->channels) {
+ chan = dev->channels->data;
+
+ path = g_strdup(chan->path);
+ if (!g_dbus_unregister_interface(dev->conn, path,
+ HEALTH_CHANNEL))
+ health_channel_destroy(chan);
+ g_free(path);
+ }
+}
+
+static void close_device_con(struct hdp_device *dev, gboolean cache)
+{
+ if (!dev->mcl)
+ return;
+
+ mcap_close_mcl(dev->mcl, cache);
+ dev->mcl_conn = FALSE;
+
+ if (cache)
+ return;
+
+ device_unref_mcl(dev);
+ remove_channels(dev);
+
+ if (!dev->sdp_present) {
+ const char *path;
+
+ path = device_get_path(dev->dev);
+ g_dbus_unregister_interface(dev->conn, path, HEALTH_DEVICE);
+ }
+}
+
+static int send_echo_data(int sock, const void *buf, uint32_t size)
+{
+ const uint8_t *buf_b = buf;
+ uint32_t sent = 0;
+
+ while (sent < size) {
+ int n = write(sock, buf_b + sent, size - sent);
+ if (n < 0)
+ return -1;
+ sent += n;
+ }
+
+ return 0;
+}
+
+static gboolean serve_echo(GIOChannel *io_chan, GIOCondition cond,
+ gpointer data)
+{
+ struct hdp_channel *chan = data;
+ uint8_t buf[MCAP_DC_MTU];
+ int fd, len;
+
+ if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
+ hdp_channel_unref(chan);
+ return FALSE;
+ }
+
+ if (chan->edata->echo_done)
+ goto fail;
+
+ chan->edata->echo_done = TRUE;
+
+ fd = g_io_channel_unix_get_fd(io_chan);
+ len = read(fd, buf, sizeof(buf));
+
+ if (send_echo_data(fd, buf, len) >= 0)
+ return TRUE;
+
+fail:
+ close_device_con(chan->dev, FALSE);
+ hdp_channel_unref(chan);
+ return FALSE;
+}
+
+static gboolean check_channel_conf(struct hdp_channel *chan)
+{
+ GError *err = NULL;
+ GIOChannel *io;
+ uint8_t mode;
+ uint16_t imtu, omtu;
+ int fd;
+
+ fd = mcap_mdl_get_fd(chan->mdl);
+ if (fd < 0)
+ return FALSE;
+ io = g_io_channel_unix_new(fd);
+
+ if (!bt_io_get(io, BT_IO_L2CAP, &err,
+ BT_IO_OPT_MODE, &mode,
+ BT_IO_OPT_IMTU, &imtu,
+ BT_IO_OPT_OMTU, &omtu,
+ BT_IO_OPT_INVALID)) {
+ error("Error: %s", err->message);
+ g_io_channel_unref(io);
+ g_error_free(err);
+ return FALSE;
+ }
+
+ g_io_channel_unref(io);
+
+ switch (chan->config) {
+ case HDP_RELIABLE_DC:
+ if (mode != L2CAP_MODE_ERTM)
+ return FALSE;
+ break;
+ case HDP_STREAMING_DC:
+ if (mode != L2CAP_MODE_STREAMING)
+ return FALSE;
+ break;
+ default:
+ error("Error: Connected with unknown configuration");
+ return FALSE;
+ }
+
+ DBG("MDL imtu %d omtu %d Channel imtu %d omtu %d", imtu, omtu,
+ chan->imtu, chan->omtu);
+
+ if (!chan->imtu)
+ chan->imtu = imtu;
+ if (!chan->omtu)
+ chan->omtu = omtu;
+
+ if (chan->imtu != imtu || chan->omtu != omtu)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void hdp_mcap_mdl_connected_cb(struct mcap_mdl *mdl, void *data)
+{
+ struct hdp_device *dev = data;
+ struct hdp_channel *chan;
+
+ DBG("hdp_mcap_mdl_connected_cb");
+ if (!dev->ndc)
+ return;
+
+ chan = dev->ndc;
+ if (!chan->mdl)
+ chan->mdl = mcap_mdl_ref(mdl);
+
+ if (!g_slist_find(dev->channels, chan))
+ dev->channels = g_slist_prepend(dev->channels,
+ hdp_channel_ref(chan));
+
+ if (!check_channel_conf(chan)) {
+ close_mdl(chan);
+ goto end;
+ }
+
+ if (chan->mdep == HDP_MDEP_ECHO) {
+ GIOChannel *io;
+ int fd;
+
+ fd = mcap_mdl_get_fd(chan->mdl);
+ if (fd < 0)
+ goto end;
+
+ chan->edata->echo_done = FALSE;
+ io = g_io_channel_unix_new(fd);
+ g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL | G_IO_IN,
+ serve_echo, hdp_channel_ref(chan));
+ g_io_channel_unref(io);
+ goto end;
+ }
+
+ g_dbus_emit_signal(dev->conn, device_get_path(dev->dev), HEALTH_DEVICE,
+ "ChannelConnected",
+ DBUS_TYPE_OBJECT_PATH, &chan->path,
+ DBUS_TYPE_INVALID);
+
+ if (dev->fr)
+ goto end;
+
+ dev->fr = hdp_channel_ref(chan);
+
+ emit_property_changed(dev->conn, device_get_path(dev->dev),
+ HEALTH_DEVICE, "MainChannel",
+ DBUS_TYPE_OBJECT_PATH, &dev->fr->path);
+
+end:
+ hdp_channel_unref(dev->ndc);
+ dev->ndc = NULL;
+}
+
+static void hdp_mcap_mdl_closed_cb(struct mcap_mdl *mdl, void *data)
+{
+ /* struct hdp_device *dev = data; */
+
+ DBG("hdp_mcap_mdl_closed_cb");
+
+ /* Nothing to do */
+}
+
+static void hdp_mcap_mdl_deleted_cb(struct mcap_mdl *mdl, void *data)
+{
+ struct hdp_device *dev = data;
+ struct hdp_channel *chan;
+ char *path;
+ GSList *l;
+
+ DBG("hdp_mcap_mdl_deleted_cb");
+ l = g_slist_find_custom(dev->channels, mdl, cmp_chan_mdl);
+ if (!l)
+ return;
+
+ chan = l->data;
+
+ path = g_strdup(chan->path);
+ if (!g_dbus_unregister_interface(dev->conn, path, HEALTH_CHANNEL))
+ health_channel_destroy(chan);
+ g_free(path);
+}
+
+static void hdp_mcap_mdl_aborted_cb(struct mcap_mdl *mdl, void *data)
+{
+ struct hdp_device *dev = data;
+
+ DBG("hdp_mcap_mdl_aborted_cb");
+ if (!dev->ndc)
+ return;
+
+ dev->ndc->mdl = mcap_mdl_ref(mdl);
+
+ if (!g_slist_find(dev->channels, dev->ndc))
+ dev->channels = g_slist_prepend(dev->channels,
+ hdp_channel_ref(dev->ndc));
+
+ if (dev->ndc->mdep != HDP_MDEP_ECHO)
+ g_dbus_emit_signal(dev->conn, device_get_path(dev->dev),
+ HEALTH_DEVICE, "ChannelConnected",
+ DBUS_TYPE_OBJECT_PATH, &dev->ndc->path,
+ DBUS_TYPE_INVALID);
+
+ hdp_channel_unref(dev->ndc);
+ dev->ndc = NULL;
+}
+
+static uint8_t hdp2l2cap_mode(uint8_t hdp_mode)
+{
+ return hdp_mode == HDP_STREAMING_DC ? L2CAP_MODE_STREAMING :
+ L2CAP_MODE_ERTM;
+}
+
+static uint8_t hdp_mcap_mdl_conn_req_cb(struct mcap_mcl *mcl, uint8_t mdepid,
+ uint16_t mdlid, uint8_t *conf, void *data)
+{
+ struct hdp_device *dev = data;
+ struct hdp_application *app;
+ GError *err = NULL;
+ GSList *l;
+
+ DBG("Data channel request");
+
+ if (mdepid == HDP_MDEP_ECHO) {
+ switch (*conf) {
+ case HDP_NO_PREFERENCE_DC:
+ *conf = HDP_RELIABLE_DC;
+ case HDP_RELIABLE_DC:
+ break;
+ case HDP_STREAMING_DC:
+ return MCAP_CONFIGURATION_REJECTED;
+ default:
+ /* Special case defined in HDP spec 3.4. When an invalid
+ * configuration is received we shall close the MCL when
+ * we are still processing the callback. */
+ close_device_con(dev, FALSE);
+ return MCAP_CONFIGURATION_REJECTED; /* not processed */
+ }
+
+ if (!mcap_set_data_chan_mode(dev->hdp_adapter->mi,
+ L2CAP_MODE_ERTM, &err)) {
+ error("Error: %s", err->message);
+ g_error_free(err);
+ return MCAP_MDL_BUSY;
+ }
+
+ dev->ndc = create_channel(dev, *conf, NULL, mdlid, NULL, NULL);
+ if (!dev->ndc)
+ return MCAP_MDL_BUSY;
+
+ return MCAP_SUCCESS;
+ }
+
+ l = g_slist_find_custom(applications, &mdepid, cmp_app_id);
+ if (!l)
+ return MCAP_INVALID_MDEP;
+
+ app = l->data;
+
+ /* Check if is the first dc if so,
+ * only reliable configuration is allowed */
+ switch (*conf) {
+ case HDP_NO_PREFERENCE_DC:
+ if (app->role == HDP_SINK)
+ return MCAP_CONFIGURATION_REJECTED;
+ else if (dev->fr && app->chan_type_set)
+ *conf = app->chan_type;
+ else
+ *conf = HDP_RELIABLE_DC;
+ break;
+ case HDP_STREAMING_DC:
+ if (!dev->fr || app->role == HDP_SOURCE)
+ return MCAP_CONFIGURATION_REJECTED;
+ case HDP_RELIABLE_DC:
+ if (app->role == HDP_SOURCE)
+ return MCAP_CONFIGURATION_REJECTED;
+ break;
+ default:
+ /* Special case defined in HDP spec 3.4. When an invalid
+ * configuration is received we shall close the MCL when
+ * we are still processing the callback. */
+ close_device_con(dev, FALSE);
+ return MCAP_CONFIGURATION_REJECTED; /* not processed */
+ }
+
+ l = g_slist_find_custom(dev->channels, &mdlid, cmp_chan_mdlid);
+ if (l) {
+ struct hdp_channel *chan = l->data;
+ char *path;
+
+ path = g_strdup(chan->path);
+ g_dbus_unregister_interface(dev->conn, path, HEALTH_CHANNEL);
+ g_free(path);
+ }
+
+ if (!mcap_set_data_chan_mode(dev->hdp_adapter->mi,
+ hdp2l2cap_mode(*conf), &err)) {
+ error("Error: %s", err->message);
+ g_error_free(err);
+ return MCAP_MDL_BUSY;
+ }
+
+ dev->ndc = create_channel(dev, *conf, NULL, mdlid, app, NULL);
+ if (!dev->ndc)
+ return MCAP_MDL_BUSY;
+
+ return MCAP_SUCCESS;
+}
+
+static uint8_t hdp_mcap_mdl_reconn_req_cb(struct mcap_mdl *mdl, void *data)
+{
+ struct hdp_device *dev = data;
+ struct hdp_channel *chan;
+ GError *err = NULL;
+ GSList *l;
+
+ l = g_slist_find_custom(dev->channels, mdl, cmp_chan_mdl);
+ if (!l)
+ return MCAP_INVALID_MDL;
+
+ chan = l->data;
+
+ if (!dev->fr && (chan->config != HDP_RELIABLE_DC) &&
+ (chan->mdep != HDP_MDEP_ECHO))
+ return MCAP_UNSPECIFIED_ERROR;
+
+ if (!mcap_set_data_chan_mode(dev->hdp_adapter->mi,
+ hdp2l2cap_mode(chan->config), &err)) {
+ error("Error: %s", err->message);
+ g_error_free(err);
+ return MCAP_MDL_BUSY;
+ }
+
+ dev->ndc = hdp_channel_ref(chan);
+
+ return MCAP_SUCCESS;
+}
+
+gboolean hdp_set_mcl_cb(struct hdp_device *device, GError **err)
+{
+ gboolean ret;
+
+ if (!device->mcl)
+ return FALSE;
+
+ ret = mcap_mcl_set_cb(device->mcl, device, err,
+ MCAP_MDL_CB_CONNECTED, hdp_mcap_mdl_connected_cb,
+ MCAP_MDL_CB_CLOSED, hdp_mcap_mdl_closed_cb,
+ MCAP_MDL_CB_DELETED, hdp_mcap_mdl_deleted_cb,
+ MCAP_MDL_CB_ABORTED, hdp_mcap_mdl_aborted_cb,
+ MCAP_MDL_CB_REMOTE_CONN_REQ, hdp_mcap_mdl_conn_req_cb,
+ MCAP_MDL_CB_REMOTE_RECONN_REQ, hdp_mcap_mdl_reconn_req_cb,
+ MCAP_MDL_CB_INVALID);
+
+ if (ret)
+ return TRUE;
+
+ error("Can't set mcl callbacks, closing mcl");
+ close_device_con(device, TRUE);
+
+ return FALSE;
+}
+
+static void mcl_connected(struct mcap_mcl *mcl, gpointer data)
+{
+ struct hdp_device *hdp_device;
+ bdaddr_t addr;
+ GSList *l;
+
+ mcap_mcl_get_addr(mcl, &addr);
+ l = g_slist_find_custom(devices, &addr, cmp_dev_addr);
+ if (!l) {
+ struct hdp_adapter *hdp_adapter = data;
+ struct btd_device *device;
+ char str[18];
+
+ ba2str(&addr, str);
+ device = adapter_get_device(connection,
+ hdp_adapter->btd_adapter, str);
+ if (!device)
+ return;
+ hdp_device = create_health_device(connection, device);
+ if (!hdp_device)
+ return;
+ devices = g_slist_append(devices, hdp_device);
+ } else
+ hdp_device = l->data;
+
+ hdp_device->mcl = mcap_mcl_ref(mcl);
+ hdp_device->mcl_conn = TRUE;
+
+ DBG("New mcl connected from %s", device_get_path(hdp_device->dev));
+
+ hdp_set_mcl_cb(hdp_device, NULL);
+}
+
+static void mcl_reconnected(struct mcap_mcl *mcl, gpointer data)
+{
+ struct hdp_device *hdp_device;
+ GSList *l;
+
+ l = g_slist_find_custom(devices, mcl, cmp_dev_mcl);
+ if (!l)
+ return;
+
+ hdp_device = l->data;
+ hdp_device->mcl_conn = TRUE;
+
+ DBG("MCL reconnected %s", device_get_path(hdp_device->dev));
+
+ hdp_set_mcl_cb(hdp_device, NULL);
+}
+
+static void mcl_disconnected(struct mcap_mcl *mcl, gpointer data)
+{
+ struct hdp_device *hdp_device;
+ GSList *l;
+
+ l = g_slist_find_custom(devices, mcl, cmp_dev_mcl);
+ if (!l)
+ return;
+
+ hdp_device = l->data;
+ hdp_device->mcl_conn = FALSE;
+
+ DBG("Mcl disconnected %s", device_get_path(hdp_device->dev));
+}
+
+static void mcl_uncached(struct mcap_mcl *mcl, gpointer data)
+{
+ struct hdp_device *hdp_device;
+ const char *path;
+ GSList *l;
+
+ l = g_slist_find_custom(devices, mcl, cmp_dev_mcl);
+ if (!l)
+ return;
+
+ hdp_device = l->data;
+ device_unref_mcl(hdp_device);
+
+ if (hdp_device->sdp_present)
+ return;
+
+ /* Because remote device hasn't announced an HDP record */
+ /* the Bluetooth daemon won't notify when the device shall */
+ /* be removed. Then we have to remove the HealthDevice */
+ /* interface manually */
+ path = device_get_path(hdp_device->dev);
+ g_dbus_unregister_interface(hdp_device->conn, path, HEALTH_DEVICE);
+ DBG("Mcl uncached %s", path);
+}
+
+static void check_devices_mcl()
+{
+ struct hdp_device *dev;
+ GSList *l, *to_delete = NULL;
+
+ for (l = devices; l; l = l->next) {
+ dev = l->data;
+ device_unref_mcl(dev);
+
+ if (!dev->sdp_present)
+ to_delete = g_slist_append(to_delete, dev);
+ else
+ remove_channels(dev);
+ }
+
+ for (l = to_delete; l; l = l->next) {
+ const char *path;
+
+ path = device_get_path(dev->dev);
+ g_dbus_unregister_interface(dev->conn, path, HEALTH_DEVICE);
+ }
+
+ g_slist_free(to_delete);
+}
+
+static void release_adapter_instance(struct hdp_adapter *hdp_adapter)
+{
+ if (!hdp_adapter->mi)
+ return;
+
+ check_devices_mcl();
+ mcap_release_instance(hdp_adapter->mi);
+ mcap_instance_unref(hdp_adapter->mi);
+ hdp_adapter->mi = NULL;
+}
+
+static gboolean update_adapter(struct hdp_adapter *hdp_adapter)
+{
+ GError *err = NULL;
+ bdaddr_t addr;
+
+ if (!applications) {
+ release_adapter_instance(hdp_adapter);
+ goto update;
+ }
+
+ if (hdp_adapter->mi)
+ goto update;
+
+ adapter_get_address(hdp_adapter->btd_adapter, &addr);
+ hdp_adapter->mi = mcap_create_instance(&addr, BT_IO_SEC_MEDIUM, 0, 0,
+ mcl_connected, mcl_reconnected,
+ mcl_disconnected, mcl_uncached,
+ NULL, /* CSP is not used by now */
+ hdp_adapter, &err);
+
+ if (!hdp_adapter->mi) {
+ error("Error creating the MCAP instance: %s", err->message);
+ g_error_free(err);
+ return FALSE;
+ }
+
+ hdp_adapter->ccpsm = mcap_get_ctrl_psm(hdp_adapter->mi, &err);
+ if (err) {
+ error("Error getting MCAP control PSM: %s", err->message);
+ goto fail;
+ }
+
+ hdp_adapter->dcpsm = mcap_get_data_psm(hdp_adapter->mi, &err);
+ if (err) {
+ error("Error getting MCAP data PSM: %s", err->message);
+ goto fail;
+ }
+
+update:
+ if (hdp_update_sdp_record(hdp_adapter, applications))
+ return TRUE;
+ error("Error updating the SDP record");
+
+fail:
+ release_adapter_instance(hdp_adapter);
+ if (err)
+ g_error_free(err);
+ return FALSE;
+}
+
+int hdp_adapter_register(DBusConnection *conn, struct btd_adapter *adapter)
+{
+ struct hdp_adapter *hdp_adapter;
+
+ hdp_adapter = g_new0(struct hdp_adapter, 1);
+ hdp_adapter->btd_adapter = btd_adapter_ref(adapter);
+
+ if(!update_adapter(hdp_adapter))
+ goto fail;
+
+ adapters = g_slist_append(adapters, hdp_adapter);
+
+ return 0;
+
+fail:
+ btd_adapter_unref(hdp_adapter->btd_adapter);
+ g_free(hdp_adapter);
+ return -1;
+}
+
+void hdp_adapter_unregister(struct btd_adapter *adapter)
+{
+ struct hdp_adapter *hdp_adapter;
+ GSList *l;
+
+ l = g_slist_find_custom(adapters, adapter, cmp_adapter);
+
+ if (!l)
+ return;
+
+ hdp_adapter = l->data;
+ adapters = g_slist_remove(adapters, hdp_adapter);
+ if (hdp_adapter->sdp_handler)
+ remove_record_from_server(hdp_adapter->sdp_handler);
+ release_adapter_instance(hdp_adapter);
+ btd_adapter_unref(hdp_adapter->btd_adapter);
+ g_free(hdp_adapter);
+}
+
+static void delete_echo_channel_cb(GError *err, gpointer chan)
+{
+ if (err && err->code != MCAP_INVALID_MDL) {
+ /* TODO: Decide if more action is required here */
+ error("Error deleting echo channel: %s", err->message);
+ return;
+ }
+
+ health_channel_destroy(chan);
+}
+
+static void delete_echo_channel(struct hdp_channel *chan)
+{
+ GError *err = NULL;
+
+ if (!chan->dev->mcl_conn) {
+ error("Echo channel cannot be deleted: mcl closed");
+ return;
+ }
+
+ if (mcap_delete_mdl(chan->mdl, delete_echo_channel_cb,
+ hdp_channel_ref(chan),
+ (GDestroyNotify) hdp_channel_unref, &err))
+ return;
+
+ hdp_channel_unref(chan);
+ error("Error deleting the echo channel: %s", err->message);
+ g_error_free(err);
+
+ /* TODO: Decide if more action is required here */
+}
+
+static void abort_echo_channel_cb(GError *err, gpointer data)
+{
+ struct hdp_channel *chan = data;
+
+ if (err && err->code != MCAP_ERROR_INVALID_OPERATION) {
+ error("Aborting error: %s", err->message);
+ if (err->code == MCAP_INVALID_MDL) {
+ /* MDL is removed from MCAP so we can */
+ /* free the data channel without sending */
+ /* a MD_DELETE_MDL_REQ */
+ /* TODO review the above comment */
+ /* hdp_channel_unref(chan); */
+ }
+ return;
+ }
+
+ delete_echo_channel(chan);
+}
+
+static void destroy_create_dc_data(gpointer data)
+{
+ struct hdp_create_dc *dc_data = data;
+
+ hdp_create_data_unref(dc_data);
+}
+
+static void *generate_echo_packet()
+{
+ uint8_t *buf;
+ int i;
+
+ buf = g_malloc(HDP_ECHO_LEN);
+ srand(time(NULL));
+
+ for(i = 0; i < HDP_ECHO_LEN; i++)
+ buf[i] = rand() % UINT8_MAX;
+
+ return buf;
+}
+
+static gboolean check_echo(GIOChannel *io_chan, GIOCondition cond,
+ gpointer data)
+{
+ struct hdp_tmp_dc_data *hdp_conn = data;
+ struct hdp_echo_data *edata = hdp_conn->hdp_chann->edata;
+ struct hdp_channel *chan = hdp_conn->hdp_chann;
+ uint8_t buf[MCAP_DC_MTU];
+ DBusMessage *reply;
+ gboolean value;
+ int fd, len;
+
+ if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
+ value = FALSE;
+ goto end;
+ }
+
+ fd = g_io_channel_unix_get_fd(io_chan);
+ len = read(fd, buf, sizeof(buf));
+
+ if (len != HDP_ECHO_LEN) {
+ value = FALSE;
+ goto end;
+ }
+
+ value = (memcmp(buf, edata->buf, len) == 0);
+
+end:
+ reply = g_dbus_create_reply(hdp_conn->msg, DBUS_TYPE_BOOLEAN, &value,
+ DBUS_TYPE_INVALID);
+ g_dbus_send_message(hdp_conn->conn, reply);
+ g_source_remove(edata->tid);
+ edata->tid = 0;
+ g_free(edata->buf);
+ edata->buf = NULL;
+
+ if (!value)
+ close_device_con(chan->dev, FALSE);
+ else
+ delete_echo_channel(chan);
+ hdp_tmp_dc_data_unref(hdp_conn);
+
+ return FALSE;
+}
+
+static gboolean echo_timeout(gpointer data)
+{
+ struct hdp_channel *chan = data;
+ GIOChannel *io;
+ int fd;
+
+ error("Error: Echo request timeout");
+ chan->edata->tid = 0;
+
+ fd = mcap_mdl_get_fd(chan->mdl);
+ if (fd < 0)
+ return FALSE;
+
+ io = g_io_channel_unix_new(fd);
+ g_io_channel_shutdown(io, TRUE, NULL);
+
+ return FALSE;
+}
+
+static void hdp_echo_connect_cb(struct mcap_mdl *mdl, GError *err,
+ gpointer data)
+{
+ struct hdp_tmp_dc_data *hdp_conn = data;
+ struct hdp_echo_data *edata;
+ GError *gerr = NULL;
+ DBusMessage *reply;
+ GIOChannel *io;
+ int fd;
+
+ if (err) {
+ reply = g_dbus_create_error(hdp_conn->msg,
+ ERROR_INTERFACE ".HealthError",
+ "%s", err->message);
+ g_dbus_send_message(hdp_conn->conn, reply);
+
+ /* Send abort request because remote */
+ /* side is now in PENDING state. */
+ if (!mcap_mdl_abort(hdp_conn->hdp_chann->mdl,
+ abort_echo_channel_cb,
+ hdp_channel_ref(hdp_conn->hdp_chann),
+ (GDestroyNotify) hdp_channel_unref,
+ &gerr)) {
+ error("%s", gerr->message);
+ g_error_free(gerr);
+ hdp_channel_unref(hdp_conn->hdp_chann);
+ }
+ return;
+ }
+
+ fd = mcap_mdl_get_fd(hdp_conn->hdp_chann->mdl);
+ if (fd < 0) {
+ reply = g_dbus_create_error(hdp_conn->msg,
+ ERROR_INTERFACE ".HealthError",
+ "Can't write in echo channel");
+ g_dbus_send_message(hdp_conn->conn, reply);
+ delete_echo_channel(hdp_conn->hdp_chann);
+ return;
+ }
+
+ edata = hdp_conn->hdp_chann->edata;
+ edata->buf = generate_echo_packet();
+ send_echo_data(fd, edata->buf, HDP_ECHO_LEN);
+
+ io = g_io_channel_unix_new(fd);
+ g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL | G_IO_IN,
+ check_echo, hdp_tmp_dc_data_ref(hdp_conn));
+
+ edata->tid = g_timeout_add_seconds_full(G_PRIORITY_DEFAULT,
+ ECHO_TIMEOUT, echo_timeout,
+ hdp_channel_ref(hdp_conn->hdp_chann),
+ (GDestroyNotify) hdp_channel_unref);
+
+ g_io_channel_unref(io);
+}
+
+static void delete_mdl_cb(GError *err, gpointer data)
+{
+ if (err)
+ error("Deleting error: %s", err->message);
+}
+
+static void abort_and_del_mdl_cb(GError *err, gpointer data)
+{
+ struct mcap_mdl *mdl = data;
+ GError *gerr = NULL;
+
+ if (err) {
+ error("%s", err->message);
+ if (err->code == MCAP_INVALID_MDL) {
+ /* MDL is removed from MCAP so we don't */
+ /* need to delete it. */
+ return;
+ }
+ }
+
+ if (!mcap_delete_mdl(mdl, delete_mdl_cb, NULL, NULL, &gerr)) {
+ error("%s", gerr->message);
+ g_error_free(gerr);
+ }
+}
+
+static void hdp_mdl_conn_cb(struct mcap_mdl *mdl, GError *err, gpointer data)
+{
+ struct hdp_tmp_dc_data *hdp_conn = data;
+ struct hdp_channel *hdp_chann = hdp_conn->hdp_chann;
+ struct hdp_device *dev = hdp_chann->dev;
+ DBusMessage *reply;
+ GError *gerr = NULL;
+
+ if (err) {
+ error("%s", err->message);
+ reply = g_dbus_create_reply(hdp_conn->msg,
+ DBUS_TYPE_OBJECT_PATH, &hdp_chann->path,
+ DBUS_TYPE_INVALID);
+ g_dbus_send_message(hdp_conn->conn, reply);
+
+ /* Send abort request because remote side */
+ /* is now in PENDING state */
+ if (!mcap_mdl_abort(hdp_chann->mdl, abort_mdl_cb, NULL,
+ NULL, &gerr)) {
+ error("%s", gerr->message);
+ g_error_free(gerr);
+ }
+ return;
+ }
+
+ reply = g_dbus_create_reply(hdp_conn->msg,
+ DBUS_TYPE_OBJECT_PATH, &hdp_chann->path,
+ DBUS_TYPE_INVALID);
+ g_dbus_send_message(hdp_conn->conn, reply);
+
+ if (!check_channel_conf(hdp_chann)) {
+ close_mdl(hdp_chann);
+ return;
+ }
+
+ if (dev->fr)
+ return;
+
+ dev->fr = hdp_channel_ref(hdp_chann);
+
+ emit_property_changed(dev->conn, device_get_path(dev->dev),
+ HEALTH_DEVICE, "MainChannel",
+ DBUS_TYPE_OBJECT_PATH, &dev->fr->path);
+}
+
+static void device_create_mdl_cb(struct mcap_mdl *mdl, uint8_t conf,
+ GError *err, gpointer data)
+{
+ struct hdp_create_dc *user_data = data;
+ struct hdp_tmp_dc_data *hdp_conn;
+ struct hdp_channel *hdp_chan;
+ GError *gerr = NULL;
+ DBusMessage *reply;
+
+ if (err) {
+ reply = g_dbus_create_error(user_data->msg,
+ ERROR_INTERFACE ".HealthError",
+ "%s", err->message);
+ g_dbus_send_message(user_data->conn, reply);
+ return;
+ }
+
+ if (user_data->mdep != HDP_MDEP_ECHO &&
+ user_data->config == HDP_NO_PREFERENCE_DC) {
+ if (!user_data->dev->fr && (conf != HDP_RELIABLE_DC)) {
+ g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
+ "Data channel aborted, first data "
+ "channel should be reliable");
+ goto fail;
+ } else if (conf == HDP_NO_PREFERENCE_DC ||
+ conf > HDP_STREAMING_DC) {
+ g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
+ "Data channel aborted, "
+ "configuration error");
+ goto fail;
+ }
+ }
+
+ hdp_chan = create_channel(user_data->dev, conf, mdl,
+ mcap_mdl_get_mdlid(mdl),
+ user_data->app, &gerr);
+ if (!hdp_chan)
+ goto fail;
+
+ if (user_data->mdep != HDP_MDEP_ECHO)
+ g_dbus_emit_signal(user_data->conn,
+ device_get_path(hdp_chan->dev->dev),
+ HEALTH_DEVICE,
+ "ChannelConnected",
+ DBUS_TYPE_OBJECT_PATH, &hdp_chan->path,
+ DBUS_TYPE_INVALID);
+
+ hdp_conn = g_new0(struct hdp_tmp_dc_data, 1);
+ hdp_conn->msg = dbus_message_ref(user_data->msg);
+ hdp_conn->conn = dbus_connection_ref(user_data->conn);
+ hdp_conn->hdp_chann = hdp_chan;
+ hdp_conn->cb = user_data->cb;
+ hdp_chan->mdep = user_data->mdep;
+
+ if (hdp_get_dcpsm(hdp_chan->dev, hdp_get_dcpsm_cb,
+ hdp_tmp_dc_data_ref(hdp_conn),
+ hdp_tmp_dc_data_destroy, &gerr))
+ return;
+
+ error("%s", gerr->message);
+ g_error_free(gerr);
+ gerr = NULL;
+
+ reply = g_dbus_create_reply(hdp_conn->msg,
+ DBUS_TYPE_OBJECT_PATH, &hdp_chan->path,
+ DBUS_TYPE_INVALID);
+ g_dbus_send_message(hdp_conn->conn, reply);
+ hdp_tmp_dc_data_unref(hdp_conn);
+
+ /* Send abort request because remote side is now in PENDING state */
+ if (!mcap_mdl_abort(mdl, abort_mdl_cb, NULL, NULL, &gerr)) {
+ error("%s", gerr->message);
+ g_error_free(gerr);
+ }
+
+ return;
+
+fail:
+ reply = g_dbus_create_error(user_data->msg,
+ ERROR_INTERFACE ".HealthError",
+ "%s", gerr->message);
+ g_dbus_send_message(user_data->conn, reply);
+ g_error_free(gerr);
+ gerr = NULL;
+
+ /* Send abort request because remote side is now in PENDING */
+ /* state. Then we have to delete it because we couldn't */
+ /* register the HealthChannel interface */
+ if (!mcap_mdl_abort(mdl, abort_and_del_mdl_cb, mcap_mdl_ref(mdl), NULL,
+ &gerr)) {
+ error("%s", gerr->message);
+ g_error_free(gerr);
+ mcap_mdl_unref(mdl);
+ }
+}
+
+static void device_create_dc_cb(gpointer user_data, GError *err)
+{
+ struct hdp_create_dc *data = user_data;
+ DBusMessage *reply;
+ GError *gerr = NULL;
+
+ if (err) {
+ reply = g_dbus_create_error(data->msg,
+ ERROR_INTERFACE ".HealthError",
+ "%s", err->message);
+ g_dbus_send_message(data->conn, reply);
+ return;
+ }
+
+ if (!data->dev->mcl) {
+ g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
+ "Mcl was closed");
+ goto fail;
+ }
+
+ hdp_create_data_ref(data);
+
+ if (mcap_create_mdl(data->dev->mcl, data->mdep, data->config,
+ device_create_mdl_cb, data,
+ destroy_create_dc_data, &gerr))
+ return;
+ hdp_create_data_unref(data);
+
+fail:
+ reply = g_dbus_create_error(data->msg, ERROR_INTERFACE ".HealthError",
+ "%s", gerr->message);
+ g_error_free(gerr);
+ g_dbus_send_message(data->conn, reply);
+}
+
+static DBusMessage *device_echo(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct hdp_device *device = user_data;
+ struct hdp_create_dc *data;
+ DBusMessage *reply;
+ GError *err = NULL;
+
+ data = g_new0(struct hdp_create_dc, 1);
+ data->dev = health_device_ref(device);
+ data->mdep = HDP_MDEP_ECHO;
+ data->config = HDP_RELIABLE_DC;
+ data->msg = dbus_message_ref(msg);
+ data->conn = dbus_connection_ref(conn);
+ data->cb = hdp_echo_connect_cb;
+ hdp_create_data_ref(data);
+
+ if (device->mcl_conn && device->mcl) {
+ if (mcap_create_mdl(device->mcl, data->mdep, data->config,
+ device_create_mdl_cb, data,
+ destroy_create_dc_data, &err))
+ return NULL;
+ goto fail;
+ }
+
+ if (hdp_establish_mcl(data->dev, device_create_dc_cb,
+ data, destroy_create_dc_data, &err))
+ return NULL;
+
+fail:
+ reply = g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError",
+ "%s", err->message);
+ g_error_free(err);
+ hdp_create_data_unref(data);
+ return reply;
+}
+
+static void device_get_mdep_cb(uint8_t mdep, gpointer data, GError *err)
+{
+ struct hdp_create_dc *dc_data, *user_data = data;
+ DBusMessage *reply;
+ GError *gerr = NULL;
+
+ if (err) {
+ reply = g_dbus_create_error(user_data->msg,
+ ERROR_INTERFACE ".HealthError",
+ "%s", err->message);
+ g_dbus_send_message(user_data->conn, reply);
+ return;
+ }
+
+ dc_data = hdp_create_data_ref(user_data);
+ dc_data->mdep = mdep;
+
+ if (user_data->dev->mcl_conn) {
+ device_create_dc_cb(dc_data, NULL);
+ hdp_create_data_unref(dc_data);
+ return;
+ }
+
+ if (hdp_establish_mcl(dc_data->dev, device_create_dc_cb,
+ dc_data, destroy_create_dc_data, &gerr))
+ return;
+
+ reply = g_dbus_create_error(user_data->msg,
+ ERROR_INTERFACE ".HealthError",
+ "%s", gerr->message);
+ hdp_create_data_unref(dc_data);
+ g_error_free(gerr);
+ g_dbus_send_message(user_data->conn, reply);
+}
+
+static DBusMessage *device_create_channel(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct hdp_device *device = user_data;
+ struct hdp_application *app;
+ struct hdp_create_dc *data;
+ char *app_path, *conf;
+ DBusMessage *reply;
+ GError *err = NULL;
+ uint8_t config;
+ GSList *l;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &app_path,
+ DBUS_TYPE_STRING, &conf,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ l = g_slist_find_custom(applications, app_path, cmp_app);
+ if (!l)
+ return btd_error_invalid_args(msg);
+
+ app = l->data;
+
+ if (g_ascii_strcasecmp("Reliable", conf) == 0)
+ config = HDP_RELIABLE_DC;
+ else if (g_ascii_strcasecmp("Streaming", conf) == 0)
+ config = HDP_STREAMING_DC;
+ else if (g_ascii_strcasecmp("Any", conf) == 0)
+ config = HDP_NO_PREFERENCE_DC;
+ else
+ return btd_error_invalid_args(msg);
+
+ if (app->role == HDP_SINK && config != HDP_NO_PREFERENCE_DC)
+ return btd_error_invalid_args(msg);
+
+ if (app->role == HDP_SOURCE && config == HDP_NO_PREFERENCE_DC)
+ return btd_error_invalid_args(msg);
+
+ if (!device->fr && config == HDP_STREAMING_DC)
+ return btd_error_invalid_args(msg);
+
+ data = g_new0(struct hdp_create_dc, 1);
+ data->dev = health_device_ref(device);
+ data->config = config;
+ data->app = hdp_application_ref(app);
+ data->msg = dbus_message_ref(msg);
+ data->conn = dbus_connection_ref(conn);
+ data->cb = hdp_mdl_conn_cb;
+
+ if (hdp_get_mdep(device, l->data, device_get_mdep_cb,
+ hdp_create_data_ref(data),
+ destroy_create_dc_data, &err))
+ return NULL;
+
+ reply = g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError",
+ "%s", err->message);
+ g_error_free(err);
+ hdp_create_data_unref(data);
+ return reply;
+}
+
+static void hdp_mdl_delete_cb(GError *err, gpointer data)
+{
+ struct hdp_tmp_dc_data *del_data = data;
+ DBusMessage *reply;
+ char *path;
+
+ if (err && err->code != MCAP_INVALID_MDL) {
+ reply = g_dbus_create_error(del_data->msg,
+ ERROR_INTERFACE ".HealthError",
+ "%s", err->message);
+ g_dbus_send_message(del_data->conn, reply);
+ return;
+ }
+
+ path = g_strdup(del_data->hdp_chann->path);
+ g_dbus_unregister_interface(del_data->conn, path, HEALTH_CHANNEL);
+ g_free(path);
+
+ reply = g_dbus_create_reply(del_data->msg, DBUS_TYPE_INVALID);
+ g_dbus_send_message(del_data->conn, reply);
+}
+
+static void hdp_continue_del_cb(gpointer user_data, GError *err)
+{
+ struct hdp_tmp_dc_data *del_data = user_data;
+ GError *gerr = NULL;
+ DBusMessage *reply;
+
+ if (err) {
+ reply = g_dbus_create_error(del_data->msg,
+ ERROR_INTERFACE ".HealthError",
+ "%s", err->message);
+ g_dbus_send_message(del_data->conn, reply);
+ return;
+ }
+
+ if (mcap_delete_mdl(del_data->hdp_chann->mdl, hdp_mdl_delete_cb,
+ hdp_tmp_dc_data_ref(del_data),
+ hdp_tmp_dc_data_destroy, &gerr))
+ return;
+
+ reply = g_dbus_create_error(del_data->msg,
+ ERROR_INTERFACE ".HealthError",
+ "%s", gerr->message);
+ hdp_tmp_dc_data_unref(del_data);
+ g_error_free(gerr);
+ g_dbus_send_message(del_data->conn, reply);
+}
+
+static DBusMessage *device_destroy_channel(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct hdp_device *device = user_data;
+ struct hdp_tmp_dc_data *del_data;
+ struct hdp_channel *hdp_chan;
+ DBusMessage *reply;
+ GError *err = NULL;
+ char *path;
+ GSList *l;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID)){
+ return btd_error_invalid_args(msg);
+ }
+
+ l = g_slist_find_custom(device->channels, path, cmp_chan_path);
+ if (!l)
+ return btd_error_invalid_args(msg);
+
+ hdp_chan = l->data;
+ del_data = g_new0(struct hdp_tmp_dc_data, 1);
+ del_data->msg = dbus_message_ref(msg);
+ del_data->conn = dbus_connection_ref(conn);
+ del_data->hdp_chann = hdp_channel_ref(hdp_chan);
+
+ if (device->mcl_conn) {
+ if (mcap_delete_mdl(hdp_chan->mdl, hdp_mdl_delete_cb,
+ hdp_tmp_dc_data_ref(del_data),
+ hdp_tmp_dc_data_destroy, &err))
+ return NULL;
+ goto fail;
+ }
+
+ if (hdp_establish_mcl(device, hdp_continue_del_cb,
+ hdp_tmp_dc_data_ref(del_data),
+ hdp_tmp_dc_data_destroy, &err))
+ return NULL;
+
+fail:
+ reply = g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError",
+ "%s", err->message);
+ hdp_tmp_dc_data_unref(del_data);
+ g_error_free(err);
+ return reply;
+}
+
+static DBusMessage *device_get_properties(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct hdp_device *device = user_data;
+ DBusMessageIter iter, dict;
+ DBusMessage *reply;
+ char *path;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ if (device->fr)
+ path = g_strdup(device->fr->path);
+ else
+ path = g_strdup("");
+ dict_append_entry(&dict, "MainChannel", DBUS_TYPE_STRING, &path);
+ g_free(path);
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static void health_device_destroy(void *data)
+{
+ struct hdp_device *device = data;
+
+ DBG("Unregistered interface %s on path %s", HEALTH_DEVICE,
+ device_get_path(device->dev));
+
+ remove_channels(device);
+ if (device->ndc) {
+ hdp_channel_unref(device->ndc);
+ device->ndc = NULL;
+ }
+
+ devices = g_slist_remove(devices, device);
+ health_device_unref(device);
+}
+
+static GDBusMethodTable health_device_methods[] = {
+ {"Echo", "", "b", device_echo,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ {"CreateChannel", "os", "o", device_create_channel,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ {"DestroyChannel", "o", "", device_destroy_channel,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ {"GetProperties", "", "a{sv}", device_get_properties},
+ { NULL }
+};
+
+static GDBusSignalTable health_device_signals[] = {
+ {"ChannelConnected", "o" },
+ {"ChannelDeleted", "o" },
+ {"PropertyChanged", "sv" },
+ { NULL }
+};
+
+static struct hdp_device *create_health_device(DBusConnection *conn,
+ struct btd_device *device)
+{
+ struct btd_adapter *adapter = device_get_adapter(device);
+ const gchar *path = device_get_path(device);
+ struct hdp_device *dev;
+ GSList *l;
+
+ if (!device)
+ return NULL;
+
+ dev = g_new0(struct hdp_device, 1);
+ dev->conn = dbus_connection_ref(conn);
+ dev->dev = btd_device_ref(device);
+ health_device_ref(dev);
+
+ l = g_slist_find_custom(adapters, adapter, cmp_adapter);
+ if (!l)
+ goto fail;
+
+ dev->hdp_adapter = l->data;
+
+ if (!g_dbus_register_interface(conn, path,
+ HEALTH_DEVICE,
+ health_device_methods,
+ health_device_signals, NULL,
+ dev, health_device_destroy)) {
+ error("D-Bus failed to register %s interface", HEALTH_DEVICE);
+ goto fail;
+ }
+
+ DBG("Registered interface %s on path %s", HEALTH_DEVICE, path);
+ return dev;
+
+fail:
+ health_device_unref(dev);
+ return NULL;
+}
+
+int hdp_device_register(DBusConnection *conn, struct btd_device *device)
+{
+ struct hdp_device *hdev;
+ GSList *l;
+
+ l = g_slist_find_custom(devices, device, cmp_device);
+ if (l) {
+ hdev = l->data;
+ hdev->sdp_present = TRUE;
+ return 0;
+ }
+
+ hdev = create_health_device(conn, device);
+ if (!hdev)
+ return -1;
+
+ hdev->sdp_present = TRUE;
+
+ devices = g_slist_prepend(devices, hdev);
+ return 0;
+}
+
+void hdp_device_unregister(struct btd_device *device)
+{
+ struct hdp_device *hdp_dev;
+ const char *path;
+ GSList *l;
+
+ l = g_slist_find_custom(devices, device, cmp_device);
+ if (!l)
+ return;
+
+ hdp_dev = l->data;
+ path = device_get_path(hdp_dev->dev);
+ g_dbus_unregister_interface(hdp_dev->conn, path, HEALTH_DEVICE);
+}
+
+int hdp_manager_start(DBusConnection *conn)
+{
+ DBG("Starting Health manager");
+
+ if (!g_dbus_register_interface(conn, MANAGER_PATH,
+ HEALTH_MANAGER,
+ health_manager_methods, NULL, NULL,
+ NULL, manager_path_unregister)) {
+ error("D-Bus failed to register %s interface", HEALTH_MANAGER);
+ return -1;
+ }
+
+ connection = dbus_connection_ref(conn);
+
+ return 0;
+}
+
+void hdp_manager_stop()
+{
+ g_dbus_unregister_interface(connection, MANAGER_PATH, HEALTH_MANAGER);
+
+ dbus_connection_unref(connection);
+ DBG("Stopped Health manager");
+}
+
+struct hdp_device *health_device_ref(struct hdp_device *hdp_dev)
+{
+ hdp_dev->ref++;
+
+ DBG("health_device_ref(%p): ref=%d", hdp_dev, hdp_dev->ref);
+
+ return hdp_dev;
+}
+
+void health_device_unref(struct hdp_device *hdp_dev)
+{
+ hdp_dev->ref--;
+
+ DBG("health_device_unref(%p): ref=%d", hdp_dev, hdp_dev->ref);
+
+ if (hdp_dev->ref > 0)
+ return;
+
+ free_health_device(hdp_dev);
+}
diff --git a/health/hdp.h b/health/hdp.h
new file mode 100644
index 0000000..3cf87ca
--- /dev/null
+++ b/health/hdp.h
@@ -0,0 +1,35 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ * Authors:
+ * Santiago Carot Nemesio <sancane at gmail.com>
+ * Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
+ *
+ * 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
+ *
+ */
+
+int hdp_adapter_register(DBusConnection *conn, struct btd_adapter *btd_adapter);
+void hdp_adapter_unregister(struct btd_adapter *btd_adapter);
+
+int hdp_device_register(DBusConnection *conn, struct btd_device *device);
+void hdp_device_unregister(struct btd_device *device);
+
+int hdp_manager_start(DBusConnection *conn);
+void hdp_manager_stop();
+
+gboolean hdp_set_mcl_cb(struct hdp_device *device, GError **err);
diff --git a/health/hdp_main.c b/health/hdp_main.c
new file mode 100644
index 0000000..b40a37c
--- /dev/null
+++ b/health/hdp_main.c
@@ -0,0 +1,62 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ * Authors:
+ * Santiago Carot Nemesio <sancane at gmail.com>
+ * Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
+ *
+ * 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 <errno.h>
+
+#include <gdbus.h>
+
+#include "plugin.h"
+#include "hdp_manager.h"
+
+static DBusConnection *connection = NULL;
+
+static int hdp_init(void)
+{
+ connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+ if (connection == NULL)
+ return -EIO;
+
+ if (hdp_manager_init(connection) < 0) {
+ dbus_connection_unref(connection);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void hdp_exit(void)
+{
+ hdp_manager_exit();
+
+ dbus_connection_unref(connection);
+ connection = NULL;
+}
+
+BLUETOOTH_PLUGIN_DEFINE(health, VERSION,
+ BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, hdp_init, hdp_exit)
diff --git a/health/hdp_manager.c b/health/hdp_manager.c
new file mode 100644
index 0000000..88b49fc
--- /dev/null
+++ b/health/hdp_manager.c
@@ -0,0 +1,100 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ * Authors:
+ * Santiago Carot Nemesio <sancane at gmail.com>
+ * Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
+ *
+ * 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 <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <btio.h>
+#include <adapter.h>
+#include <device.h>
+
+#include "hdp_types.h"
+
+#include "log.h"
+#include "hdp_manager.h"
+#include "hdp.h"
+
+#include "glib-helper.h"
+
+static DBusConnection *connection = NULL;
+
+static int hdp_adapter_probe(struct btd_adapter *adapter)
+{
+ return hdp_adapter_register(connection, adapter);
+}
+
+static void hdp_adapter_remove(struct btd_adapter *adapter)
+{
+ hdp_adapter_unregister(adapter);
+}
+
+static struct btd_adapter_driver hdp_adapter_driver = {
+ .name = "hdp-adapter-driver",
+ .probe = hdp_adapter_probe,
+ .remove = hdp_adapter_remove,
+};
+
+static int hdp_driver_probe(struct btd_device *device, GSList *uuids)
+{
+ return hdp_device_register(connection, device);
+}
+
+static void hdp_driver_remove(struct btd_device *device)
+{
+ hdp_device_unregister(device);
+}
+
+static struct btd_device_driver hdp_device_driver = {
+ .name = "hdp-device-driver",
+ .uuids = BTD_UUIDS(HDP_UUID, HDP_SOURCE_UUID, HDP_SINK_UUID),
+ .probe = hdp_driver_probe,
+ .remove = hdp_driver_remove
+};
+
+int hdp_manager_init(DBusConnection *conn)
+{
+ if (hdp_manager_start(conn))
+ return -1;
+
+ connection = dbus_connection_ref(conn);
+ btd_register_adapter_driver(&hdp_adapter_driver);
+ btd_register_device_driver(&hdp_device_driver);
+
+ return 0;
+}
+
+void hdp_manager_exit(void)
+{
+ btd_unregister_device_driver(&hdp_device_driver);
+ btd_unregister_adapter_driver(&hdp_adapter_driver);
+ hdp_manager_stop();
+
+ dbus_connection_unref(connection);
+ connection = NULL;
+}
diff --git a/health/hdp_manager.h b/health/hdp_manager.h
new file mode 100644
index 0000000..b91ef75
--- /dev/null
+++ b/health/hdp_manager.h
@@ -0,0 +1,27 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ * Authors:
+ * Santiago Carot Nemesio <sancane at gmail.com>
+ * Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
+ *
+ * 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
+ *
+ */
+
+int hdp_manager_init(DBusConnection *conn);
+void hdp_manager_exit(void);
diff --git a/health/hdp_types.h b/health/hdp_types.h
new file mode 100644
index 0000000..7d23293
--- /dev/null
+++ b/health/hdp_types.h
@@ -0,0 +1,129 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ * Authors:
+ * Santiago Carot Nemesio <sancane at gmail.com>
+ * Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
+ *
+ * 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
+ *
+ */
+
+#ifndef __HDP_TYPES_H__
+#define __HDP_TYPES_H__
+
+#define HDP_UUID "00001400-0000-1000-8000-00805F9B34FB"
+#define HDP_SOURCE_UUID "00001401-0000-1000-8000-00805F9B34FB"
+#define HDP_SINK_UUID "00001402-0000-1000-8000-00805F9B34FB"
+
+#define MANAGER_PATH "/org/bluez"
+
+#define HEALTH_MANAGER "org.bluez.HealthManager"
+#define HEALTH_DEVICE "org.bluez.HealthDevice"
+#define HEALTH_CHANNEL "org.bluez.HealthChannel"
+
+#define HDP_VERSION 0x0100
+
+#define HDP_SERVICE_NAME "Bluez HDP"
+#define HDP_SERVICE_DSC "A Bluez health device profile implementation"
+#define HDP_SERVICE_PROVIDER "Bluez"
+
+#define HDP_MDEP_ECHO 0x00
+#define HDP_MDEP_INITIAL 0x01
+#define HDP_MDEP_FINAL 0x7F
+
+#define HDP_ERROR g_quark_from_static_string("hdp-error-quark")
+
+#define HDP_NO_PREFERENCE_DC 0x00
+#define HDP_RELIABLE_DC 0x01
+#define HDP_STREAMING_DC 0x02
+
+#define HDP_SINK_ROLE_AS_STRING "sink"
+#define HDP_SOURCE_ROLE_AS_STRING "source"
+
+typedef enum {
+ HDP_SOURCE = 0x00,
+ HDP_SINK = 0x01
+} HdpRole;
+
+typedef enum {
+ HDP_DIC_PARSE_ERROR,
+ HDP_DIC_ENTRY_PARSE_ERROR,
+ HDP_CONNECTION_ERROR,
+ HDP_UNSPECIFIED_ERROR,
+ HDP_UNKNOWN_ERROR
+} HdpError;
+
+enum data_specs {
+ DATA_EXCHANGE_SPEC_11073 = 0x01
+};
+
+struct hdp_application {
+ DBusConnection *conn; /* For dbus watcher */
+ char *path; /* The path of the application */
+ uint16_t data_type; /* Data type handled for this application */
+ gboolean data_type_set; /* Flag for dictionary parsing */
+ uint8_t role; /* Role of this application */
+ gboolean role_set; /* Flag for dictionary parsing */
+ uint8_t chan_type; /* QoS preferred by source applications */
+ gboolean chan_type_set; /* Flag for dictionary parsing */
+ char *description; /* Options description for SDP record */
+ uint8_t id; /* The identification is also the mdepid */
+ char *oname; /* Name of the owner application */
+ int dbus_watcher; /* Watch for clients disconnection */
+ gint ref; /* Reference counter */
+};
+
+struct hdp_adapter {
+ struct btd_adapter *btd_adapter; /* Bluetooth adapter */
+ struct mcap_instance *mi; /* Mcap instance in */
+ uint16_t ccpsm; /* Control channel psm */
+ uint16_t dcpsm; /* Data channel psm */
+ uint32_t sdp_handler; /* SDP record handler */
+ uint32_t record_state; /* Service record state */
+};
+
+struct hdp_device {
+ DBusConnection *conn; /* For name listener handling */
+ struct btd_device *dev; /* Device reference */
+ struct hdp_adapter *hdp_adapter; /* hdp_adapater */
+ struct mcap_mcl *mcl; /* The mcap control channel */
+ gboolean mcl_conn; /* Mcl status */
+ gboolean sdp_present; /* Has an sdp record */
+ GSList *channels; /* Data Channel list */
+ struct hdp_channel *ndc; /* Data channel being negotiated */
+ struct hdp_channel *fr; /* First reliable data channel */
+ gint ref; /* Reference counting */
+};
+
+struct hdp_echo_data;
+
+struct hdp_channel {
+ struct hdp_device *dev; /* Device where this channel belongs */
+ struct hdp_application *app; /* Application */
+ struct mcap_mdl *mdl; /* The data channel reference */
+ char *path; /* The path of the channel */
+ uint8_t config; /* Channel configuration */
+ uint8_t mdep; /* Remote MDEP */
+ uint16_t mdlid; /* Data channel Id */
+ uint16_t imtu; /* Channel incoming MTU */
+ uint16_t omtu; /* Channel outgoing MTU */
+ struct hdp_echo_data *edata; /* private data used by echo channels */
+ gint ref; /* Reference counter */
+};
+
+#endif /* __HDP_TYPES_H__ */
diff --git a/health/hdp_util.c b/health/hdp_util.c
new file mode 100644
index 0000000..aefe5f9
--- /dev/null
+++ b/health/hdp_util.c
@@ -0,0 +1,1211 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ * Authors:
+ * Santiago Carot Nemesio <sancane at gmail.com>
+ * Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
+ *
+ * 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
+ *
+ */
+
+#include <gdbus.h>
+
+#include <adapter.h>
+#include <device.h>
+#include <stdint.h>
+#include <hdp_types.h>
+#include <hdp_util.h>
+#include <mcap.h>
+#include <hdp.h>
+
+#include <sdpd.h>
+#include <sdp_lib.h>
+#include <glib-helper.h>
+
+#include <btio.h>
+#include <mcap_lib.h>
+
+#include <log.h>
+
+typedef gboolean (*parse_item_f)(DBusMessageIter *iter, gpointer user_data,
+ GError **err);
+
+struct dict_entry_func {
+ char *key;
+ parse_item_f func;
+};
+
+struct get_mdep_data {
+ struct hdp_application *app;
+ gpointer data;
+ hdp_continue_mdep_f func;
+ GDestroyNotify destroy;
+};
+
+struct conn_mcl_data {
+ int refs;
+ gpointer data;
+ hdp_continue_proc_f func;
+ GDestroyNotify destroy;
+ struct hdp_device *dev;
+};
+
+struct get_dcpsm_data {
+ gpointer data;
+ hdp_continue_dcpsm_f func;
+ GDestroyNotify destroy;
+};
+
+static gboolean parse_dict_entry(struct dict_entry_func dict_context[],
+ DBusMessageIter *iter,
+ GError **err,
+ gpointer user_data)
+{
+ DBusMessageIter entry;
+ char *key;
+ int ctype, i;
+ struct dict_entry_func df;
+
+ dbus_message_iter_recurse(iter, &entry);
+ ctype = dbus_message_iter_get_arg_type(&entry);
+ if (ctype != DBUS_TYPE_STRING) {
+ g_set_error(err, HDP_ERROR, HDP_DIC_ENTRY_PARSE_ERROR,
+ "Dictionary entries should have a string as key");
+ return FALSE;
+ }
+
+ dbus_message_iter_get_basic(&entry, &key);
+ dbus_message_iter_next(&entry);
+ /* Find function and call it */
+ for (i = 0, df = dict_context[0]; df.key; i++, df = dict_context[i]) {
+ if (g_ascii_strcasecmp(df.key, key) == 0)
+ return df.func(&entry, user_data, err);
+ }
+
+ g_set_error(err, HDP_ERROR, HDP_DIC_ENTRY_PARSE_ERROR,
+ "No function found for parsing value for key %s", key);
+ return FALSE;
+}
+
+static gboolean parse_dict(struct dict_entry_func dict_context[],
+ DBusMessageIter *iter,
+ GError **err,
+ gpointer user_data)
+{
+ int ctype;
+ DBusMessageIter dict;
+
+ ctype = dbus_message_iter_get_arg_type(iter);
+ if (ctype != DBUS_TYPE_ARRAY) {
+ g_set_error(err, HDP_ERROR, HDP_DIC_PARSE_ERROR,
+ "Dictionary should be an array");
+ return FALSE;
+ }
+
+ dbus_message_iter_recurse(iter, &dict);
+ while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
+ DBUS_TYPE_INVALID) {
+ if (ctype != DBUS_TYPE_DICT_ENTRY) {
+ g_set_error(err, HDP_ERROR, HDP_DIC_PARSE_ERROR,
+ "Dictionary array should "
+ "contain dict entries");
+ return FALSE;
+ }
+
+ /* Start parsing entry */
+ if (!parse_dict_entry(dict_context, &dict, err,
+ user_data))
+ return FALSE;
+ /* Finish entry parsing */
+
+ dbus_message_iter_next(&dict);
+ }
+
+ return TRUE;
+}
+
+static gboolean parse_data_type(DBusMessageIter *iter, gpointer data,
+ GError **err)
+{
+ struct hdp_application *app = data;
+ DBusMessageIter *value;
+ int ctype;
+
+ ctype = dbus_message_iter_get_arg_type(iter);
+ value = iter;
+ if (ctype == DBUS_TYPE_VARIANT) {
+ DBusMessageIter variant;
+
+ /* Get value inside the variable */
+ dbus_message_iter_recurse(iter, &variant);
+ ctype = dbus_message_iter_get_arg_type(&variant);
+ value = &variant;
+ }
+
+ if (ctype != DBUS_TYPE_UINT16) {
+ g_set_error(err, HDP_ERROR, HDP_DIC_ENTRY_PARSE_ERROR,
+ "Final value for data type should be uint16");
+ return FALSE;
+ }
+
+ dbus_message_iter_get_basic(value, &app->data_type);
+ app->data_type_set = TRUE;
+ return TRUE;
+}
+
+static gboolean parse_role(DBusMessageIter *iter, gpointer data, GError **err)
+{
+ struct hdp_application *app = data;
+ DBusMessageIter *string;
+ int ctype;
+ const char *role;
+
+ ctype = dbus_message_iter_get_arg_type(iter);
+ if (ctype == DBUS_TYPE_VARIANT) {
+ DBusMessageIter value;
+
+ /* Get value inside the variable */
+ dbus_message_iter_recurse(iter, &value);
+ ctype = dbus_message_iter_get_arg_type(&value);
+ string = &value;
+ } else {
+ string = iter;
+ }
+
+ if (ctype != DBUS_TYPE_STRING) {
+ g_set_error(err, HDP_ERROR, HDP_UNSPECIFIED_ERROR,
+ "Value data spec should be variable or string");
+ return FALSE;
+ }
+
+ dbus_message_iter_get_basic(string, &role);
+ if (g_ascii_strcasecmp(role, HDP_SINK_ROLE_AS_STRING) == 0) {
+ app->role = HDP_SINK;
+ } else if (g_ascii_strcasecmp(role, HDP_SOURCE_ROLE_AS_STRING) == 0) {
+ app->role = HDP_SOURCE;
+ } else {
+ g_set_error(err, HDP_ERROR, HDP_UNSPECIFIED_ERROR,
+ "Role value should be \"source\" or \"sink\"");
+ return FALSE;
+ }
+
+ app->role_set = TRUE;
+
+ return TRUE;
+}
+
+static gboolean parse_desc(DBusMessageIter *iter, gpointer data, GError **err)
+{
+ struct hdp_application *app = data;
+ DBusMessageIter *string;
+ int ctype;
+ const char *desc;
+
+ ctype = dbus_message_iter_get_arg_type(iter);
+ if (ctype == DBUS_TYPE_VARIANT) {
+ DBusMessageIter variant;
+
+ /* Get value inside the variable */
+ dbus_message_iter_recurse(iter, &variant);
+ ctype = dbus_message_iter_get_arg_type(&variant);
+ string = &variant;
+ } else {
+ string = iter;
+ }
+
+ if (ctype != DBUS_TYPE_STRING) {
+ g_set_error(err, HDP_ERROR, HDP_DIC_ENTRY_PARSE_ERROR,
+ "Value data spec should be variable or string");
+ return FALSE;
+ }
+
+ dbus_message_iter_get_basic(string, &desc);
+ app->description = g_strdup(desc);
+ return TRUE;
+}
+
+static gboolean parse_chan_type(DBusMessageIter *iter, gpointer data,
+ GError **err)
+{
+ struct hdp_application *app = data;
+ DBusMessageIter *value;
+ char *chan_type;
+ int ctype;
+
+ ctype = dbus_message_iter_get_arg_type(iter);
+ value = iter;
+ if (ctype == DBUS_TYPE_VARIANT) {
+ DBusMessageIter variant;
+
+ /* Get value inside the variable */
+ dbus_message_iter_recurse(iter, &variant);
+ ctype = dbus_message_iter_get_arg_type(&variant);
+ value = &variant;
+ }
+
+ if (ctype != DBUS_TYPE_STRING) {
+ g_set_error(err, HDP_ERROR, HDP_DIC_ENTRY_PARSE_ERROR,
+ "Final value for channel type should be an string");
+ return FALSE;
+ }
+
+ dbus_message_iter_get_basic(value, &chan_type);
+
+ if (g_ascii_strcasecmp("Reliable", chan_type) == 0)
+ app->chan_type = HDP_RELIABLE_DC;
+ else if (g_ascii_strcasecmp("Streaming", chan_type) == 0)
+ app->chan_type = HDP_STREAMING_DC;
+ else {
+ g_set_error(err, HDP_ERROR, HDP_DIC_ENTRY_PARSE_ERROR,
+ "Invalid value for data type");
+ return FALSE;
+ }
+
+ app->chan_type_set = TRUE;
+
+ return TRUE;
+}
+
+static struct dict_entry_func dict_parser[] = {
+ {"DataType", parse_data_type},
+ {"Role", parse_role},
+ {"Description", parse_desc},
+ {"ChannelType", parse_chan_type},
+ {NULL, NULL}
+};
+
+struct hdp_application *hdp_get_app_config(DBusMessageIter *iter, GError **err)
+{
+ struct hdp_application *app;
+
+ app = g_new0(struct hdp_application, 1);
+ app->ref = 1;
+ if (!parse_dict(dict_parser, iter, err, app))
+ goto fail;
+ if (!app->data_type_set || !app->role_set) {
+ g_set_error(err, HDP_ERROR, HDP_DIC_PARSE_ERROR,
+ "Mandatory fields aren't set");
+ goto fail;
+ }
+ return app;
+
+fail:
+ hdp_application_unref(app);
+ return NULL;
+}
+
+static gboolean is_app_role(GSList *app_list, HdpRole role)
+{
+ GSList *l;
+
+ for (l = app_list; l; l = l->next) {
+ struct hdp_application *app = l->data;
+
+ if (app->role == role)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean set_sdp_services_uuid(sdp_record_t *record, HdpRole role)
+{
+ uuid_t svc_uuid_source, svc_uuid_sink;
+ sdp_list_t *svc_list = NULL;
+
+ sdp_uuid16_create(&svc_uuid_sink, HDP_SINK_SVCLASS_ID);
+ sdp_uuid16_create(&svc_uuid_source, HDP_SOURCE_SVCLASS_ID);
+
+ sdp_get_service_classes(record, &svc_list);
+
+ if (role == HDP_SOURCE) {
+ if (!sdp_list_find(svc_list, &svc_uuid_source, sdp_uuid_cmp))
+ svc_list = sdp_list_append(svc_list, &svc_uuid_source);
+ } else if (role == HDP_SINK) {
+ if (!sdp_list_find(svc_list, &svc_uuid_sink, sdp_uuid_cmp))
+ svc_list = sdp_list_append(svc_list, &svc_uuid_sink);
+ }
+
+ if (sdp_set_service_classes(record, svc_list) < 0) {
+ sdp_list_free(svc_list, NULL);
+ return FALSE;
+ }
+
+ sdp_list_free(svc_list, NULL);
+
+ return TRUE;
+}
+
+static gboolean register_service_protocols(struct hdp_adapter *adapter,
+ sdp_record_t *sdp_record)
+{
+ gboolean ret;
+ uuid_t l2cap_uuid, mcap_c_uuid;
+ sdp_list_t *l2cap_list, *proto_list = NULL, *mcap_list = NULL;
+ sdp_list_t *access_proto_list = NULL;
+ sdp_data_t *psm = NULL, *mcap_ver = NULL;
+ uint16_t version = MCAP_VERSION;
+
+ /* set l2cap information */
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ l2cap_list = sdp_list_append(NULL, &l2cap_uuid);
+ if (!l2cap_list) {
+ ret = FALSE;
+ goto end;
+ }
+
+ psm = sdp_data_alloc(SDP_UINT16, &adapter->ccpsm);
+ if (!psm) {
+ ret = FALSE;
+ goto end;
+ }
+
+ if (!sdp_list_append(l2cap_list, psm)) {
+ ret = FALSE;
+ goto end;
+ }
+
+ proto_list = sdp_list_append(NULL, l2cap_list);
+ if (!proto_list) {
+ ret = FALSE;
+ goto end;
+ }
+
+ /* set mcap information */
+ sdp_uuid16_create(&mcap_c_uuid, MCAP_CTRL_UUID);
+ mcap_list = sdp_list_append(NULL, &mcap_c_uuid);
+ if (!mcap_list) {
+ ret = FALSE;
+ goto end;
+ }
+
+ mcap_ver = sdp_data_alloc(SDP_UINT16, &version);
+ if (!mcap_ver) {
+ ret = FALSE;
+ goto end;
+ }
+
+ if (!sdp_list_append(mcap_list, mcap_ver)) {
+ ret = FALSE;
+ goto end;
+ }
+
+ if (!sdp_list_append(proto_list, mcap_list)) {
+ ret = FALSE;
+ goto end;
+ }
+
+ /* attach protocol information to service record */
+ access_proto_list = sdp_list_append(NULL, proto_list);
+ if (!access_proto_list) {
+ ret = FALSE;
+ goto end;
+ }
+
+ if (sdp_set_access_protos(sdp_record, access_proto_list) < 0) {
+ ret = FALSE;
+ goto end;
+ }
+ ret = TRUE;
+
+end:
+ if (l2cap_list)
+ sdp_list_free(l2cap_list, NULL);
+ if (mcap_list)
+ sdp_list_free(mcap_list, NULL);
+ if (proto_list)
+ sdp_list_free(proto_list, NULL);
+ if (access_proto_list)
+ sdp_list_free(access_proto_list, NULL);
+ if (psm)
+ sdp_data_free(psm);
+ if (mcap_ver)
+ sdp_data_free(mcap_ver);
+
+ return ret;
+}
+
+static gboolean register_service_profiles(sdp_record_t *sdp_record)
+{
+ gboolean ret;
+ sdp_list_t *profile_list;
+ sdp_profile_desc_t hdp_profile;
+
+ /* set hdp information */
+ sdp_uuid16_create(&hdp_profile.uuid, HDP_SVCLASS_ID);
+ hdp_profile.version = HDP_VERSION;
+ profile_list = sdp_list_append(NULL, &hdp_profile);
+ if (!profile_list)
+ return FALSE;
+
+ /* set profile descriptor list */
+ if (sdp_set_profile_descs(sdp_record, profile_list) < 0)
+ ret = FALSE;
+ else
+ ret = TRUE;
+
+ sdp_list_free(profile_list, NULL);
+
+ return ret;
+}
+
+static gboolean register_service_additional_protocols(
+ struct hdp_adapter *adapter,
+ sdp_record_t *sdp_record)
+{
+ gboolean ret;
+ uuid_t l2cap_uuid, mcap_d_uuid;
+ sdp_list_t *l2cap_list, *proto_list = NULL, *mcap_list = NULL;
+ sdp_list_t *access_proto_list = NULL;
+ sdp_data_t *psm = NULL;
+
+ /* set l2cap information */
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ l2cap_list = sdp_list_append(NULL, &l2cap_uuid);
+ if (!l2cap_list) {
+ ret = FALSE;
+ goto end;
+ }
+
+ psm = sdp_data_alloc(SDP_UINT16, &adapter->dcpsm);
+ if (!psm) {
+ ret = FALSE;
+ goto end;
+ }
+
+ if (!sdp_list_append(l2cap_list, psm)) {
+ ret = FALSE;
+ goto end;
+ }
+
+ proto_list = sdp_list_append(NULL, l2cap_list);
+ if (!proto_list) {
+ ret = FALSE;
+ goto end;
+ }
+
+ /* set mcap information */
+ sdp_uuid16_create(&mcap_d_uuid, MCAP_DATA_UUID);
+ mcap_list = sdp_list_append(NULL, &mcap_d_uuid);
+ if (!mcap_list) {
+ ret = FALSE;
+ goto end;
+ }
+
+ if (!sdp_list_append(proto_list, mcap_list)) {
+ ret = FALSE;
+ goto end;
+ }
+
+ /* attach protocol information to service record */
+ access_proto_list = sdp_list_append(NULL, proto_list);
+ if (!access_proto_list) {
+ ret = FALSE;
+ goto end;
+ }
+
+ if (sdp_set_add_access_protos(sdp_record, access_proto_list) < 0)
+ ret = FALSE;
+ else
+ ret = TRUE;
+
+end:
+ if (l2cap_list)
+ sdp_list_free(l2cap_list, NULL);
+ if (mcap_list)
+ sdp_list_free(mcap_list, NULL);
+ if (proto_list)
+ sdp_list_free(proto_list, NULL);
+ if (access_proto_list)
+ sdp_list_free(access_proto_list, NULL);
+ if (psm)
+ sdp_data_free(psm);
+
+ return ret;
+}
+
+static sdp_list_t *app_to_sdplist(struct hdp_application *app)
+{
+ sdp_data_t *mdepid,
+ *dtype = NULL,
+ *role = NULL,
+ *desc = NULL;
+ sdp_list_t *f_list = NULL;
+
+ mdepid = sdp_data_alloc(SDP_UINT8, &app->id);
+ if (!mdepid)
+ return NULL;
+
+ dtype = sdp_data_alloc(SDP_UINT16, &app->data_type);
+ if (!dtype)
+ goto fail;
+
+ role = sdp_data_alloc(SDP_UINT8, &app->role);
+ if (!role)
+ goto fail;
+
+ if (app->description) {
+ desc = sdp_data_alloc(SDP_TEXT_STR8, app->description);
+ if (!desc)
+ goto fail;
+ }
+
+ f_list = sdp_list_append(NULL, mdepid);
+ if (!f_list)
+ goto fail;
+
+ if (!sdp_list_append(f_list, dtype))
+ goto fail;
+
+ if (!sdp_list_append(f_list, role))
+ goto fail;
+
+ if (desc)
+ if (!sdp_list_append(f_list, desc))
+ goto fail;
+
+ return f_list;
+
+fail:
+ if (f_list)
+ sdp_list_free(f_list, NULL);
+ if (mdepid)
+ sdp_data_free(mdepid);
+ if (dtype)
+ sdp_data_free(dtype);
+ if (role)
+ sdp_data_free(role);
+ if (desc)
+ sdp_data_free(desc);
+
+ return NULL;
+}
+
+static gboolean register_features(struct hdp_application *app,
+ sdp_list_t **sup_features)
+{
+ sdp_list_t *hdp_feature;
+
+ hdp_feature = app_to_sdplist(app);
+ if (!hdp_feature)
+ goto fail;
+
+ if (!*sup_features) {
+ *sup_features = sdp_list_append(NULL, hdp_feature);
+ if (!*sup_features)
+ goto fail;
+ } else if (!sdp_list_append(*sup_features, hdp_feature)) {
+ goto fail;
+ }
+
+ return TRUE;
+
+fail:
+ if (hdp_feature)
+ sdp_list_free(hdp_feature, (sdp_free_func_t)sdp_data_free);
+ return FALSE;
+}
+
+static void free_hdp_list(void *list)
+{
+ sdp_list_t *hdp_list = list;
+
+ sdp_list_free(hdp_list, (sdp_free_func_t)sdp_data_free);
+}
+
+static gboolean register_service_sup_features(GSList *app_list,
+ sdp_record_t *sdp_record)
+{
+ GSList *l;
+ sdp_list_t *sup_features = NULL;
+
+ for (l = app_list; l; l = l->next) {
+ if (!register_features(l->data, &sup_features))
+ return FALSE;
+ }
+
+ if (sdp_set_supp_feat(sdp_record, sup_features) < 0) {
+ sdp_list_free(sup_features, free_hdp_list);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean register_data_exchange_spec(sdp_record_t *record)
+{
+ sdp_data_t *spec;
+ uint8_t data_spec = DATA_EXCHANGE_SPEC_11073;
+ /* As by now 11073 is the only supported we set it by default */
+
+ spec = sdp_data_alloc(SDP_UINT8, &data_spec);
+ if (!spec)
+ return FALSE;
+
+ if (sdp_attr_add(record, SDP_ATTR_DATA_EXCHANGE_SPEC, spec) < 0) {
+ sdp_data_free(spec);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean register_mcap_features(sdp_record_t *sdp_record)
+{
+ sdp_data_t *mcap_proc;
+ uint8_t mcap_sup_proc = MCAP_SUP_PROC;
+
+ mcap_proc = sdp_data_alloc(SDP_UINT8, &mcap_sup_proc);
+ if (!mcap_proc)
+ return FALSE;
+
+ if (sdp_attr_add(sdp_record, SDP_ATTR_MCAP_SUPPORTED_PROCEDURES,
+ mcap_proc) < 0) {
+ sdp_data_free(mcap_proc);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean hdp_update_sdp_record(struct hdp_adapter *adapter, GSList *app_list)
+{
+ sdp_record_t *sdp_record;
+ bdaddr_t addr;
+
+ if (adapter->sdp_handler)
+ remove_record_from_server(adapter->sdp_handler);
+
+ if (!app_list) {
+ adapter->sdp_handler = 0;
+ return TRUE;
+ }
+
+ sdp_record = sdp_record_alloc();
+ if (!sdp_record)
+ return FALSE;
+
+ if (adapter->sdp_handler)
+ sdp_record->handle = adapter->sdp_handler;
+ else
+ sdp_record->handle = 0xffffffff; /* Set automatically */
+
+ if (is_app_role(app_list, HDP_SINK))
+ set_sdp_services_uuid(sdp_record, HDP_SINK);
+ if (is_app_role(app_list, HDP_SOURCE))
+ set_sdp_services_uuid(sdp_record, HDP_SOURCE);
+
+ if (!register_service_protocols(adapter, sdp_record))
+ goto fail;
+ if (!register_service_profiles(sdp_record))
+ goto fail;
+ if (!register_service_additional_protocols(adapter, sdp_record))
+ goto fail;
+
+ sdp_set_info_attr(sdp_record, HDP_SERVICE_NAME, HDP_SERVICE_PROVIDER,
+ HDP_SERVICE_DSC);
+ if (!register_service_sup_features(app_list, sdp_record))
+ goto fail;
+ if (!register_data_exchange_spec(sdp_record))
+ goto fail;
+
+ register_mcap_features(sdp_record);
+
+ if (sdp_set_record_state(sdp_record, adapter->record_state++))
+ goto fail;
+
+ adapter_get_address(adapter->btd_adapter, &addr);
+
+ if (add_record_to_server(&addr, sdp_record) < 0)
+ goto fail;
+ adapter->sdp_handler = sdp_record->handle;
+ return TRUE;
+
+fail:
+ if (sdp_record)
+ sdp_record_free(sdp_record);
+ return FALSE;
+}
+
+static gboolean check_role(uint8_t rec_role, uint8_t app_role)
+{
+ if ((rec_role == HDP_SINK && app_role == HDP_SOURCE) ||
+ (rec_role == HDP_SOURCE && app_role == HDP_SINK))
+ return TRUE;
+
+ return FALSE;
+}
+
+static gboolean get_mdep_from_rec(const sdp_record_t *rec, uint8_t role,
+ uint16_t d_type, uint8_t *mdep, char **desc)
+{
+ sdp_data_t *list, *feat;
+
+ if (!desc && !mdep)
+ return TRUE;
+
+ list = sdp_data_get(rec, SDP_ATTR_SUPPORTED_FEATURES_LIST);
+
+ if (list->dtd != SDP_SEQ8 && list->dtd != SDP_SEQ16 &&
+ list->dtd != SDP_SEQ32)
+ return FALSE;
+
+ for (feat = list->val.dataseq; feat; feat = feat->next) {
+ sdp_data_t *data_type, *mdepid, *role_t, *desc_t;
+
+ if (feat->dtd != SDP_SEQ8 && feat->dtd != SDP_SEQ16 &&
+ feat->dtd != SDP_SEQ32)
+ continue;
+
+ mdepid = feat->val.dataseq;
+ if (!mdepid)
+ continue;
+
+ data_type = mdepid->next;
+ if (!data_type)
+ continue;
+
+ role_t = data_type->next;
+ if (!role_t)
+ continue;
+
+ desc_t = role_t->next;
+
+ if (data_type->dtd != SDP_UINT16 || mdepid->dtd != SDP_UINT8 ||
+ role_t->dtd != SDP_UINT8)
+ continue;
+
+ if (data_type->val.uint16 != d_type ||
+ !check_role(role_t->val.uint8, role))
+ continue;
+
+ if (mdep)
+ *mdep = mdepid->val.uint8;
+
+ if (desc && desc_t && (desc_t->dtd == SDP_TEXT_STR8 ||
+ desc_t->dtd == SDP_TEXT_STR16 ||
+ desc_t->dtd == SDP_TEXT_STR32))
+ *desc = g_strdup(desc_t->val.str);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void get_mdep_cb(sdp_list_t *recs, int err, gpointer user_data)
+{
+ struct get_mdep_data *mdep_data = user_data;
+ GError *gerr = NULL;
+ uint8_t mdep;
+
+ if (err || !recs) {
+ g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
+ "Error getting remote SDP records");
+ mdep_data->func(0, mdep_data->data, gerr);
+ g_error_free(gerr);
+ return;
+ }
+
+ if (!get_mdep_from_rec(recs->data, mdep_data->app->role,
+ mdep_data->app->data_type, &mdep, NULL)) {
+ g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
+ "No matching MDEP found");
+ mdep_data->func(0, mdep_data->data, gerr);
+ g_error_free(gerr);
+ return;
+ }
+
+ mdep_data->func(mdep, mdep_data->data, NULL);
+}
+
+static void free_mdep_data(gpointer data)
+{
+ struct get_mdep_data *mdep_data = data;
+
+ if (mdep_data->destroy)
+ mdep_data->destroy(mdep_data->data);
+ hdp_application_unref(mdep_data->app);
+
+ g_free(mdep_data);
+}
+
+gboolean hdp_get_mdep(struct hdp_device *device, struct hdp_application *app,
+ hdp_continue_mdep_f func, gpointer data,
+ GDestroyNotify destroy, GError **err)
+{
+ struct get_mdep_data *mdep_data;
+ bdaddr_t dst, src;
+ uuid_t uuid;
+
+ device_get_address(device->dev, &dst);
+ adapter_get_address(device_get_adapter(device->dev), &src);
+
+ mdep_data = g_new0(struct get_mdep_data, 1);
+ mdep_data->app = hdp_application_ref(app);
+ mdep_data->func = func;
+ mdep_data->data = data;
+ mdep_data->destroy = destroy;
+
+ bt_string2uuid(&uuid, HDP_UUID);
+ if (bt_search_service(&src, &dst, &uuid, get_mdep_cb, mdep_data,
+ free_mdep_data)) {
+ g_set_error(err, HDP_ERROR, HDP_CONNECTION_ERROR,
+ "Can't get remote SDP record");
+ g_free(mdep_data);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean get_prot_desc_entry(sdp_data_t *entry, int type, guint16 *val)
+{
+ sdp_data_t *iter;
+ int proto;
+
+ if (!entry || (entry->dtd != SDP_SEQ8 && entry->dtd != SDP_SEQ16 &&
+ entry->dtd != SDP_SEQ32))
+ return FALSE;
+
+ iter = entry->val.dataseq;
+ if (!(iter->dtd & SDP_UUID_UNSPEC))
+ return FALSE;
+
+ proto = sdp_uuid_to_proto(&iter->val.uuid);
+ if (proto != type)
+ return FALSE;
+
+ if (!val)
+ return TRUE;
+
+ iter = iter->next;
+ if (iter->dtd != SDP_UINT16)
+ return FALSE;
+
+ *val = iter->val.uint16;
+
+ return TRUE;
+}
+
+static gboolean hdp_get_prot_desc_list(const sdp_record_t *rec, guint16 *psm,
+ guint16 *version)
+{
+ sdp_data_t *pdl, *p0, *p1;
+
+ if (!psm && !version)
+ return TRUE;
+
+ pdl = sdp_data_get(rec, SDP_ATTR_PROTO_DESC_LIST);
+ if (pdl->dtd != SDP_SEQ8 && pdl->dtd != SDP_SEQ16 &&
+ pdl->dtd != SDP_SEQ32)
+ return FALSE;
+
+ p0 = pdl->val.dataseq;
+ if (!get_prot_desc_entry(p0, L2CAP_UUID, psm))
+ return FALSE;
+
+ p1 = p0->next;
+ if (!get_prot_desc_entry(p1, MCAP_CTRL_UUID, version))
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean hdp_get_add_prot_desc_list(const sdp_record_t *rec,
+ guint16 *psm)
+{
+ sdp_data_t *pdl, *p0, *p1;
+
+ if (!psm)
+ return TRUE;
+
+ pdl = sdp_data_get(rec, SDP_ATTR_ADD_PROTO_DESC_LIST);
+ if (pdl->dtd != SDP_SEQ8)
+ return FALSE;
+ pdl = pdl->val.dataseq;
+ if (pdl->dtd != SDP_SEQ8)
+ return FALSE;
+
+ p0 = pdl->val.dataseq;
+
+ if (!get_prot_desc_entry(p0, L2CAP_UUID, psm))
+ return FALSE;
+ p1 = p0->next;
+ if (!get_prot_desc_entry(p1, MCAP_DATA_UUID, NULL))
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean get_ccpsm(sdp_list_t *recs, uint16_t *ccpsm)
+{
+ sdp_list_t *l;
+
+ for (l = recs; l; l = l->next) {
+ sdp_record_t *rec = l->data;
+
+ if (hdp_get_prot_desc_list(rec, ccpsm, NULL))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean get_dcpsm(sdp_list_t *recs, uint16_t *dcpsm)
+{
+ sdp_list_t *l;
+
+ for (l = recs; l; l = l->next) {
+ sdp_record_t *rec = l->data;
+
+ if (hdp_get_add_prot_desc_list(rec, dcpsm))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void con_mcl_data_unref(struct conn_mcl_data *conn_data)
+{
+ if (!conn_data)
+ return;
+
+ if (--conn_data->refs > 0)
+ return;
+
+ if (conn_data->destroy)
+ conn_data->destroy(conn_data->data);
+
+ health_device_unref(conn_data->dev);
+ g_free(conn_data);
+}
+
+static void destroy_con_mcl_data(gpointer data)
+{
+ con_mcl_data_unref(data);
+}
+
+static struct conn_mcl_data *con_mcl_data_ref(struct conn_mcl_data *conn_data)
+{
+ if (!conn_data)
+ return NULL;
+
+ conn_data->refs++;
+ return conn_data;
+}
+
+static void create_mcl_cb(struct mcap_mcl *mcl, GError *err, gpointer data)
+{
+ struct conn_mcl_data *conn_data = data;
+ struct hdp_device *device = conn_data->dev;
+ GError *gerr = NULL;
+
+ if (err) {
+ conn_data->func(conn_data->data, err);
+ return;
+ }
+
+ if (!device->mcl)
+ device->mcl = mcap_mcl_ref(mcl);
+ device->mcl_conn = TRUE;
+
+ hdp_set_mcl_cb(device, &gerr);
+
+ conn_data->func(conn_data->data, gerr);
+ if (gerr)
+ g_error_free(gerr);
+}
+
+static void search_cb(sdp_list_t *recs, int err, gpointer user_data)
+{
+ struct conn_mcl_data *conn_data = user_data;
+ GError *gerr = NULL;
+ bdaddr_t dst;
+ uint16_t ccpsm;
+
+ if (!conn_data->dev->hdp_adapter->mi) {
+ g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
+ "Mcap instance released");
+ goto fail;
+ }
+
+ if (err || !recs) {
+ g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
+ "Error getting remote SDP records");
+ goto fail;
+ }
+
+ if (!get_ccpsm(recs, &ccpsm)) {
+ g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
+ "Can't get remote PSM for control channel");
+ goto fail;
+ }
+
+ conn_data = con_mcl_data_ref(conn_data);
+
+ device_get_address(conn_data->dev->dev, &dst);
+ if (!mcap_create_mcl(conn_data->dev->hdp_adapter->mi, &dst, ccpsm,
+ create_mcl_cb, conn_data,
+ destroy_con_mcl_data, &gerr)) {
+ con_mcl_data_unref(conn_data);
+ goto fail;
+ }
+ return;
+fail:
+ conn_data->func(conn_data->data, gerr);
+ g_error_free(gerr);
+}
+
+gboolean hdp_establish_mcl(struct hdp_device *device,
+ hdp_continue_proc_f func,
+ gpointer data,
+ GDestroyNotify destroy,
+ GError **err)
+{
+ struct conn_mcl_data *conn_data;
+ bdaddr_t dst, src;
+ uuid_t uuid;
+
+ device_get_address(device->dev, &dst);
+ adapter_get_address(device_get_adapter(device->dev), &src);
+
+ conn_data = g_new0(struct conn_mcl_data, 1);
+ conn_data->refs = 1;
+ conn_data->func = func;
+ conn_data->data = data;
+ conn_data->destroy = destroy;
+ conn_data->dev = health_device_ref(device);
+
+ bt_string2uuid(&uuid, HDP_UUID);
+ if (bt_search_service(&src, &dst, &uuid, search_cb, conn_data,
+ destroy_con_mcl_data)) {
+ g_set_error(err, HDP_ERROR, HDP_CONNECTION_ERROR,
+ "Can't get remote SDP record");
+ g_free(conn_data);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void get_dcpsm_cb(sdp_list_t *recs, int err, gpointer data)
+{
+ struct get_dcpsm_data *dcpsm_data = data;
+ GError *gerr = NULL;
+ uint16_t dcpsm;
+
+ if (err || !recs) {
+ g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
+ "Error getting remote SDP records");
+ goto fail;
+ }
+
+ if (!get_dcpsm(recs, &dcpsm)) {
+ g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
+ "Can't get remote PSM for data channel");
+ goto fail;
+ }
+
+ dcpsm_data->func(dcpsm, dcpsm_data->data, NULL);
+ return;
+
+fail:
+ dcpsm_data->func(0, dcpsm_data->data, gerr);
+ g_error_free(gerr);
+}
+
+static void free_dcpsm_data(gpointer data)
+{
+ struct get_dcpsm_data *dcpsm_data = data;
+
+ if (!dcpsm_data)
+ return;
+
+ if (dcpsm_data->destroy)
+ dcpsm_data->destroy(dcpsm_data->data);
+
+ g_free(dcpsm_data);
+}
+
+gboolean hdp_get_dcpsm(struct hdp_device *device, hdp_continue_dcpsm_f func,
+ gpointer data,
+ GDestroyNotify destroy,
+ GError **err)
+{
+ struct get_dcpsm_data *dcpsm_data;
+ bdaddr_t dst, src;
+ uuid_t uuid;
+
+ device_get_address(device->dev, &dst);
+ adapter_get_address(device_get_adapter(device->dev), &src);
+
+ dcpsm_data = g_new0(struct get_dcpsm_data, 1);
+ dcpsm_data->func = func;
+ dcpsm_data->data = data;
+ dcpsm_data->destroy = destroy;
+
+ bt_string2uuid(&uuid, HDP_UUID);
+ if (bt_search_service(&src, &dst, &uuid, get_dcpsm_cb, dcpsm_data,
+ free_dcpsm_data)) {
+ g_set_error(err, HDP_ERROR, HDP_CONNECTION_ERROR,
+ "Can't get remote SDP record");
+ g_free(dcpsm_data);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void hdp_free_application(struct hdp_application *app)
+{
+ if (app->dbus_watcher)
+ g_dbus_remove_watch(app->conn, app->dbus_watcher);
+
+ if (app->conn)
+ dbus_connection_unref(app->conn);
+ g_free(app->oname);
+ g_free(app->description);
+ g_free(app->path);
+ g_free(app);
+}
+
+struct hdp_application *hdp_application_ref(struct hdp_application *app)
+{
+ if (!app)
+ return NULL;
+
+ app->ref++;
+
+ DBG("health_application_ref(%p): ref=%d", app, app->ref);
+ return app;
+}
+
+void hdp_application_unref(struct hdp_application *app)
+{
+ if (!app)
+ return;
+
+ app->ref --;
+
+ DBG("health_application_unref(%p): ref=%d", app, app->ref);
+ if (app->ref > 0)
+ return;
+
+ hdp_free_application(app);
+}
diff --git a/health/hdp_util.h b/health/hdp_util.h
new file mode 100644
index 0000000..fc59f50
--- /dev/null
+++ b/health/hdp_util.h
@@ -0,0 +1,61 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ * Authors:
+ * Santiago Carot Nemesio <sancane at gmail.com>
+ * Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
+ *
+ * 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
+ *
+ */
+
+#ifndef __HDP_UTIL_H__
+#define __HDP_UTIL_H__
+
+typedef void (*hdp_continue_mdep_f)(uint8_t mdep, gpointer user_data,
+ GError *err);
+typedef void (*hdp_continue_dcpsm_f)(uint16_t dcpsm, gpointer user_data,
+ GError *err);
+typedef void (*hdp_continue_proc_f)(gpointer user_data, GError *err);
+
+struct hdp_application *hdp_get_app_config(DBusMessageIter *iter, GError **err);
+gboolean hdp_update_sdp_record(struct hdp_adapter *adapter, GSList *app_list);
+gboolean hdp_get_mdep(struct hdp_device *device, struct hdp_application *app,
+ hdp_continue_mdep_f func,
+ gpointer data, GDestroyNotify destroy,
+ GError **err);
+
+gboolean hdp_establish_mcl(struct hdp_device *device,
+ hdp_continue_proc_f func,
+ gpointer data,
+ GDestroyNotify destroy,
+ GError **err);
+
+gboolean hdp_get_dcpsm(struct hdp_device *device, hdp_continue_dcpsm_f func,
+ gpointer data,
+ GDestroyNotify destroy,
+ GError **err);
+
+
+struct hdp_application *hdp_application_ref(struct hdp_application *app);
+void hdp_application_unref(struct hdp_application *app);
+
+struct hdp_device *health_device_ref(struct hdp_device *hdp_dev);
+void health_device_unref(struct hdp_device *hdp_dev);
+
+
+#endif /* __HDP_UTIL_H__ */
diff --git a/health/mcap.c b/health/mcap.c
new file mode 100644
index 0000000..48866d4
--- /dev/null
+++ b/health/mcap.c
@@ -0,0 +1,2195 @@
+/*
+ *
+ * MCAP for BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ * Authors:
+ * Santiago Carot-Nemesio <sancane at gmail.com>
+ * Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
+ *
+ * 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
+ *
+ */
+
+#include "log.h"
+#include "error.h"
+
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "btio.h"
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#include "mcap.h"
+#include "mcap_lib.h"
+#include "mcap_internal.h"
+
+#define RESPONSE_TIMER 6 /* seconds */
+#define MAX_CACHED 10 /* 10 devices */
+
+#define MCAP_ERROR g_quark_from_static_string("mcap-error-quark")
+
+#define RELEASE_TIMER(__mcl) do { \
+ if (__mcl->tid) { \
+ g_source_remove(__mcl->tid); \
+ __mcl->tid = 0; \
+ } \
+} while(0)
+
+struct connect_mcl {
+ struct mcap_mcl *mcl; /* MCL for this operation */
+ mcap_mcl_connect_cb connect_cb; /* Connect callback */
+ GDestroyNotify destroy; /* Destroy callback */
+ gpointer user_data; /* Callback user data */
+};
+
+typedef union {
+ mcap_mdl_operation_cb op;
+ mcap_mdl_operation_conf_cb op_conf;
+ mcap_mdl_notify_cb notify;
+} mcap_cb_type;
+
+struct mcap_mdl_op_cb {
+ struct mcap_mdl *mdl; /* MDL for this operation */
+ mcap_cb_type cb; /* Operation callback */
+ GDestroyNotify destroy; /* Destroy callback */
+ gpointer user_data; /* Callback user data */
+};
+
+/* MCAP finite state machine functions */
+static void proc_req_connected(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t l);
+static void proc_req_pending(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t l);
+static void proc_req_active(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t l);
+
+static void (*proc_req[])(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) = {
+ proc_req_connected,
+ proc_req_pending,
+ proc_req_active
+};
+
+static void mcap_cache_mcl(struct mcap_mcl *mcl);
+
+static void default_mdl_connected_cb(struct mcap_mdl *mdl, gpointer data)
+{
+ DBG("MCAP Unmanaged mdl connection");
+}
+
+static void default_mdl_closed_cb(struct mcap_mdl *mdl, gpointer data)
+{
+ DBG("MCAP Unmanaged mdl closed");
+}
+
+static void default_mdl_deleted_cb(struct mcap_mdl *mdl, gpointer data)
+{
+ DBG("MCAP Unmanaged mdl deleted");
+}
+
+static void default_mdl_aborted_cb(struct mcap_mdl *mdl, gpointer data)
+{
+ DBG("MCAP Unmanaged mdl aborted");
+}
+
+static uint8_t default_mdl_conn_req_cb(struct mcap_mcl *mcl,
+ uint8_t mdepid, uint16_t mdlid,
+ uint8_t *conf, gpointer data)
+{
+ DBG("MCAP mdl remote connection aborted");
+ /* Due to this callback isn't managed this request won't be supported */
+ return MCAP_REQUEST_NOT_SUPPORTED;
+}
+
+static uint8_t default_mdl_reconn_req_cb(struct mcap_mdl *mdl,
+ gpointer data)
+{
+ DBG("MCAP mdl remote reconnection aborted");
+ /* Due to this callback isn't managed this request won't be supported */
+ return MCAP_REQUEST_NOT_SUPPORTED;
+}
+
+static void set_default_cb(struct mcap_mcl *mcl)
+{
+ if (!mcl->cb)
+ mcl->cb = g_new0(struct mcap_mdl_cb, 1);
+
+ mcl->cb->mdl_connected = default_mdl_connected_cb;
+ mcl->cb->mdl_closed = default_mdl_closed_cb;
+ mcl->cb->mdl_deleted = default_mdl_deleted_cb;
+ mcl->cb->mdl_aborted = default_mdl_aborted_cb;
+ mcl->cb->mdl_conn_req = default_mdl_conn_req_cb;
+ mcl->cb->mdl_reconn_req = default_mdl_reconn_req_cb;
+}
+
+static char *error2str(uint8_t rc)
+{
+ switch (rc) {
+ case MCAP_SUCCESS:
+ return "Success";
+ case MCAP_INVALID_OP_CODE:
+ return "Invalid Op Code";
+ case MCAP_INVALID_PARAM_VALUE:
+ return "Invalid Parameter Value";
+ case MCAP_INVALID_MDEP:
+ return "Invalid MDEP";
+ case MCAP_MDEP_BUSY:
+ return "MDEP Busy";
+ case MCAP_INVALID_MDL:
+ return "Invalid MDL";
+ case MCAP_MDL_BUSY:
+ return "MDL Busy";
+ case MCAP_INVALID_OPERATION:
+ return "Invalid Operation";
+ case MCAP_RESOURCE_UNAVAILABLE:
+ return "Resource Unavailable";
+ case MCAP_UNSPECIFIED_ERROR:
+ return "Unspecified Error";
+ case MCAP_REQUEST_NOT_SUPPORTED:
+ return "Request Not Supported";
+ case MCAP_CONFIGURATION_REJECTED:
+ return "Configuration Rejected";
+ default:
+ return "Unknown Response Code";
+ }
+}
+
+static gboolean mcap_send_std_opcode(struct mcap_mcl *mcl, void *cmd,
+ uint32_t size, GError **err)
+{
+ if (mcl->state == MCL_IDLE) {
+ g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
+ "MCL is not connected");
+ return FALSE;
+ }
+
+ if (mcl->req != MCL_AVAILABLE) {
+ g_set_error(err, MCAP_ERROR, MCAP_ERROR_RESOURCE_UNAVAILABLE,
+ "Pending request");
+ return FALSE;
+ }
+
+ if (!(mcl->ctrl & MCAP_CTRL_STD_OP)) {
+ g_set_error(err, MCAP_ERROR, MCAP_ERROR_REQUEST_NOT_SUPPORTED,
+ "Remote does not support standard opcodes");
+ return FALSE;
+ }
+
+ if (mcl->state == MCL_PENDING) {
+ g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_OPERATION,
+ "Not Std Op. Codes can be sent in PENDING State");
+ return FALSE;
+ }
+
+ if (mcap_send_data(g_io_channel_unix_get_fd(mcl->cc), cmd, size) < 0) {
+ g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
+ "Command can't be sent, write error");
+ return FALSE;
+ }
+
+ mcl->lcmd = cmd;
+ mcl->req = MCL_WAITING_RSP;
+
+ return TRUE;
+}
+
+static void update_mcl_state(struct mcap_mcl *mcl)
+{
+ GSList *l;
+ struct mcap_mdl *mdl;
+
+ if (mcl->state == MCL_PENDING)
+ return;
+
+ for (l = mcl->mdls; l; l = l->next) {
+ mdl = l->data;
+
+ if (mdl->state == MDL_CONNECTED) {
+ mcl->state = MCL_ACTIVE;
+ return;
+ }
+ }
+
+ mcl->state = MCL_CONNECTED;
+}
+
+static void shutdown_mdl(struct mcap_mdl *mdl)
+{
+ mdl->state = MDL_CLOSED;
+
+ if (mdl->wid) {
+ g_source_remove(mdl->wid);
+ mdl->wid = 0;
+ }
+
+ if (mdl->dc) {
+ g_io_channel_shutdown(mdl->dc, TRUE, NULL);
+ g_io_channel_unref(mdl->dc);
+ mdl->dc = NULL;
+ }
+}
+
+static void free_mdl(struct mcap_mdl *mdl)
+{
+ if (!mdl)
+ return;
+
+ mcap_mcl_unref(mdl->mcl);
+ g_free(mdl);
+}
+
+static gint cmp_mdl_state(gconstpointer a, gconstpointer b)
+{
+ const struct mcap_mdl *mdl = a;
+ const MDLState *st = b;
+
+ if (mdl->state == *st)
+ return 0;
+ else if (mdl->state < *st)
+ return -1;
+ else
+ return 1;
+}
+
+static void free_mcap_mdl_op(struct mcap_mdl_op_cb *op)
+{
+ if (op->destroy)
+ op->destroy(op->user_data);
+
+ if (op->mdl)
+ mcap_mdl_unref(op->mdl);
+
+ g_free(op);
+}
+
+static void free_mcl_priv_data(struct mcap_mcl *mcl)
+{
+ free_mcap_mdl_op(mcl->priv_data);
+ mcl->priv_data = NULL;
+}
+
+static void mcap_notify_error(struct mcap_mcl *mcl, GError *err)
+{
+ struct mcap_mdl_op_cb *con = mcl->priv_data;
+ struct mcap_mdl *mdl;
+ MDLState st;
+ GSList *l;
+
+ if (!con || !mcl->lcmd)
+ return;
+
+ switch (mcl->lcmd[0]) {
+ case MCAP_MD_CREATE_MDL_REQ:
+ st = MDL_WAITING;
+ l = g_slist_find_custom(mcl->mdls, &st, cmp_mdl_state);
+ mdl = l->data;
+ mcl->mdls = g_slist_remove(mcl->mdls, mdl);
+ mcap_mdl_unref(mdl);
+ update_mcl_state(mcl);
+ con->cb.op_conf(NULL, 0, err, con->user_data);
+ break;
+ case MCAP_MD_ABORT_MDL_REQ:
+ st = MDL_WAITING;
+ l = g_slist_find_custom(mcl->mdls, &st, cmp_mdl_state);
+ shutdown_mdl(l->data);
+ update_mcl_state(mcl);
+ con->cb.notify(err, con->user_data);
+ break;
+ case MCAP_MD_DELETE_MDL_REQ:
+ for (l = mcl->mdls; l; l = l->next) {
+ mdl = l->data;
+ if (mdl->state == MDL_DELETING)
+ mdl->state = (mdl->dc) ? MDL_CONNECTED :
+ MDL_CLOSED;
+ }
+ update_mcl_state(mcl);
+ con->cb.notify(err, con->user_data);
+ break;
+ case MCAP_MD_RECONNECT_MDL_REQ:
+ st = MDL_WAITING;
+ l = g_slist_find_custom(mcl->mdls, &st, cmp_mdl_state);
+ shutdown_mdl(l->data);
+ update_mcl_state(mcl);
+ con->cb.op(NULL, err, con->user_data);
+ break;
+ }
+
+ free_mcl_priv_data(mcl);
+ g_free(mcl->lcmd);
+ mcl->lcmd = NULL;
+}
+
+int mcap_send_data(int sock, const void *buf, uint32_t size)
+{
+ const uint8_t *buf_b = buf;
+ uint32_t sent = 0;
+
+ while (sent < size) {
+ int n = write(sock, buf_b + sent, size - sent);
+ if (n < 0)
+ return -1;
+ sent += n;
+ }
+
+ return 0;
+}
+
+static int mcap_send_cmd(struct mcap_mcl *mcl, uint8_t oc, uint8_t rc,
+ uint16_t mdl, uint8_t *data, size_t len)
+{
+ mcap_rsp *cmd;
+ int sock, sent;
+
+ if (mcl->cc == NULL)
+ return -1;
+
+ sock = g_io_channel_unix_get_fd(mcl->cc);
+
+ cmd = g_malloc(sizeof(mcap_rsp) + len);
+ cmd->op = oc;
+ cmd->rc = rc;
+ cmd->mdl = htons(mdl);
+
+ if (data && len > 0)
+ memcpy(cmd->data, data, len);
+
+ sent = mcap_send_data(sock, cmd, sizeof(mcap_rsp) + len);
+ g_free(cmd);
+
+ return sent;
+}
+
+static struct mcap_mdl *get_mdl(struct mcap_mcl *mcl, uint16_t mdlid)
+{
+ GSList *l;
+ struct mcap_mdl *mdl;
+
+ for (l = mcl->mdls; l; l = l->next) {
+ mdl = l->data;
+ if (mdlid == mdl->mdlid)
+ return mdl;
+ }
+
+ return NULL;
+}
+
+static uint16_t generate_mdlid(struct mcap_mcl *mcl)
+{
+ uint16_t mdlid = mcl->next_mdl;
+ struct mcap_mdl *mdl;
+
+ do {
+ mdl = get_mdl(mcl, mdlid);
+ if (!mdl) {
+ mcl->next_mdl = (mdlid % MCAP_MDLID_FINAL) + 1;
+ return mdlid;
+ } else
+ mdlid = (mdlid % MCAP_MDLID_FINAL) + 1;
+ } while (mdlid != mcl->next_mdl);
+
+ /* No more mdlids availables */
+ return 0;
+}
+
+static mcap_md_req *create_req(uint8_t op, uint16_t mdl_id)
+{
+ mcap_md_req *req_cmd;
+
+ req_cmd = g_new0(mcap_md_req, 1);
+
+ req_cmd->op = op;
+ req_cmd->mdl = htons(mdl_id);
+
+ return req_cmd;
+}
+
+static mcap_md_create_mdl_req *create_mdl_req(uint16_t mdl_id, uint8_t mdep,
+ uint8_t conf)
+{
+ mcap_md_create_mdl_req *req_mdl;
+
+ req_mdl = g_new0(mcap_md_create_mdl_req, 1);
+
+ req_mdl->op = MCAP_MD_CREATE_MDL_REQ;
+ req_mdl->mdl = htons(mdl_id);
+ req_mdl->mdep = mdep;
+ req_mdl->conf = conf;
+
+ return req_mdl;
+}
+
+static gint compare_mdl(gconstpointer a, gconstpointer b)
+{
+ const struct mcap_mdl *mdla = a;
+ const struct mcap_mdl *mdlb = b;
+
+ if (mdla->mdlid == mdlb->mdlid)
+ return 0;
+ else if (mdla->mdlid < mdlb->mdlid)
+ return -1;
+ else
+ return 1;
+}
+
+static gboolean wait_response_timer(gpointer data)
+{
+ struct mcap_mcl *mcl = data;
+
+ GError *gerr = NULL;
+
+ RELEASE_TIMER(mcl);
+
+ g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_FAILED,
+ "Timeout waiting response");
+
+ mcap_notify_error(mcl, gerr);
+
+ g_error_free(gerr);
+ mcl->mi->mcl_disconnected_cb(mcl, mcl->mi->user_data);
+ mcap_cache_mcl(mcl);
+
+ return FALSE;
+}
+
+gboolean mcap_create_mdl(struct mcap_mcl *mcl,
+ uint8_t mdepid,
+ uint8_t conf,
+ mcap_mdl_operation_conf_cb connect_cb,
+ gpointer user_data,
+ GDestroyNotify destroy,
+ GError **err)
+{
+ struct mcap_mdl *mdl;
+ struct mcap_mdl_op_cb *con;
+ mcap_md_create_mdl_req *cmd;
+ uint16_t id;
+
+ id = generate_mdlid(mcl);
+ if (!id) {
+ g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
+ "Not more mdlids available");
+ return FALSE;
+ }
+
+ mdl = g_new0(struct mcap_mdl, 1);
+ mdl->mcl = mcap_mcl_ref(mcl);
+ mdl->mdlid = id;
+ mdl->mdep_id = mdepid;
+ mdl->state = MDL_WAITING;
+
+ con = g_new0(struct mcap_mdl_op_cb, 1);
+ con->mdl = mcap_mdl_ref(mdl);
+ con->cb.op_conf = connect_cb;
+ con->destroy = destroy;
+ con->user_data = user_data;
+
+ cmd = create_mdl_req(id, mdepid, conf);
+ if (!mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_create_mdl_req),
+ err)) {
+ mcap_mdl_unref(con->mdl);
+ g_free(con);
+ g_free(cmd);
+ return FALSE;
+ }
+
+ mcl->state = MCL_ACTIVE;
+ mcl->priv_data = con;
+
+ mcl->mdls = g_slist_insert_sorted(mcl->mdls, mcap_mdl_ref(mdl),
+ compare_mdl);
+ mcl->tid = g_timeout_add_seconds(RESPONSE_TIMER, wait_response_timer,
+ mcl);
+ return TRUE;
+}
+
+gboolean mcap_reconnect_mdl(struct mcap_mdl *mdl,
+ mcap_mdl_operation_cb reconnect_cb,
+ gpointer user_data,
+ GDestroyNotify destroy,
+ GError **err)
+{
+ struct mcap_mdl_op_cb *con;
+ struct mcap_mcl *mcl = mdl->mcl;
+ mcap_md_req *cmd;
+
+ if (mdl->state != MDL_CLOSED) {
+ g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
+ "MDL is not closed");
+ return FALSE;
+ }
+
+ cmd = create_req(MCAP_MD_RECONNECT_MDL_REQ, mdl->mdlid);
+ if (!mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_req), err)) {
+ g_free(cmd);
+ return FALSE;
+ }
+
+ mdl->state = MDL_WAITING;
+
+ con = g_new0(struct mcap_mdl_op_cb, 1);
+ con->mdl = mcap_mdl_ref(mdl);
+ con->cb.op = reconnect_cb;
+ con->destroy = destroy;
+ con->user_data = user_data;
+
+ mcl->state = MCL_ACTIVE;
+ mcl->priv_data = con;
+
+ mcl->tid = g_timeout_add_seconds(RESPONSE_TIMER, wait_response_timer,
+ mcl);
+ return TRUE;
+}
+
+static gboolean send_delete_req(struct mcap_mcl *mcl,
+ struct mcap_mdl_op_cb *con,
+ uint16_t mdlid,
+ GError **err)
+{
+ mcap_md_req *cmd;
+
+ cmd = create_req(MCAP_MD_DELETE_MDL_REQ, mdlid);
+ if (!mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_req), err)) {
+ g_free(cmd);
+ return FALSE;
+ }
+
+ mcl->priv_data = con;
+
+ mcl->tid = g_timeout_add_seconds(RESPONSE_TIMER, wait_response_timer,
+ mcl);
+ return TRUE;
+}
+
+gboolean mcap_delete_all_mdls(struct mcap_mcl *mcl,
+ mcap_mdl_notify_cb delete_cb,
+ gpointer user_data,
+ GDestroyNotify destroy,
+ GError **err)
+{
+ GSList *l;
+ struct mcap_mdl *mdl;
+ struct mcap_mdl_op_cb *con;
+
+ DBG("MCL in state: %d", mcl->state);
+ if (!mcl->mdls) {
+ g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
+ "There are not MDLs created");
+ return FALSE;
+ }
+
+ for (l = mcl->mdls; l; l = l->next) {
+ mdl = l->data;
+ if (mdl->state != MDL_WAITING)
+ mdl->state = MDL_DELETING;
+ }
+
+ con = g_new0(struct mcap_mdl_op_cb, 1);
+ con->mdl = NULL;
+ con->cb.notify = delete_cb;
+ con->destroy = destroy;
+ con->user_data = user_data;
+
+
+ if (!send_delete_req(mcl, con, MCAP_ALL_MDLIDS, err)) {
+ g_free(con);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean mcap_delete_mdl(struct mcap_mdl *mdl, mcap_mdl_notify_cb delete_cb,
+ gpointer user_data,
+ GDestroyNotify destroy,
+ GError **err)
+{
+ struct mcap_mcl *mcl= mdl->mcl;
+ struct mcap_mdl_op_cb *con;
+ GSList *l;
+
+ l = g_slist_find(mcl->mdls, mdl);
+
+ if (!l) {
+ g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_MDL,
+ "%s" , error2str(MCAP_INVALID_MDEP));
+ return FALSE;
+ }
+
+ if (mdl->state == MDL_WAITING) {
+ g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
+ "Mdl is not created");
+ return FALSE;
+ }
+
+ mdl->state = MDL_DELETING;
+
+ con = g_new0(struct mcap_mdl_op_cb, 1);
+ con->mdl = mcap_mdl_ref(mdl);
+ con->cb.notify = delete_cb;
+ con->destroy = destroy;
+ con->user_data = user_data;
+
+ if (!send_delete_req(mcl, con, mdl->mdlid, err)) {
+ mcap_mdl_unref(con->mdl);
+ g_free(con);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean mcap_mdl_abort(struct mcap_mdl *mdl, mcap_mdl_notify_cb abort_cb,
+ gpointer user_data,
+ GDestroyNotify destroy,
+ GError **err)
+{
+ struct mcap_mdl_op_cb *con;
+ struct mcap_mcl *mcl = mdl->mcl;
+ mcap_md_req *cmd;
+
+ if (mdl->state != MDL_WAITING) {
+ g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
+ "Mdl in invalid state");
+ return FALSE;
+ }
+
+ cmd = create_req(MCAP_MD_ABORT_MDL_REQ, mdl->mdlid);
+ if (!mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_req), err)) {
+ g_free(cmd);
+ return FALSE;
+ }
+
+ con = g_new0(struct mcap_mdl_op_cb, 1);
+ con->mdl = mcap_mdl_ref(mdl);
+ con->cb.notify = abort_cb;
+ con->destroy = destroy;
+ con->user_data = user_data;
+
+ mcl->priv_data = con;
+ mcl->tid = g_timeout_add_seconds(RESPONSE_TIMER, wait_response_timer,
+ mcl);
+ return TRUE;
+}
+
+static struct mcap_mcl *find_mcl(GSList *list, const bdaddr_t *addr)
+{
+ GSList *l;
+ struct mcap_mcl *mcl;
+
+ for (l = list; l; l = l->next) {
+ mcl = l->data;
+
+ if (!bacmp(&mcl->addr, addr))
+ return mcl;
+ }
+
+ return NULL;
+}
+
+int mcap_mdl_get_fd(struct mcap_mdl *mdl)
+{
+ if (!mdl || mdl->state != MDL_CONNECTED)
+ return -ENOTCONN;
+
+ return g_io_channel_unix_get_fd(mdl->dc);
+}
+
+uint16_t mcap_mdl_get_mdlid(struct mcap_mdl *mdl)
+{
+ if (!mdl)
+ return MCAP_MDLID_RESERVED;
+
+ return mdl->mdlid;
+}
+
+static void close_mcl(struct mcap_mcl *mcl, gboolean cache_requested)
+{
+ gboolean save = ((!(mcl->ctrl & MCAP_CTRL_FREE)) && cache_requested);
+
+ RELEASE_TIMER(mcl);
+
+ if (mcl->cc) {
+ g_io_channel_shutdown(mcl->cc, TRUE, NULL);
+ g_io_channel_unref(mcl->cc);
+ mcl->cc = NULL;
+ }
+
+ if (mcl->wid) {
+ g_source_remove(mcl->wid);
+ mcl->wid = 0;
+ }
+
+ if (mcl->lcmd) {
+ g_free(mcl->lcmd);
+ mcl->lcmd = NULL;
+ }
+
+ if (mcl->priv_data)
+ free_mcl_priv_data(mcl);
+
+ g_slist_foreach(mcl->mdls, (GFunc) shutdown_mdl, NULL);
+
+ mcap_sync_stop(mcl);
+
+ mcl->state = MCL_IDLE;
+
+ if (save)
+ return;
+
+ g_slist_foreach(mcl->mdls, (GFunc) mcap_mdl_unref, NULL);
+ g_slist_free(mcl->mdls);
+ mcl->mdls = NULL;
+}
+
+static void mcap_mcl_shutdown(struct mcap_mcl *mcl)
+{
+ close_mcl(mcl, TRUE);
+}
+
+static void mcap_mcl_release(struct mcap_mcl *mcl)
+{
+ close_mcl(mcl, FALSE);
+}
+
+static void mcap_cache_mcl(struct mcap_mcl *mcl)
+{
+ GSList *l;
+ struct mcap_mcl *last;
+ int len;
+
+ if (mcl->ctrl & MCAP_CTRL_CACHED)
+ return;
+
+ mcl->mi->mcls = g_slist_remove(mcl->mi->mcls, mcl);
+
+ if (mcl->ctrl & MCAP_CTRL_NOCACHE) {
+ mcl->mi->cached = g_slist_remove(mcl->mi->cached, mcl);
+ mcap_mcl_release(mcl);
+ mcap_mcl_unref(mcl);
+ return;
+ }
+
+ DBG("Caching MCL");
+
+ len = g_slist_length(mcl->mi->cached);
+ if (len == MAX_CACHED) {
+ /* Remove the latest cached mcl */
+ l = g_slist_last(mcl->mi->cached);
+ last = l->data;
+ mcl->mi->cached = g_slist_remove(mcl->mi->cached, last);
+ last->ctrl &= ~MCAP_CTRL_CACHED;
+ if (last->ctrl & MCAP_CTRL_CONN) {
+ /* We have to release this MCL if */
+ /* connection is not succesful */
+ last->ctrl |= MCAP_CTRL_FREE;
+ } else {
+ mcap_mcl_release(last);
+ last->mi->mcl_uncached_cb(last, last->mi->user_data);
+ }
+ mcap_mcl_unref(last);
+ }
+
+ mcl->mi->cached = g_slist_prepend(mcl->mi->cached, mcl);
+ mcl->ctrl |= MCAP_CTRL_CACHED;
+ mcap_mcl_shutdown(mcl);
+}
+
+static void mcap_uncache_mcl(struct mcap_mcl *mcl)
+{
+ if (!(mcl->ctrl & MCAP_CTRL_CACHED))
+ return;
+
+ DBG("Got MCL from cache");
+
+ mcl->mi->cached = g_slist_remove(mcl->mi->cached, mcl);
+ mcl->mi->mcls = g_slist_prepend(mcl->mi->mcls, mcl);
+ mcl->ctrl &= ~MCAP_CTRL_CACHED;
+ mcl->ctrl &= ~MCAP_CTRL_FREE;
+}
+
+void mcap_close_mcl(struct mcap_mcl *mcl, gboolean cache)
+{
+ if (!mcl)
+ return;
+
+ if (mcl->ctrl & MCAP_CTRL_FREE) {
+ mcap_mcl_release(mcl);
+ return;
+ }
+
+ if (!cache)
+ mcl->ctrl |= MCAP_CTRL_NOCACHE;
+
+ if (mcl->cc) {
+ g_io_channel_shutdown(mcl->cc, TRUE, NULL);
+ g_io_channel_unref(mcl->cc);
+ mcl->cc = NULL;
+ mcl->state = MCL_IDLE;
+ } else if ((mcl->ctrl & MCAP_CTRL_CACHED) &&
+ (mcl->ctrl & MCAP_CTRL_NOCACHE)) {
+ mcl->ctrl &= ~MCAP_CTRL_CACHED;
+ mcl->mi->cached = g_slist_remove(mcl->mi->cached, mcl);
+ mcap_mcl_release(mcl);
+ mcap_mcl_unref(mcl);
+ }
+}
+
+struct mcap_mcl *mcap_mcl_ref(struct mcap_mcl *mcl)
+{
+ mcl->ref++;
+
+ DBG("mcap_mcl_ref(%p): ref=%d", mcl, mcl->ref);
+
+ return mcl;
+}
+
+void mcap_mcl_unref(struct mcap_mcl *mcl)
+{
+ mcl->ref--;
+
+ DBG("mcap_mcl_unref(%p): ref=%d", mcl, mcl->ref);
+
+ if (mcl->ref > 0)
+ return;
+
+ mcap_mcl_release(mcl);
+ mcap_instance_unref(mcl->mi);
+ g_free(mcl->cb);
+ g_free(mcl);
+}
+
+static gboolean parse_set_opts(struct mcap_mdl_cb *mdl_cb, GError **err,
+ McapMclCb cb1, va_list args)
+{
+ McapMclCb cb = cb1;
+ struct mcap_mdl_cb *c;
+
+ c = g_new0(struct mcap_mdl_cb, 1);
+
+ while (cb != MCAP_MDL_CB_INVALID) {
+ switch (cb) {
+ case MCAP_MDL_CB_CONNECTED:
+ c->mdl_connected = va_arg(args, mcap_mdl_event_cb);
+ break;
+ case MCAP_MDL_CB_CLOSED:
+ c->mdl_closed = va_arg(args, mcap_mdl_event_cb);
+ break;
+ case MCAP_MDL_CB_DELETED:
+ c->mdl_deleted = va_arg(args, mcap_mdl_event_cb);
+ break;
+ case MCAP_MDL_CB_ABORTED:
+ c->mdl_aborted = va_arg(args, mcap_mdl_event_cb);
+ break;
+ case MCAP_MDL_CB_REMOTE_CONN_REQ:
+ c->mdl_conn_req = va_arg(args,
+ mcap_remote_mdl_conn_req_cb);
+ break;
+ case MCAP_MDL_CB_REMOTE_RECONN_REQ:
+ c->mdl_reconn_req = va_arg(args,
+ mcap_remote_mdl_reconn_req_cb);
+ break;
+ default:
+ g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS,
+ "Unknown option %d", cb);
+ return FALSE;
+ }
+ cb = va_arg(args, int);
+ }
+
+ /* Set new callbacks */
+ if (c->mdl_connected)
+ mdl_cb->mdl_connected = c->mdl_connected;
+ if (c->mdl_closed)
+ mdl_cb->mdl_closed = c->mdl_closed;
+ if (c->mdl_deleted)
+ mdl_cb->mdl_deleted = c->mdl_deleted;
+ if (c->mdl_aborted)
+ mdl_cb->mdl_aborted = c->mdl_aborted;
+ if (c->mdl_conn_req)
+ mdl_cb->mdl_conn_req = c->mdl_conn_req;
+ if (c->mdl_reconn_req)
+ mdl_cb->mdl_reconn_req = c->mdl_reconn_req;
+
+ g_free(c);
+
+ return TRUE;
+}
+
+gboolean mcap_mcl_set_cb(struct mcap_mcl *mcl, gpointer user_data,
+ GError **gerr, McapMclCb cb1, ...)
+{
+ va_list args;
+ gboolean ret;
+
+ va_start(args, cb1);
+ ret = parse_set_opts(mcl->cb, gerr, cb1, args);
+ va_end(args);
+
+ if (!ret)
+ return FALSE;
+
+ mcl->cb->user_data = user_data;
+ return TRUE;
+}
+
+void mcap_mcl_get_addr(struct mcap_mcl *mcl, bdaddr_t *addr)
+{
+ bacpy(addr, &mcl->addr);
+}
+
+static void mcap_del_mdl(gpointer elem, gpointer user_data)
+{
+ struct mcap_mdl *mdl = elem;
+ gboolean notify = *(gboolean *) user_data;
+
+ shutdown_mdl(mdl);
+ if (notify)
+ mdl->mcl->cb->mdl_deleted(mdl, mdl->mcl->cb->user_data);
+
+ mcap_mdl_unref(mdl);
+}
+
+static gboolean check_cmd_req_length(struct mcap_mcl *mcl, void *cmd,
+ uint32_t rlen, uint32_t explen, uint8_t rspcod)
+{
+ mcap_md_req *req;
+ uint16_t mdl_id;
+
+ if (rlen != explen) {
+ if (rlen >= sizeof(mcap_md_req)) {
+ req = cmd;
+ mdl_id = ntohs(req->mdl);
+ } else {
+ /* We can't get mdlid */
+ mdl_id = MCAP_MDLID_RESERVED;
+ }
+ mcap_send_cmd(mcl, rspcod, MCAP_INVALID_PARAM_VALUE, mdl_id,
+ NULL, 0);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void process_md_create_mdl_req(struct mcap_mcl *mcl, void *cmd,
+ uint32_t len)
+{
+ mcap_md_create_mdl_req *req;
+ struct mcap_mdl *mdl;
+ uint16_t mdl_id;
+ uint8_t mdep_id;
+ uint8_t cfga, conf;
+ uint8_t rsp;
+
+ if (!check_cmd_req_length(mcl, cmd, len, sizeof(mcap_md_create_mdl_req),
+ MCAP_MD_CREATE_MDL_RSP))
+ return;
+
+ req = cmd;
+ mdl_id = ntohs(req->mdl);
+ if (mdl_id < MCAP_MDLID_INITIAL || mdl_id > MCAP_MDLID_FINAL) {
+ mcap_send_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_INVALID_MDL,
+ mdl_id, NULL, 0);
+ return;
+ }
+
+ mdep_id = req->mdep;
+ if (mdep_id > MCAP_MDEPID_FINAL) {
+ mcap_send_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_INVALID_MDEP,
+ mdl_id, NULL, 0);
+ return;
+ }
+
+ mdl = get_mdl(mcl, mdl_id);
+ if (mdl && (mdl->state == MDL_WAITING || mdl->state == MDL_DELETING )) {
+ /* Creation request arrives for a MDL that is being managed
+ * at current moment */
+ mcap_send_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_MDL_BUSY,
+ mdl_id, NULL, 0);
+ return;
+ }
+
+ cfga = conf = req->conf;
+ /* Callback to upper layer */
+ rsp = mcl->cb->mdl_conn_req(mcl, mdep_id, mdl_id, &conf,
+ mcl->cb->user_data);
+ if (mcl->state == MCL_IDLE) {
+ /* MCL has been closed int the callback */
+ return;
+ }
+
+ if (cfga != 0 && cfga != conf) {
+ /* Remote device set default configuration but upper profile */
+ /* has changed it. Protocol Error: force closing the MCL by */
+ /* remote device using UNSPECIFIED_ERROR response */
+ mcap_send_cmd(mcl, MCAP_MD_CREATE_MDL_RSP,
+ MCAP_UNSPECIFIED_ERROR, mdl_id, NULL, 0);
+ return;
+ }
+ if (rsp != MCAP_SUCCESS) {
+ mcap_send_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, rsp, mdl_id,
+ NULL, 0);
+ return;
+ }
+
+ if (!mdl) {
+ mdl = g_new0(struct mcap_mdl, 1);
+ mdl->mcl = mcap_mcl_ref(mcl);
+ mdl->mdlid = mdl_id;
+ mcl->mdls = g_slist_insert_sorted(mcl->mdls, mcap_mdl_ref(mdl),
+ compare_mdl);
+ } else if (mdl->state == MDL_CONNECTED) {
+ /* MCAP specification says that we should close the MCL if
+ * it is open when we receive a MD_CREATE_MDL_REQ */
+ shutdown_mdl(mdl);
+ }
+
+ mdl->mdep_id = mdep_id;
+ mdl->state = MDL_WAITING;
+
+ mcl->state = MCL_PENDING;
+ mcap_send_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_SUCCESS, mdl_id,
+ &conf, 1);
+}
+
+static void process_md_reconnect_mdl_req(struct mcap_mcl *mcl, void *cmd,
+ uint32_t len)
+{
+ mcap_md_req *req;
+ struct mcap_mdl *mdl;
+ uint16_t mdl_id;
+ uint8_t rsp;
+
+ if (!check_cmd_req_length(mcl, cmd, len, sizeof(mcap_md_req),
+ MCAP_MD_RECONNECT_MDL_RSP))
+ return;
+
+ req = cmd;
+ mdl_id = ntohs(req->mdl);
+
+ mdl = get_mdl(mcl, mdl_id);
+ if (!mdl) {
+ mcap_send_cmd(mcl, MCAP_MD_RECONNECT_MDL_RSP, MCAP_INVALID_MDL,
+ mdl_id, NULL, 0);
+ return;
+ } else if (mdl->state == MDL_WAITING || mdl->state == MDL_DELETING ) {
+ /* Creation request arrives for a MDL that is being managed
+ * at current moment */
+ mcap_send_cmd(mcl, MCAP_MD_RECONNECT_MDL_RSP, MCAP_MDL_BUSY,
+ mdl_id, NULL, 0);
+ return;
+ }
+
+ /* Callback to upper layer */
+ rsp = mcl->cb->mdl_reconn_req(mdl, mcl->cb->user_data);
+ if (mcl->state == MCL_IDLE)
+ return;
+
+ if (rsp != MCAP_SUCCESS) {
+ mcap_send_cmd(mcl, MCAP_MD_RECONNECT_MDL_RSP, rsp, mdl_id,
+ NULL, 0);
+ return;
+ }
+
+ if (mdl->state == MDL_CONNECTED)
+ shutdown_mdl(mdl);
+
+ mdl->state = MDL_WAITING;
+ mcl->state = MCL_PENDING;
+ mcap_send_cmd(mcl, MCAP_MD_RECONNECT_MDL_RSP, MCAP_SUCCESS, mdl_id,
+ NULL, 0);
+}
+
+static void process_md_abort_mdl_req(struct mcap_mcl *mcl, void *cmd,
+ uint32_t len)
+{
+ mcap_md_req *req;
+ GSList *l;
+ struct mcap_mdl *mdl, *abrt;
+ uint16_t mdl_id;
+
+ if (!check_cmd_req_length(mcl, cmd, len, sizeof(mcap_md_req),
+ MCAP_MD_ABORT_MDL_RSP))
+ return;
+
+ req = cmd;
+ mdl_id = ntohs(req->mdl);
+ mcl->state = MCL_CONNECTED;
+ abrt = NULL;
+ for (l = mcl->mdls; l; l = l->next) {
+ mdl = l->data;
+ if (mdl_id == mdl->mdlid && mdl->state == MDL_WAITING) {
+ abrt = mdl;
+ if (mcl->state != MCL_CONNECTED)
+ break;
+ continue;
+ }
+ if (mdl->state == MDL_CONNECTED && mcl->state != MCL_ACTIVE)
+ mcl->state = MCL_ACTIVE;
+
+ if (abrt && mcl->state == MCL_ACTIVE)
+ break;
+ }
+
+ if (!abrt) {
+ mcap_send_cmd(mcl, MCAP_MD_ABORT_MDL_RSP, MCAP_INVALID_MDL,
+ mdl_id, NULL, 0);
+ return;
+ }
+
+ mcl->cb->mdl_aborted(abrt, mcl->cb->user_data);
+ abrt->state = MDL_CLOSED;
+ mcap_send_cmd(mcl, MCAP_MD_ABORT_MDL_RSP, MCAP_SUCCESS, mdl_id,
+ NULL, 0);
+}
+
+static void process_md_delete_mdl_req(struct mcap_mcl *mcl, void *cmd,
+ uint32_t len)
+{
+ mcap_md_req *req;
+ struct mcap_mdl *mdl, *aux;
+ uint16_t mdlid;
+ gboolean notify;
+ GSList *l;
+
+ if (!check_cmd_req_length(mcl, cmd, len, sizeof(mcap_md_req),
+ MCAP_MD_DELETE_MDL_RSP))
+ return;
+
+ req = cmd;
+ mdlid = ntohs(req->mdl);
+ if (mdlid == MCAP_ALL_MDLIDS) {
+ notify = FALSE;
+ g_slist_foreach(mcl->mdls, mcap_del_mdl, &notify);
+ g_slist_free(mcl->mdls);
+ mcl->mdls = NULL;
+ mcl->state = MCL_CONNECTED;
+ /* NULL mdl means ALL_MDLS */
+ mcl->cb->mdl_deleted(NULL, mcl->cb->user_data);
+ goto resp;
+ }
+
+ if (mdlid < MCAP_MDLID_INITIAL || mdlid > MCAP_MDLID_FINAL) {
+ mcap_send_cmd(mcl, MCAP_MD_DELETE_MDL_RSP, MCAP_INVALID_MDL,
+ mdlid, NULL, 0);
+ return;
+ }
+
+ for (l = mcl->mdls, mdl = NULL; l; l = l->next) {
+ aux = l->data;
+ if (aux->mdlid == mdlid) {
+ mdl = aux;
+ break;
+ }
+ }
+
+ if (!mdl || mdl->state == MDL_WAITING) {
+ mcap_send_cmd(mcl, MCAP_MD_DELETE_MDL_RSP, MCAP_INVALID_MDL,
+ mdlid, NULL, 0);
+ return;
+ }
+
+ mcl->mdls = g_slist_remove(mcl->mdls, mdl);
+ update_mcl_state(mcl);
+ notify = TRUE;
+ mcap_del_mdl(mdl, &notify);
+
+resp:
+ mcap_send_cmd(mcl, MCAP_MD_DELETE_MDL_RSP, MCAP_SUCCESS, mdlid,
+ NULL, 0);
+}
+
+static void invalid_req_state(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+ uint16_t mdlr;
+
+ error("Invalid cmd received (op code = %d) in state %d", cmd[0],
+ mcl->state);
+ /* Get previously mdlid sent to generate an appropriate
+ * response if it is possible */
+ mdlr = len < sizeof(mcap_md_req) ? MCAP_MDLID_RESERVED :
+ ntohs(((mcap_md_req *) cmd)->mdl);
+ mcap_send_cmd(mcl, cmd[0]+1, MCAP_INVALID_OPERATION, mdlr, NULL, 0);
+}
+
+/* Function used to process commands depending of MCL state */
+static void proc_req_connected(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+ switch (cmd[0]) {
+ case MCAP_MD_CREATE_MDL_REQ:
+ process_md_create_mdl_req(mcl, cmd, len);
+ break;
+ case MCAP_MD_RECONNECT_MDL_REQ:
+ process_md_reconnect_mdl_req(mcl, cmd, len);
+ break;
+ case MCAP_MD_DELETE_MDL_REQ:
+ process_md_delete_mdl_req(mcl, cmd, len);
+ break;
+ default:
+ invalid_req_state(mcl, cmd, len);
+ }
+}
+
+static void proc_req_pending(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+ if (cmd[0] == MCAP_MD_ABORT_MDL_REQ)
+ process_md_abort_mdl_req(mcl, cmd, len);
+ else
+ invalid_req_state(mcl, cmd, len);
+}
+
+static void proc_req_active(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+ switch (cmd[0]) {
+ case MCAP_MD_CREATE_MDL_REQ:
+ process_md_create_mdl_req(mcl, cmd, len);
+ break;
+ case MCAP_MD_RECONNECT_MDL_REQ:
+ process_md_reconnect_mdl_req(mcl, cmd, len);
+ break;
+ case MCAP_MD_DELETE_MDL_REQ:
+ process_md_delete_mdl_req(mcl, cmd, len);
+ break;
+ default:
+ invalid_req_state(mcl, cmd, len);
+ }
+}
+
+/* Function used to process replies */
+static gboolean check_err_rsp(struct mcap_mcl *mcl, mcap_rsp *rsp,
+ uint32_t rlen, uint32_t len, GError **gerr)
+{
+ mcap_md_req *cmdlast = (mcap_md_req *) mcl->lcmd;
+ gint err = MCAP_ERROR_FAILED;
+ gboolean close = FALSE;
+ char *msg;
+
+ if (rsp->op == MCAP_ERROR_RSP) {
+ msg = "MCAP_ERROR_RSP received";
+ close = FALSE;
+ goto fail;
+ }
+
+ /* Check if the response matches with the last request */
+ if (rlen < sizeof(mcap_rsp) || (mcl->lcmd[0] + 1) != rsp->op) {
+ msg = "Protocol error";
+ close = FALSE;
+ goto fail;
+ }
+
+ if (rlen < len) {
+ msg = "Protocol error";
+ close = FALSE;
+ goto fail;
+ }
+
+ if (rsp->mdl != cmdlast->mdl) {
+ msg = "MDLID received doesn't match with MDLID sent";
+ close = TRUE;
+ goto fail;
+ }
+
+ if (rsp->rc == MCAP_REQUEST_NOT_SUPPORTED) {
+ msg = "Remote does not support opcodes";
+ mcl->ctrl &= ~MCAP_CTRL_STD_OP;
+ goto fail;
+ }
+
+ if (rsp->rc == MCAP_UNSPECIFIED_ERROR) {
+ msg = "Unspecified error";
+ close = TRUE;
+ goto fail;
+ }
+
+ if (rsp->rc != MCAP_SUCCESS) {
+ msg = error2str(rsp->rc);
+ err = rsp->rc;
+ goto fail;
+ }
+
+ return FALSE;
+
+fail:
+ g_set_error(gerr, MCAP_ERROR, err, "%s", msg);
+ return close;
+}
+
+static gboolean process_md_create_mdl_rsp(struct mcap_mcl *mcl,
+ mcap_rsp *rsp, uint32_t len)
+{
+ mcap_md_create_mdl_req *cmdlast = (mcap_md_create_mdl_req *) mcl->lcmd;
+ struct mcap_mdl_op_cb *conn = mcl->priv_data;
+ mcap_mdl_operation_conf_cb connect_cb = conn->cb.op_conf;
+ gpointer user_data = conn->user_data;
+ struct mcap_mdl *mdl = conn->mdl;
+ uint8_t conf = cmdlast->conf;
+ gboolean close;
+ GError *gerr = NULL;
+
+ close = check_err_rsp(mcl, rsp, len, sizeof(mcap_rsp) + 1, &gerr);
+ g_free(mcl->lcmd);
+ mcl->lcmd = NULL;
+ mcl->req = MCL_AVAILABLE;
+
+ if (gerr)
+ goto fail;
+
+ /* Check if preferences changed */
+ if (conf != 0x00 && rsp->data[0] != conf) {
+ g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_FAILED,
+ "Configuration changed");
+ close = TRUE;
+ goto fail;
+ }
+
+ connect_cb(mdl, rsp->data[0], gerr, user_data);
+ return close;
+
+fail:
+ connect_cb(NULL, 0, gerr, user_data);
+ mcl->mdls = g_slist_remove(mcl->mdls, mdl);
+ mcap_mdl_unref(mdl);
+ g_error_free(gerr);
+ update_mcl_state(mcl);
+ return close;
+}
+
+static gboolean process_md_reconnect_mdl_rsp(struct mcap_mcl *mcl,
+ mcap_rsp *rsp, uint32_t len)
+{
+ struct mcap_mdl_op_cb *reconn = mcl->priv_data;
+ mcap_mdl_operation_cb reconn_cb = reconn->cb.op;
+ gpointer user_data = reconn->user_data;
+ struct mcap_mdl *mdl = reconn->mdl;
+ GError *gerr = NULL;
+ gboolean close;
+
+ close = check_err_rsp(mcl, rsp, len, sizeof(mcap_rsp), &gerr);
+
+ g_free(mcl->lcmd);
+ mcl->lcmd = NULL;
+ mcl->req = MCL_AVAILABLE;
+
+ reconn_cb(mdl, gerr, user_data);
+ if (!gerr)
+ return close;
+
+ g_error_free(gerr);
+ shutdown_mdl(mdl);
+ update_mcl_state(mcl);
+
+ if (rsp->rc != MCAP_INVALID_MDL)
+ return close;
+
+ /* Remove cached mdlid */
+ mcl->mdls = g_slist_remove(mcl->mdls, mdl);
+ mcl->cb->mdl_deleted(mdl, mcl->cb->user_data);
+ mcap_mdl_unref(mdl);
+
+ return close;
+}
+
+static gboolean process_md_abort_mdl_rsp(struct mcap_mcl *mcl,
+ mcap_rsp *rsp, uint32_t len)
+{
+ struct mcap_mdl_op_cb *abrt = mcl->priv_data;
+ mcap_mdl_notify_cb abrt_cb = abrt->cb.notify;
+ gpointer user_data = abrt->user_data;
+ struct mcap_mdl *mdl = abrt->mdl;
+ GError *gerr = NULL;
+ gboolean close;
+
+ close = check_err_rsp(mcl, rsp, len, sizeof(mcap_rsp), &gerr);
+
+ g_free(mcl->lcmd);
+ mcl->lcmd = NULL;
+ mcl->req = MCL_AVAILABLE;
+
+ abrt_cb(gerr, user_data);
+ shutdown_mdl(mdl);
+
+ if (len >= sizeof(mcap_rsp) && rsp->rc == MCAP_INVALID_MDL) {
+ mcl->mdls = g_slist_remove(mcl->mdls, mdl);
+ mcl->cb->mdl_deleted(mdl, mcl->cb->user_data);
+ mcap_mdl_unref(mdl);
+ }
+
+ if (gerr)
+ g_error_free(gerr);
+
+ update_mcl_state(mcl);
+
+ return close;
+}
+
+static void restore_mdl(gpointer elem, gpointer data)
+{
+ struct mcap_mdl *mdl = elem;
+
+ if (mdl->state == MDL_DELETING) {
+ if (mdl->dc)
+ mdl->state = MDL_CONNECTED;
+ else
+ mdl->state = MDL_CLOSED;
+ } else if (mdl->state == MDL_CLOSED)
+ mdl->mcl->cb->mdl_closed(mdl, mdl->mcl->cb->user_data);
+}
+
+static void check_mdl_del_err(struct mcap_mdl *mdl, mcap_rsp *rsp)
+{
+ if (rsp->rc != MCAP_ERROR_INVALID_MDL) {
+ restore_mdl(mdl, NULL);
+ return;
+ }
+
+ /* MDL does not exist in remote side, we can delete it */
+ mdl->mcl->mdls = g_slist_remove(mdl->mcl->mdls, mdl);
+ mcap_mdl_unref(mdl);
+}
+
+static gboolean process_md_delete_mdl_rsp(struct mcap_mcl *mcl, mcap_rsp *rsp,
+ uint32_t len)
+{
+ struct mcap_mdl_op_cb *del = mcl->priv_data;
+ struct mcap_mdl *mdl = del->mdl;
+ mcap_mdl_notify_cb deleted_cb = del->cb.notify;
+ gpointer user_data = del->user_data;
+ mcap_md_req *cmdlast = (mcap_md_req *) mcl->lcmd;
+ uint16_t mdlid = ntohs(cmdlast->mdl);
+ GError *gerr = NULL;
+ gboolean close;
+ gboolean notify = FALSE;
+
+ close = check_err_rsp(mcl, rsp, len, sizeof(mcap_rsp), &gerr);
+
+ g_free(mcl->lcmd);
+ mcl->lcmd = NULL;
+ mcl->req = MCL_AVAILABLE;
+
+ if (gerr) {
+ if (mdl)
+ check_mdl_del_err(mdl, rsp);
+ else
+ g_slist_foreach(mcl->mdls, restore_mdl, NULL);
+ deleted_cb(gerr, user_data);
+ g_error_free(gerr);
+ return close;
+ }
+
+ if (mdlid == MCAP_ALL_MDLIDS) {
+ g_slist_foreach(mcl->mdls, mcap_del_mdl, &notify);
+ g_slist_free(mcl->mdls);
+ mcl->mdls = NULL;
+ mcl->state = MCL_CONNECTED;
+ } else {
+ mcl->mdls = g_slist_remove(mcl->mdls, mdl);
+ update_mcl_state(mcl);
+ mcap_del_mdl(mdl, &notify);
+ }
+
+ deleted_cb(gerr, user_data);
+
+ return close;
+}
+
+static void post_process_rsp(struct mcap_mcl *mcl, struct mcap_mdl_op_cb *op)
+{
+ if (mcl->priv_data != op) {
+ /* Queued MCAP request in some callback. */
+ /* We should not delete the mcl private data */
+ free_mcap_mdl_op(op);
+ } else {
+ /* This is not a queued request. It's safe */
+ /* delete the mcl private data here. */
+ free_mcl_priv_data(mcl);
+ }
+}
+
+static void proc_response(struct mcap_mcl *mcl, void *buf, uint32_t len)
+{
+ struct mcap_mdl_op_cb *op = mcl->priv_data;
+ mcap_rsp *rsp = buf;
+ gboolean close;
+
+ RELEASE_TIMER(mcl);
+
+ switch (mcl->lcmd[0] + 1) {
+ case MCAP_MD_CREATE_MDL_RSP:
+ close = process_md_create_mdl_rsp(mcl, rsp, len);
+ post_process_rsp(mcl, op);
+ break;
+ case MCAP_MD_RECONNECT_MDL_RSP:
+ close = process_md_reconnect_mdl_rsp(mcl, rsp, len);
+ post_process_rsp(mcl, op);
+ break;
+ case MCAP_MD_ABORT_MDL_RSP:
+ close = process_md_abort_mdl_rsp(mcl, rsp, len);
+ post_process_rsp(mcl, op);
+ break;
+ case MCAP_MD_DELETE_MDL_RSP:
+ close = process_md_delete_mdl_rsp(mcl, rsp, len);
+ post_process_rsp(mcl, op);
+ break;
+ default:
+ DBG("Unknown cmd response received (op code = %d)", rsp->op);
+ close = TRUE;
+ break;
+ }
+
+ if (close) {
+ mcl->mi->mcl_disconnected_cb(mcl, mcl->mi->user_data);
+ mcap_cache_mcl(mcl);
+ }
+}
+
+static void proc_cmd(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+ GError *gerr = NULL;
+
+ if (cmd[0] > MCAP_MD_SYNC_INFO_IND ||
+ (cmd[0] > MCAP_MD_DELETE_MDL_RSP &&
+ cmd[0] < MCAP_MD_SYNC_CAP_REQ)) {
+ error("Unknown cmd received (op code = %d)", cmd[0]);
+ mcap_send_cmd(mcl, MCAP_ERROR_RSP, MCAP_INVALID_OP_CODE,
+ MCAP_MDLID_RESERVED, NULL, 0);
+ return;
+ }
+
+ if (cmd[0] >= MCAP_MD_SYNC_CAP_REQ &&
+ cmd[0] <= MCAP_MD_SYNC_INFO_IND) {
+ proc_sync_cmd(mcl, cmd, len);
+ return;
+ }
+
+ if (!(mcl->ctrl & MCAP_CTRL_STD_OP)) {
+ /* In case the remote device doesn't work correctly */
+ error("Remote device does not support opcodes, cmd ignored");
+ return;
+ }
+
+ if (mcl->req == MCL_WAITING_RSP) {
+ if (cmd[0] & 0x01) {
+ /* Request arrived when a response is expected */
+ if (mcl->role == MCL_INITIATOR)
+ /* ignore */
+ return;
+ /* Initiator will ignore our last request */
+ RELEASE_TIMER(mcl);
+ mcl->req = MCL_AVAILABLE;
+ g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_REQ_IGNORED,
+ "Initiator sent a request with more priority");
+ mcap_notify_error(mcl, gerr);
+ proc_req[mcl->state](mcl, cmd, len);
+ return;
+ }
+ proc_response(mcl, cmd, len);
+ } else if (cmd[0] & 0x01)
+ proc_req[mcl->state](mcl, cmd, len);
+}
+
+static gboolean mdl_event_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+
+ struct mcap_mdl *mdl = data;
+ gboolean notify;
+
+ DBG("Close MDL %d", mdl->mdlid);
+
+ notify = (mdl->state == MDL_CONNECTED);
+ shutdown_mdl(mdl);
+
+ update_mcl_state(mdl->mcl);
+
+ if (notify) {
+ /*Callback to upper layer */
+ mdl->mcl->cb->mdl_closed(mdl, mdl->mcl->cb->user_data);
+ }
+
+ return FALSE;
+}
+
+static void mcap_connect_mdl_cb(GIOChannel *chan, GError *conn_err,
+ gpointer data)
+{
+ struct mcap_mdl_op_cb *con = data;
+ struct mcap_mdl *mdl = con->mdl;
+ mcap_mdl_operation_cb cb = con->cb.op;
+ gpointer user_data = con->user_data;
+
+ DBG("mdl connect callback");
+
+ if (conn_err) {
+ DBG("ERROR: mdl connect callback");
+ mdl->state = MDL_CLOSED;
+ g_io_channel_unref(mdl->dc);
+ mdl->dc = NULL;
+ cb(mdl, conn_err, user_data);
+ return;
+ }
+
+ mdl->state = MDL_CONNECTED;
+ mdl->wid = g_io_add_watch_full(mdl->dc, G_PRIORITY_DEFAULT,
+ G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ (GIOFunc) mdl_event_cb,
+ mcap_mdl_ref(mdl),
+ (GDestroyNotify) mcap_mdl_unref);
+
+ cb(mdl, conn_err, user_data);
+}
+
+gboolean mcap_connect_mdl(struct mcap_mdl *mdl, uint8_t mode,
+ uint16_t dcpsm,
+ mcap_mdl_operation_cb connect_cb,
+ gpointer user_data,
+ GDestroyNotify destroy,
+ GError **err)
+{
+ struct mcap_mdl_op_cb *con;
+
+ if (mdl->state != MDL_WAITING) {
+ g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_MDL,
+ "%s", error2str(MCAP_INVALID_MDL));
+ return FALSE;
+ }
+
+ if ((mode != L2CAP_MODE_ERTM) && (mode != L2CAP_MODE_STREAMING)) {
+ g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS,
+ "Invalid MDL configuration");
+ return FALSE;
+ }
+
+ con = g_new0(struct mcap_mdl_op_cb, 1);
+ con->mdl = mcap_mdl_ref(mdl);
+ con->cb.op = connect_cb;
+ con->destroy = destroy;
+ con->user_data = user_data;
+
+ mdl->dc = bt_io_connect(BT_IO_L2CAP, mcap_connect_mdl_cb, con,
+ (GDestroyNotify) free_mcap_mdl_op, err,
+ BT_IO_OPT_SOURCE_BDADDR, &mdl->mcl->mi->src,
+ BT_IO_OPT_DEST_BDADDR, &mdl->mcl->addr,
+ BT_IO_OPT_PSM, dcpsm,
+ BT_IO_OPT_MTU, MCAP_DC_MTU,
+ BT_IO_OPT_SEC_LEVEL, mdl->mcl->mi->sec,
+ BT_IO_OPT_MODE, mode,
+ BT_IO_OPT_INVALID);
+ if (!mdl->dc) {
+ DBG("MDL Connection error");
+ mdl->state = MDL_CLOSED;
+ mcap_mdl_unref(con->mdl);
+ g_free(con);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean mcl_control_cb(GIOChannel *chan, GIOCondition cond,
+ gpointer data)
+{
+ GError *gerr = NULL;
+ struct mcap_mcl *mcl = data;
+ int sk, len;
+ uint8_t buf[MCAP_CC_MTU];
+
+ if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
+ goto fail;
+
+ sk = g_io_channel_unix_get_fd(chan);
+ len = read(sk, buf, sizeof(buf));
+ if (len < 0)
+ goto fail;
+
+ proc_cmd(mcl, buf, (uint32_t) len);
+ return TRUE;
+
+fail:
+ if (mcl->state != MCL_IDLE) {
+ if (mcl->req == MCL_WAITING_RSP) {
+ /* notify error in pending callback */
+ g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_MCL_CLOSED,
+ "MCL closed");
+ mcap_notify_error(mcl, gerr);
+ g_error_free(gerr);
+ }
+ mcl->mi->mcl_disconnected_cb(mcl, mcl->mi->user_data);
+ }
+ mcap_cache_mcl(mcl);
+ return FALSE;
+}
+
+static void mcap_connect_mcl_cb(GIOChannel *chan, GError *conn_err,
+ gpointer user_data)
+{
+ char dstaddr[18];
+ struct connect_mcl *con = user_data;
+ struct mcap_mcl *aux, *mcl = con->mcl;
+ mcap_mcl_connect_cb connect_cb = con->connect_cb;
+ gpointer data = con->user_data;
+ GError *gerr = NULL;
+
+ mcl->ctrl &= ~MCAP_CTRL_CONN;
+
+ if (conn_err) {
+ if (mcl->ctrl & MCAP_CTRL_FREE) {
+ mcap_mcl_release(mcl);
+ mcl->mi->mcl_uncached_cb(mcl, mcl->mi->user_data);
+ }
+ connect_cb(NULL, conn_err, data);
+ return;
+ }
+
+ ba2str(&mcl->addr, dstaddr);
+
+ aux = find_mcl(mcl->mi->mcls, &mcl->addr);
+ if (aux) {
+ /* Double MCL connection case */
+ error("MCL error: Device %s is already connected", dstaddr);
+ g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_ALREADY_EXISTS,
+ "MCL %s is already connected", dstaddr);
+ connect_cb(NULL, gerr, data);
+ g_error_free(gerr);
+ return;
+ }
+
+ mcl->state = MCL_CONNECTED;
+ mcl->role = MCL_INITIATOR;
+ mcl->req = MCL_AVAILABLE;
+ mcl->ctrl |= MCAP_CTRL_STD_OP;
+
+ mcap_sync_init(mcl);
+
+ if (mcl->ctrl & MCAP_CTRL_CACHED)
+ mcap_uncache_mcl(mcl);
+ else {
+ mcl->ctrl &= ~MCAP_CTRL_FREE;
+ mcl->mi->mcls = g_slist_prepend(mcl->mi->mcls,
+ mcap_mcl_ref(mcl));
+ }
+
+ mcl->wid = g_io_add_watch_full(mcl->cc, G_PRIORITY_DEFAULT,
+ G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ (GIOFunc) mcl_control_cb,
+ mcap_mcl_ref(mcl),
+ (GDestroyNotify) mcap_mcl_unref);
+ connect_cb(mcl, gerr, data);
+}
+
+static void set_mdl_properties(GIOChannel *chan, struct mcap_mdl *mdl)
+{
+ struct mcap_mcl *mcl = mdl->mcl;
+
+ mdl->state = MDL_CONNECTED;
+ mdl->dc = g_io_channel_ref(chan);
+ mdl->wid = g_io_add_watch_full(mdl->dc, G_PRIORITY_DEFAULT,
+ G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ (GIOFunc) mdl_event_cb,
+ mcap_mdl_ref(mdl),
+ (GDestroyNotify) mcap_mdl_unref);
+
+ mcl->state = MCL_ACTIVE;
+ mcl->cb->mdl_connected(mdl, mcl->cb->user_data);
+}
+
+static void mcl_io_destroy(gpointer data)
+{
+ struct connect_mcl *con = data;
+
+ mcap_mcl_unref(con->mcl);
+ if (con->destroy)
+ con->destroy(con->user_data);
+ g_free(con);
+}
+
+gboolean mcap_create_mcl(struct mcap_instance *mi,
+ const bdaddr_t *addr,
+ uint16_t ccpsm,
+ mcap_mcl_connect_cb connect_cb,
+ gpointer user_data,
+ GDestroyNotify destroy,
+ GError **err)
+{
+ struct mcap_mcl *mcl;
+ struct connect_mcl *con;
+
+ mcl = find_mcl(mi->mcls, addr);
+ if (mcl) {
+ g_set_error(err, MCAP_ERROR, MCAP_ERROR_ALREADY_EXISTS,
+ "MCL is already connected.");
+ return FALSE;
+ }
+
+ mcl = find_mcl(mi->cached, addr);
+ if (!mcl) {
+ mcl = g_new0(struct mcap_mcl, 1);
+ mcl->mi = mcap_instance_ref(mi);
+ mcl->state = MCL_IDLE;
+ bacpy(&mcl->addr, addr);
+ set_default_cb(mcl);
+ mcl->next_mdl = (rand() % MCAP_MDLID_FINAL) + 1;
+ }
+
+ mcl->ctrl |= MCAP_CTRL_CONN;
+
+ con = g_new0(struct connect_mcl, 1);
+ con->mcl = mcap_mcl_ref(mcl);
+ con->connect_cb = connect_cb;
+ con->destroy = destroy;
+ con->user_data = user_data;
+
+ mcl->cc = bt_io_connect(BT_IO_L2CAP, mcap_connect_mcl_cb, con,
+ mcl_io_destroy, err,
+ BT_IO_OPT_SOURCE_BDADDR, &mi->src,
+ BT_IO_OPT_DEST_BDADDR, addr,
+ BT_IO_OPT_PSM, ccpsm,
+ BT_IO_OPT_MTU, MCAP_CC_MTU,
+ BT_IO_OPT_SEC_LEVEL, mi->sec,
+ BT_IO_OPT_MODE, L2CAP_MODE_ERTM,
+ BT_IO_OPT_INVALID);
+ if (!mcl->cc) {
+ mcl->ctrl &= ~MCAP_CTRL_CONN;
+ if (mcl->ctrl & MCAP_CTRL_FREE) {
+ mcap_mcl_release(mcl);
+ mcl->mi->mcl_uncached_cb(mcl, mcl->mi->user_data);
+ }
+ mcap_mcl_unref(con->mcl);
+ g_free(con);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void connect_dc_event_cb(GIOChannel *chan, GError *gerr,
+ gpointer user_data)
+{
+ struct mcap_instance *mi = user_data;
+ struct mcap_mcl *mcl;
+ struct mcap_mdl *mdl;
+ GError *err = NULL;
+ bdaddr_t dst;
+ GSList *l;
+
+ if (gerr)
+ return;
+
+ bt_io_get(chan, BT_IO_L2CAP, &err,
+ BT_IO_OPT_DEST_BDADDR, &dst,
+ BT_IO_OPT_INVALID);
+ if (err) {
+ error("%s", err->message);
+ g_error_free(err);
+ goto drop;
+ }
+
+ mcl = find_mcl(mi->mcls, &dst);
+ if (!mcl || mcl->state != MCL_PENDING)
+ goto drop;
+
+ for (l = mcl->mdls; l; l = l->next) {
+ mdl = l->data;
+ if (mdl->state == MDL_WAITING) {
+ set_mdl_properties(chan, mdl);
+ return;
+ }
+ }
+
+drop:
+ g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+static void set_mcl_conf(GIOChannel *chan, struct mcap_mcl *mcl)
+{
+ gboolean reconn;
+
+ mcl->state = MCL_CONNECTED;
+ mcl->role = MCL_ACCEPTOR;
+ mcl->req = MCL_AVAILABLE;
+ mcl->cc = g_io_channel_ref(chan);
+ mcl->ctrl |= MCAP_CTRL_STD_OP;
+
+ mcap_sync_init(mcl);
+
+ reconn = (mcl->ctrl & MCAP_CTRL_CACHED);
+ if (reconn)
+ mcap_uncache_mcl(mcl);
+ else
+ mcl->mi->mcls = g_slist_prepend(mcl->mi->mcls,
+ mcap_mcl_ref(mcl));
+
+ mcl->wid = g_io_add_watch_full(mcl->cc, G_PRIORITY_DEFAULT,
+ G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ (GIOFunc) mcl_control_cb,
+ mcap_mcl_ref(mcl),
+ (GDestroyNotify) mcap_mcl_unref);
+
+ /* Callback to report new MCL */
+ if (reconn)
+ mcl->mi->mcl_reconnected_cb(mcl, mcl->mi->user_data);
+ else
+ mcl->mi->mcl_connected_cb(mcl, mcl->mi->user_data);
+}
+
+static void connect_mcl_event_cb(GIOChannel *chan, GError *gerr,
+ gpointer user_data)
+{
+ struct mcap_instance *mi = user_data;
+ struct mcap_mcl *mcl;
+ bdaddr_t dst;
+ char address[18], srcstr[18];
+ GError *err = NULL;
+
+ if (gerr)
+ return;
+
+ bt_io_get(chan, BT_IO_L2CAP, &err,
+ BT_IO_OPT_DEST_BDADDR, &dst,
+ BT_IO_OPT_DEST, address,
+ BT_IO_OPT_INVALID);
+ if (err) {
+ error("%s", err->message);
+ g_error_free(err);
+ goto drop;
+ }
+
+ ba2str(&mi->src, srcstr);
+ mcl = find_mcl(mi->mcls, &dst);
+ if (mcl) {
+ error("Control channel already created with %s on adapter %s",
+ address, srcstr);
+ goto drop;
+ }
+
+ mcl = find_mcl(mi->cached, &dst);
+ if (!mcl) {
+ mcl = g_new0(struct mcap_mcl, 1);
+ mcl->mi = mcap_instance_ref(mi);
+ bacpy(&mcl->addr, &dst);
+ set_default_cb(mcl);
+ mcl->next_mdl = (rand() % MCAP_MDLID_FINAL) + 1;
+ }
+
+ set_mcl_conf(chan, mcl);
+
+ return;
+drop:
+ g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+struct mcap_instance *mcap_create_instance(bdaddr_t *src,
+ BtIOSecLevel sec,
+ uint16_t ccpsm,
+ uint16_t dcpsm,
+ mcap_mcl_event_cb mcl_connected,
+ mcap_mcl_event_cb mcl_reconnected,
+ mcap_mcl_event_cb mcl_disconnected,
+ mcap_mcl_event_cb mcl_uncached,
+ mcap_info_ind_event_cb mcl_sync_info_ind,
+ gpointer user_data,
+ GError **gerr)
+{
+ struct mcap_instance *mi;
+
+ if (sec < BT_IO_SEC_MEDIUM) {
+ g_set_error(gerr, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS,
+ "Security level can't be minor of %d",
+ BT_IO_SEC_MEDIUM);
+ return NULL;
+ }
+
+ if (!(mcl_connected && mcl_reconnected &&
+ mcl_disconnected && mcl_uncached)) {
+ g_set_error(gerr, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS,
+ "The callbacks can't be null");
+ return NULL;
+ }
+
+ mi = g_new0(struct mcap_instance, 1);
+
+ bacpy(&mi->src, src);
+
+ mi->sec = sec;
+ mi->mcl_connected_cb = mcl_connected;
+ mi->mcl_reconnected_cb = mcl_reconnected;
+ mi->mcl_disconnected_cb = mcl_disconnected;
+ mi->mcl_uncached_cb = mcl_uncached;
+ mi->mcl_sync_infoind_cb = mcl_sync_info_ind;
+ mi->user_data = user_data;
+ mi->csp_enabled = FALSE;
+
+ /* Listen incoming connections in control channel */
+ mi->ccio = bt_io_listen(BT_IO_L2CAP, connect_mcl_event_cb, NULL, mi,
+ NULL, gerr,
+ BT_IO_OPT_SOURCE_BDADDR, &mi->src,
+ BT_IO_OPT_PSM, ccpsm,
+ BT_IO_OPT_MTU, MCAP_CC_MTU,
+ BT_IO_OPT_SEC_LEVEL, sec,
+ BT_IO_OPT_MODE, L2CAP_MODE_ERTM,
+ BT_IO_OPT_INVALID);
+ if (!mi->ccio) {
+ error("%s", (*gerr)->message);
+ g_free(mi);
+ return NULL;
+ }
+
+ /* Listen incoming connections in data channels */
+ mi->dcio = bt_io_listen(BT_IO_L2CAP, connect_dc_event_cb, NULL, mi,
+ NULL, gerr,
+ BT_IO_OPT_SOURCE_BDADDR, &mi->src,
+ BT_IO_OPT_PSM, dcpsm,
+ BT_IO_OPT_MTU, MCAP_DC_MTU,
+ BT_IO_OPT_SEC_LEVEL, sec,
+ BT_IO_OPT_INVALID);
+ if (!mi->dcio) {
+ g_io_channel_shutdown(mi->ccio, TRUE, NULL);
+ g_io_channel_unref(mi->ccio);
+ mi->ccio = NULL;
+ error("%s", (*gerr)->message);
+ g_free(mi);
+ return NULL;
+ }
+
+ /* Initialize random seed to generate mdlids for this instance */
+ srand(time(NULL));
+
+ return mcap_instance_ref(mi);;
+}
+
+void mcap_release_instance(struct mcap_instance *mi)
+{
+ GSList *l;
+
+ if (!mi)
+ return;
+
+ if (mi->ccio) {
+ g_io_channel_shutdown(mi->ccio, TRUE, NULL);
+ g_io_channel_unref(mi->ccio);
+ mi->ccio = NULL;
+ }
+
+ if (mi->dcio) {
+ g_io_channel_shutdown(mi->dcio, TRUE, NULL);
+ g_io_channel_unref(mi->dcio);
+ mi->dcio = NULL;
+ }
+
+ for (l = mi->mcls; l; l = l->next) {
+ mcap_mcl_release(l->data);
+ mcap_mcl_unref(l->data);
+ }
+
+ g_slist_free(mi->mcls);
+ mi->mcls = NULL;
+
+ for (l = mi->cached; l; l = l->next) {
+ mcap_mcl_release(l->data);
+ mcap_mcl_unref(l->data);
+ }
+
+ g_slist_free(mi->cached);
+ mi->cached = NULL;
+}
+
+struct mcap_instance *mcap_instance_ref(struct mcap_instance *mi)
+{
+ mi->ref++;
+
+ DBG("mcap_instance_ref(%p): ref=%d", mi, mi->ref);
+
+ return mi;
+}
+
+void mcap_instance_unref(struct mcap_instance *mi)
+{
+ mi->ref--;
+
+ DBG("mcap_instance_unref(%p): ref=%d", mi, mi->ref);
+
+ if (mi->ref > 0)
+ return;
+
+ mcap_release_instance(mi);
+ g_free(mi);
+}
+
+uint16_t mcap_get_ctrl_psm(struct mcap_instance *mi, GError **err)
+{
+ uint16_t lpsm;
+
+ if (!(mi && mi->ccio)) {
+ g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS,
+ "Invalid MCAP instance");
+ return 0;
+ }
+
+ if (!bt_io_get(mi->ccio, BT_IO_L2CAP, err,
+ BT_IO_OPT_PSM, &lpsm,
+ BT_IO_OPT_INVALID))
+ return 0;
+
+ return lpsm;
+}
+
+uint16_t mcap_get_data_psm(struct mcap_instance *mi, GError **err)
+{
+ uint16_t lpsm;
+
+ if (!(mi && mi->dcio)) {
+ g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS,
+ "Invalid MCAP instance");
+ return 0;
+ }
+
+ if (!bt_io_get(mi->dcio, BT_IO_L2CAP, err,
+ BT_IO_OPT_PSM, &lpsm,
+ BT_IO_OPT_INVALID))
+ return 0;
+
+ return lpsm;
+}
+
+gboolean mcap_set_data_chan_mode(struct mcap_instance *mi, uint8_t mode,
+ GError **err)
+{
+ if (!(mi && mi->dcio)) {
+ g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS,
+ "Invalid MCAP instance");
+ return FALSE;
+ }
+
+ return bt_io_set(mi->dcio, BT_IO_L2CAP, err, BT_IO_OPT_MODE, mode,
+ BT_IO_OPT_INVALID);
+}
+
+struct mcap_mdl *mcap_mdl_ref(struct mcap_mdl *mdl)
+{
+ mdl->ref++;
+
+ DBG("mcap_mdl_ref(%p): ref=%d", mdl, mdl->ref);
+
+ return mdl;
+}
+
+void mcap_mdl_unref(struct mcap_mdl *mdl)
+{
+ mdl->ref--;
+
+ DBG("mcap_mdl_unref(%p): ref=%d", mdl, mdl->ref);
+
+ if (mdl->ref > 0)
+ return;
+
+ free_mdl(mdl);
+}
diff --git a/health/mcap.h b/health/mcap.h
new file mode 100644
index 0000000..81f53f1
--- /dev/null
+++ b/health/mcap.h
@@ -0,0 +1,169 @@
+/*
+ *
+ * MCAP for BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ * Copyright (C) 2010 Signove
+ *
+ * Authors:
+ * Santiago Carot-Nemesio <sancane at gmail.com>
+ * Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
+ * Elvis Pfützenreuter <epx at signove.com>
+ *
+ * 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
+ *
+ */
+
+#ifndef __MCAP_H
+#define __MCAP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MCAP_VERSION 0x0100 /* current version 01.00 */
+
+/* bytes to get MCAP Supported Procedures */
+#define MCAP_SUP_PROC 0x06
+
+/* maximum transmission unit for channels */
+#define MCAP_CC_MTU 48
+#define MCAP_DC_MTU L2CAP_DEFAULT_MTU
+
+/* MCAP Standard Op Codes */
+#define MCAP_ERROR_RSP 0x00
+#define MCAP_MD_CREATE_MDL_REQ 0x01
+#define MCAP_MD_CREATE_MDL_RSP 0x02
+#define MCAP_MD_RECONNECT_MDL_REQ 0x03
+#define MCAP_MD_RECONNECT_MDL_RSP 0x04
+#define MCAP_MD_ABORT_MDL_REQ 0x05
+#define MCAP_MD_ABORT_MDL_RSP 0x06
+#define MCAP_MD_DELETE_MDL_REQ 0x07
+#define MCAP_MD_DELETE_MDL_RSP 0x08
+
+/* MCAP Clock Sync Op Codes */
+#define MCAP_MD_SYNC_CAP_REQ 0x11
+#define MCAP_MD_SYNC_CAP_RSP 0x12
+#define MCAP_MD_SYNC_SET_REQ 0x13
+#define MCAP_MD_SYNC_SET_RSP 0x14
+#define MCAP_MD_SYNC_INFO_IND 0x15
+
+/* MCAP Response codes */
+#define MCAP_SUCCESS 0x00
+#define MCAP_INVALID_OP_CODE 0x01
+#define MCAP_INVALID_PARAM_VALUE 0x02
+#define MCAP_INVALID_MDEP 0x03
+#define MCAP_MDEP_BUSY 0x04
+#define MCAP_INVALID_MDL 0x05
+#define MCAP_MDL_BUSY 0x06
+#define MCAP_INVALID_OPERATION 0x07
+#define MCAP_RESOURCE_UNAVAILABLE 0x08
+#define MCAP_UNSPECIFIED_ERROR 0x09
+#define MCAP_REQUEST_NOT_SUPPORTED 0x0A
+#define MCAP_CONFIGURATION_REJECTED 0x0B
+
+/* MDL IDs */
+#define MCAP_MDLID_RESERVED 0x0000
+#define MCAP_MDLID_INITIAL 0x0001
+#define MCAP_MDLID_FINAL 0xFEFF
+#define MCAP_ALL_MDLIDS 0xFFFF
+
+/* MDEP IDs */
+#define MCAP_MDEPID_INITIAL 0x00
+#define MCAP_MDEPID_FINAL 0x7F
+
+/* CSP special values */
+#define MCAP_BTCLOCK_IMMEDIATE 0xffffffffUL
+#define MCAP_TMSTAMP_DONTSET 0xffffffffffffffffULL
+#define MCAP_BTCLOCK_MAX 0x0fffffff
+#define MCAP_BTCLOCK_FIELD (MCAP_BTCLOCK_MAX + 1)
+
+/*
+ * MCAP Request Packet Format
+ */
+
+typedef struct {
+ uint8_t op;
+ uint16_t mdl;
+ uint8_t mdep;
+ uint8_t conf;
+} __attribute__ ((packed)) mcap_md_create_mdl_req;
+
+typedef struct {
+ uint8_t op;
+ uint16_t mdl;
+} __attribute__ ((packed)) mcap_md_req;
+
+/*
+ * MCAP Response Packet Format
+ */
+
+typedef struct {
+ uint8_t op;
+ uint8_t rc;
+ uint16_t mdl;
+ uint8_t data[0];
+} __attribute__ ((packed)) mcap_rsp;
+
+/*
+ * MCAP Clock Synchronization Protocol
+ */
+
+typedef struct {
+ uint8_t op;
+ uint16_t timest;
+} __attribute__ ((packed)) mcap_md_sync_cap_req;
+
+typedef struct {
+ uint8_t op;
+ uint8_t rc;
+} __attribute__ ((packed)) mcap_md_sync_rsp;
+
+typedef struct {
+ uint8_t op;
+ uint8_t rc;
+ uint8_t btclock;
+ uint16_t sltime;
+ uint16_t timestnr;
+ uint16_t timestna;
+} __attribute__ ((packed)) mcap_md_sync_cap_rsp;
+
+typedef struct {
+ uint8_t op;
+ uint8_t timestui;
+ uint32_t btclock;
+ uint64_t timestst;
+} __attribute__ ((packed)) mcap_md_sync_set_req;
+
+typedef struct {
+ int8_t op;
+ uint8_t rc;
+ uint32_t btclock;
+ uint64_t timestst;
+ uint16_t timestsa;
+} __attribute__ ((packed)) mcap_md_sync_set_rsp;
+
+typedef struct {
+ uint8_t op;
+ uint32_t btclock;
+ uint64_t timestst;
+ uint16_t timestsa;
+} __attribute__ ((packed)) mcap_md_sync_info_ind;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MCAP_H */
diff --git a/health/mcap_internal.h b/health/mcap_internal.h
new file mode 100644
index 0000000..f82e967
--- /dev/null
+++ b/health/mcap_internal.h
@@ -0,0 +1,141 @@
+/*
+ *
+ * MCAP for BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ * Authors:
+ * Santiago Carot-Nemesio <sancane at gmail.com>
+ * Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
+ *
+ * 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
+ *
+ */
+
+#ifndef __MCAP_INTERNAL_H
+#define __MCAP_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ MCL_CONNECTED,
+ MCL_PENDING,
+ MCL_ACTIVE,
+ MCL_IDLE
+} MCLState;
+
+typedef enum {
+ MCL_ACCEPTOR,
+ MCL_INITIATOR
+} MCLRole;
+
+typedef enum {
+ MCL_AVAILABLE,
+ MCL_WAITING_RSP
+} MCAPCtrl;
+
+typedef enum {
+ MDL_WAITING,
+ MDL_CONNECTED,
+ MDL_DELETING,
+ MDL_CLOSED
+} MDLState;
+
+struct mcap_mdl_cb {
+ mcap_mdl_event_cb mdl_connected; /* Remote device has created a MDL */
+ mcap_mdl_event_cb mdl_closed; /* Remote device has closed a MDL */
+ mcap_mdl_event_cb mdl_deleted; /* Remote device requested deleting a MDL */
+ mcap_mdl_event_cb mdl_aborted; /* Remote device aborted the mdl creation */
+ mcap_remote_mdl_conn_req_cb mdl_conn_req; /* Remote device requested creating a MDL */
+ mcap_remote_mdl_reconn_req_cb mdl_reconn_req; /* Remote device requested reconnecting a MDL */
+ gpointer user_data; /* User data */
+};
+
+struct mcap_instance {
+ bdaddr_t src; /* Source address */
+ GIOChannel *ccio; /* Control Channel IO */
+ GIOChannel *dcio; /* Data Channel IO */
+ GSList *mcls; /* MCAP instance list */
+ GSList *cached; /* List with all cached MCLs (MAX_CACHED macro) */
+ BtIOSecLevel sec; /* Security level */
+ mcap_mcl_event_cb mcl_connected_cb; /* New MCL connected */
+ mcap_mcl_event_cb mcl_reconnected_cb; /* Old MCL has been reconnected */
+ mcap_mcl_event_cb mcl_disconnected_cb; /* MCL disconnected */
+ mcap_mcl_event_cb mcl_uncached_cb; /* MCL has been removed from MCAP cache */
+ mcap_info_ind_event_cb mcl_sync_infoind_cb; /* (CSP Master) Received info indication */
+ gpointer user_data; /* Data to be provided in callbacks */
+ gint ref; /* Reference counter */
+
+ gboolean csp_enabled; /* CSP: functionality enabled */
+};
+
+struct mcap_csp;
+struct mcap_mdl_op_cb;
+
+struct mcap_mcl {
+ struct mcap_instance *mi; /* MCAP instance where this MCL belongs */
+ bdaddr_t addr; /* Device address */
+ GIOChannel *cc; /* MCAP Control Channel IO */
+ guint wid; /* MCL Watcher id */
+ GSList *mdls; /* List of Data Channels shorted by mdlid */
+ MCLState state; /* Current MCL State */
+ MCLRole role; /* Initiator or acceptor of this MCL */
+ MCAPCtrl req; /* Request control flag */
+ struct mcap_mdl_op_cb *priv_data; /* Temporal data to manage responses */
+ struct mcap_mdl_cb *cb; /* MDL callbacks */
+ guint tid; /* Timer id for waiting for a response */
+ uint8_t *lcmd; /* Last command sent */
+ gint ref; /* References counter */
+ uint8_t ctrl; /* MCL control flag */
+ uint16_t next_mdl; /* id used to create next MDL */
+ struct mcap_csp *csp; /* CSP control structure */
+};
+
+#define MCAP_CTRL_CACHED 0x01 /* MCL is cached */
+#define MCAP_CTRL_STD_OP 0x02 /* Support for standard op codes */
+#define MCAP_CTRL_SYNC_OP 0x04 /* Support for synchronization commands */
+#define MCAP_CTRL_CONN 0x08 /* MCL is in connecting process */
+#define MCAP_CTRL_FREE 0x10 /* MCL is marked as releasable */
+#define MCAP_CTRL_NOCACHE 0x20 /* MCL is marked as not cacheable */
+
+struct mcap_mdl {
+ struct mcap_mcl *mcl; /* MCL where this MDL belongs */
+ GIOChannel *dc; /* MCAP Data Channel IO */
+ guint wid; /* MDL Watcher id */
+ uint16_t mdlid; /* MDL id */
+ uint8_t mdep_id; /* MCAP Data End Point */
+ MDLState state; /* MDL state */
+ gint ref; /* References counter */
+};
+
+struct sync_info_ind_data {
+ uint32_t btclock;
+ uint64_t timestamp;
+ uint16_t accuracy;
+};
+
+int mcap_send_data(int sock, const void *buf, uint32_t size);
+
+void proc_sync_cmd(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len);
+void mcap_sync_init(struct mcap_mcl *mcl);
+void mcap_sync_stop(struct mcap_mcl *mcl);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MCAP_INTERNAL_H */
diff --git a/health/mcap_lib.h b/health/mcap_lib.h
new file mode 100644
index 0000000..9c1e508
--- /dev/null
+++ b/health/mcap_lib.h
@@ -0,0 +1,228 @@
+/*
+ *
+ * MCAP for BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ * Authors:
+ * Santiago Carot-Nemesio <sancane at gmail.com>
+ * Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
+ *
+ * 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
+ *
+ */
+
+#ifndef __MCAP_LIB_H
+#define __MCAP_LIB_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+/* MCAP Error Response Codes */
+ MCAP_ERROR_INVALID_OP_CODE = 1,
+ MCAP_ERROR_INVALID_PARAM_VALUE,
+ MCAP_ERROR_INVALID_MDEP,
+ MCAP_ERROR_MDEP_BUSY,
+ MCAP_ERROR_INVALID_MDL,
+ MCAP_ERROR_MDL_BUSY,
+ MCAP_ERROR_INVALID_OPERATION,
+ MCAP_ERROR_RESOURCE_UNAVAILABLE,
+ MCAP_ERROR_UNSPECIFIED_ERROR,
+ MCAP_ERROR_REQUEST_NOT_SUPPORTED,
+ MCAP_ERROR_CONFIGURATION_REJECTED,
+/* MCAP Internal Errors */
+ MCAP_ERROR_INVALID_ARGS,
+ MCAP_ERROR_ALREADY_EXISTS,
+ MCAP_ERROR_REQ_IGNORED,
+ MCAP_ERROR_MCL_CLOSED,
+ MCAP_ERROR_FAILED
+} McapError;
+
+typedef enum {
+ MCAP_MDL_CB_INVALID,
+ MCAP_MDL_CB_CONNECTED, /* mcap_mdl_event_cb */
+ MCAP_MDL_CB_CLOSED, /* mcap_mdl_event_cb */
+ MCAP_MDL_CB_DELETED, /* mcap_mdl_event_cb */
+ MCAP_MDL_CB_ABORTED, /* mcap_mdl_event_cb */
+ MCAP_MDL_CB_REMOTE_CONN_REQ, /* mcap_remote_mdl_conn_req_cb */
+ MCAP_MDL_CB_REMOTE_RECONN_REQ /* mcap_remote_mdl_reconn_req_cb */
+} McapMclCb;
+
+struct mcap_instance;
+struct mcap_mcl;
+struct mcap_mdl;
+struct sync_info_ind_data;
+
+/************ Callbacks ************/
+
+/* MDL callbacks */
+
+typedef void (* mcap_mdl_event_cb) (struct mcap_mdl *mdl, gpointer data);
+typedef void (* mcap_mdl_operation_conf_cb) (struct mcap_mdl *mdl, uint8_t conf,
+ GError *err, gpointer data);
+typedef void (* mcap_mdl_operation_cb) (struct mcap_mdl *mdl, GError *err,
+ gpointer data);
+typedef void (* mcap_mdl_notify_cb) (GError *err, gpointer data);
+
+/* Next function should return an MCAP appropriate response code */
+typedef uint8_t (* mcap_remote_mdl_conn_req_cb) (struct mcap_mcl *mcl,
+ uint8_t mdepid, uint16_t mdlid,
+ uint8_t *conf, gpointer data);
+typedef uint8_t (* mcap_remote_mdl_reconn_req_cb) (struct mcap_mdl *mdl,
+ gpointer data);
+
+/* MCL callbacks */
+
+typedef void (* mcap_mcl_event_cb) (struct mcap_mcl *mcl, gpointer data);
+typedef void (* mcap_mcl_connect_cb) (struct mcap_mcl *mcl, GError *err,
+ gpointer data);
+
+/* CSP callbacks */
+
+typedef void (* mcap_info_ind_event_cb) (struct mcap_mcl *mcl,
+ struct sync_info_ind_data *data);
+
+typedef void (* mcap_sync_cap_cb) (struct mcap_mcl *mcl,
+ uint8_t mcap_err,
+ uint8_t btclockres,
+ uint16_t synclead,
+ uint16_t tmstampres,
+ uint16_t tmstampacc,
+ GError *err,
+ gpointer data);
+
+typedef void (* mcap_sync_set_cb) (struct mcap_mcl *mcl,
+ uint8_t mcap_err,
+ uint32_t btclock,
+ uint64_t timestamp,
+ uint16_t accuracy,
+ GError *err,
+ gpointer data);
+
+/************ Operations ************/
+
+/* MDL operations */
+
+gboolean mcap_create_mdl(struct mcap_mcl *mcl,
+ uint8_t mdepid,
+ uint8_t conf,
+ mcap_mdl_operation_conf_cb connect_cb,
+ gpointer user_data,
+ GDestroyNotify destroy,
+ GError **err);
+gboolean mcap_reconnect_mdl(struct mcap_mdl *mdl,
+ mcap_mdl_operation_cb reconnect_cb,
+ gpointer user_data,
+ GDestroyNotify destroy,
+ GError **err);
+gboolean mcap_delete_all_mdls(struct mcap_mcl *mcl,
+ mcap_mdl_notify_cb delete_cb,
+ gpointer user_data,
+ GDestroyNotify destroy,
+ GError **err);
+gboolean mcap_delete_mdl(struct mcap_mdl *mdl,
+ mcap_mdl_notify_cb delete_cb,
+ gpointer user_data,
+ GDestroyNotify destroy,
+ GError **err);
+gboolean mcap_connect_mdl(struct mcap_mdl *mdl,
+ uint8_t mode,
+ uint16_t dcpsm,
+ mcap_mdl_operation_cb connect_cb,
+ gpointer user_data,
+ GDestroyNotify destroy,
+ GError **err);
+gboolean mcap_mdl_abort(struct mcap_mdl *mdl,
+ mcap_mdl_notify_cb abort_cb,
+ gpointer user_data,
+ GDestroyNotify destroy,
+ GError **err);
+
+int mcap_mdl_get_fd(struct mcap_mdl *mdl);
+uint16_t mcap_mdl_get_mdlid(struct mcap_mdl *mdl);
+
+struct mcap_mdl *mcap_mdl_ref(struct mcap_mdl *mdl);
+void mcap_mdl_unref(struct mcap_mdl *mdl);
+
+/* MCL operations */
+
+gboolean mcap_create_mcl(struct mcap_instance *mi,
+ const bdaddr_t *addr,
+ uint16_t ccpsm,
+ mcap_mcl_connect_cb connect_cb,
+ gpointer user_data,
+ GDestroyNotify destroy,
+ GError **err);
+void mcap_close_mcl(struct mcap_mcl *mcl, gboolean cache);
+gboolean mcap_mcl_set_cb(struct mcap_mcl *mcl, gpointer user_data,
+ GError **gerr, McapMclCb cb1, ...);
+void mcap_mcl_get_addr(struct mcap_mcl *mcl, bdaddr_t *addr);
+
+struct mcap_mcl *mcap_mcl_ref(struct mcap_mcl *mcl);
+void mcap_mcl_unref(struct mcap_mcl *mcl);
+
+/* CSP operations */
+
+void mcap_enable_csp(struct mcap_instance *mi);
+void mcap_disable_csp(struct mcap_instance *mi);
+
+uint64_t mcap_get_timestamp(struct mcap_mcl *mcl,
+ struct timespec *given_time);
+uint32_t mcap_get_btclock(struct mcap_mcl *mcl);
+
+void mcap_sync_cap_req(struct mcap_mcl *mcl,
+ uint16_t reqacc,
+ mcap_sync_cap_cb cb,
+ gpointer user_data,
+ GError **err);
+
+void mcap_sync_set_req(struct mcap_mcl *mcl,
+ uint8_t update,
+ uint32_t btclock,
+ uint64_t timestamp,
+ mcap_sync_set_cb cb,
+ gpointer user_data,
+ GError **err);
+
+/* MCAP main operations */
+
+struct mcap_instance *mcap_create_instance(bdaddr_t *src,
+ BtIOSecLevel sec, uint16_t ccpsm,
+ uint16_t dcpsm,
+ mcap_mcl_event_cb mcl_connected,
+ mcap_mcl_event_cb mcl_reconnected,
+ mcap_mcl_event_cb mcl_disconnected,
+ mcap_mcl_event_cb mcl_uncached,
+ mcap_info_ind_event_cb mcl_sync_info_ind,
+ gpointer user_data,
+ GError **gerr);
+void mcap_release_instance(struct mcap_instance *mi);
+
+struct mcap_instance *mcap_instance_ref(struct mcap_instance *mi);
+void mcap_instance_unref(struct mcap_instance *mi);
+
+uint16_t mcap_get_ctrl_psm(struct mcap_instance *mi, GError **err);
+uint16_t mcap_get_data_psm(struct mcap_instance *mi, GError **err);
+
+gboolean mcap_set_data_chan_mode(struct mcap_instance *mi, uint8_t mode,
+ GError **err);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MCAP_LIB_H */
diff --git a/health/mcap_sync.c b/health/mcap_sync.c
new file mode 100644
index 0000000..f4b005a
--- /dev/null
+++ b/health/mcap_sync.c
@@ -0,0 +1,1015 @@
+/*
+ *
+ * MCAP for BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ * Copyright (C) 2010 Signove
+ *
+ * Authors:
+ * Santiago Carot-Nemesio <sancane at gmail.com>
+ * Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
+ * Elvis Pfützenreuter <epx at signove.com>
+ *
+ * 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
+ *
+ */
+
+#include "btio.h"
+#include <stdint.h>
+#include <netinet/in.h>
+#include <time.h>
+#include <stdlib.h>
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#include "../src/adapter.h"
+#include "../src/manager.h"
+#include <sys/ioctl.h>
+
+#include "config.h"
+#include "log.h"
+
+#include <bluetooth/bluetooth.h>
+#include "mcap.h"
+#include "mcap_lib.h"
+#include "mcap_internal.h"
+
+#define MCAP_BTCLOCK_HALF (MCAP_BTCLOCK_FIELD / 2)
+#define CLK CLOCK_MONOTONIC
+
+#define MCAP_CSP_ERROR g_quark_from_static_string("mcap-csp-error-quark")
+#define MAX_RETRIES 10
+#define SAMPLE_COUNT 20
+
+struct mcap_csp {
+ uint64_t base_tmstamp; /* CSP base timestamp */
+ struct timespec base_time; /* CSP base time when timestamp set */
+ guint local_caps; /* CSP-Master: have got remote caps */
+ guint remote_caps; /* CSP-Slave: remote master got caps */
+ guint rem_req_acc; /* CSP-Slave: accuracy required by master */
+ guint ind_expected; /* CSP-Master: indication expected */
+ MCAPCtrl csp_req; /* CSP-Master: Request control flag */
+ guint ind_timer; /* CSP-Slave: indication timer */
+ guint set_timer; /* CSP-Slave: delayed set timer */
+ void *set_data; /* CSP-Slave: delayed set data */
+ void *csp_priv_data; /* CSP-Master: In-flight request data */
+};
+
+struct mcap_sync_cap_cbdata {
+ mcap_sync_cap_cb cb;
+ gpointer user_data;
+};
+
+struct mcap_sync_set_cbdata {
+ mcap_sync_set_cb cb;
+ gpointer user_data;
+};
+
+struct csp_caps {
+ int ts_acc; /* timestamp accuracy */
+ int ts_res; /* timestamp resolution */
+ int latency; /* Read BT clock latency */
+ int preempt_thresh; /* Preemption threshold for latency */
+ int syncleadtime_ms; /* SyncLeadTime in ms */
+};
+
+struct sync_set_data {
+ uint8_t update;
+ uint32_t sched_btclock;
+ uint64_t timestamp;
+ int ind_freq;
+ gboolean role;
+};
+
+#define hton64(x) ntoh64(x)
+
+static gboolean csp_caps_initialized = FALSE;
+struct csp_caps _caps;
+
+static int send_sync_cmd(struct mcap_mcl *mcl, const void *buf, uint32_t size)
+{
+ int sock;
+
+ if (mcl->cc == NULL)
+ return -1;
+
+ sock = g_io_channel_unix_get_fd(mcl->cc);
+ return mcap_send_data(sock, buf, size);
+}
+
+static int send_unsupported_cap_req(struct mcap_mcl *mcl)
+{
+ mcap_md_sync_cap_rsp *cmd;
+ int sent;
+
+ cmd = g_new0(mcap_md_sync_cap_rsp, 1);
+ cmd->op = MCAP_MD_SYNC_CAP_RSP;
+ cmd->rc = MCAP_REQUEST_NOT_SUPPORTED;
+
+ sent = send_sync_cmd(mcl, cmd, sizeof(*cmd));
+ g_free(cmd);
+
+ return sent;
+}
+
+static int send_unsupported_set_req(struct mcap_mcl *mcl)
+{
+ mcap_md_sync_set_rsp *cmd;
+ int sent;
+
+ cmd = g_new0(mcap_md_sync_set_rsp, 1);
+ cmd->op = MCAP_MD_SYNC_SET_RSP;
+ cmd->rc = MCAP_REQUEST_NOT_SUPPORTED;
+
+ sent = send_sync_cmd(mcl, cmd, sizeof(*cmd));
+ g_free(cmd);
+
+ return sent;
+}
+
+static void reset_tmstamp(struct mcap_csp *csp, struct timespec *base_time,
+ uint64_t new_tmstamp)
+{
+ csp->base_tmstamp = new_tmstamp;
+ if (base_time)
+ csp->base_time = *base_time;
+ else
+ clock_gettime(CLK, &csp->base_time);
+}
+
+void mcap_sync_init(struct mcap_mcl *mcl)
+{
+ if (!mcl->mi->csp_enabled) {
+ mcl->csp = NULL;
+ return;
+ }
+
+ mcl->csp = g_new0(struct mcap_csp, 1);
+
+ mcl->csp->rem_req_acc = 10000; /* safe divisor */
+ mcl->csp->set_data = NULL;
+ mcl->csp->csp_priv_data = NULL;
+
+ reset_tmstamp(mcl->csp, NULL, 0);
+}
+
+void mcap_sync_stop(struct mcap_mcl *mcl)
+{
+ if (!mcl->csp)
+ return;
+
+ if (mcl->csp->ind_timer)
+ g_source_remove(mcl->csp->ind_timer);
+
+ if (mcl->csp->set_timer)
+ g_source_remove(mcl->csp->set_timer);
+
+ if (mcl->csp->set_data)
+ g_free(mcl->csp->set_data);
+
+ if (mcl->csp->csp_priv_data)
+ g_free(mcl->csp->csp_priv_data);
+
+ mcl->csp->ind_timer = 0;
+ mcl->csp->set_timer = 0;
+ mcl->csp->set_data = NULL;
+ mcl->csp->csp_priv_data = NULL;
+
+ g_free(mcl->csp);
+ mcl->csp = NULL;
+}
+
+static uint64_t time_us(struct timespec *tv)
+{
+ return tv->tv_sec * 1000000 + tv->tv_nsec / 1000;
+}
+
+static int64_t bt2us(int bt)
+{
+ return bt * 312.5;
+}
+
+static int bt2ms(int bt)
+{
+ return bt * 312.5 / 1000;
+}
+
+static int btoffset(uint32_t btclk1, uint32_t btclk2)
+{
+ int offset = btclk2 - btclk1;
+
+ if (offset <= -MCAP_BTCLOCK_HALF)
+ offset += MCAP_BTCLOCK_FIELD;
+ else if (offset > MCAP_BTCLOCK_HALF)
+ offset -= MCAP_BTCLOCK_FIELD;
+
+ return offset;
+}
+
+static int btdiff(uint32_t btclk1, uint32_t btclk2)
+{
+ return btoffset(btclk1, btclk2);
+}
+
+static gboolean valid_btclock(uint32_t btclk)
+{
+ return btclk <= MCAP_BTCLOCK_MAX;
+}
+
+/* This call may fail; either deal with retry or use read_btclock_retry */
+static gboolean read_btclock(struct mcap_mcl *mcl, uint32_t *btclock,
+ uint16_t *btaccuracy)
+{
+ int which = 1;
+ struct btd_adapter *adapter;
+
+ adapter = manager_find_adapter(&mcl->mi->src);
+
+ if (!adapter)
+ return FALSE;
+
+ if (btd_adapter_read_clock(adapter, &mcl->addr, which, 1000,
+ btclock, btaccuracy) < 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean read_btclock_retry(struct mcap_mcl *mcl, uint32_t *btclock,
+ uint16_t *btaccuracy)
+{
+ int retries = 5;
+
+ while (--retries >= 0) {
+ if (read_btclock(mcl, btclock, btaccuracy))
+ return TRUE;
+ DBG("CSP: retrying to read bt clock...");
+ }
+
+ return FALSE;
+}
+
+static gboolean get_btrole(struct mcap_mcl *mcl)
+{
+ int sock, flags;
+ socklen_t len;
+
+ if (mcl->cc == NULL)
+ return -1;
+
+ sock = g_io_channel_unix_get_fd(mcl->cc);
+ len = sizeof(flags);
+
+ if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, &len))
+ DBG("CSP: could not read role");
+
+ return flags & L2CAP_LM_MASTER;
+}
+
+uint64_t mcap_get_timestamp(struct mcap_mcl *mcl,
+ struct timespec *given_time)
+{
+ struct timespec now;
+ uint64_t tmstamp;
+
+ if (!mcl->csp)
+ return MCAP_TMSTAMP_DONTSET;
+
+ if (given_time)
+ now = *given_time;
+ else
+ clock_gettime(CLK, &now);
+
+ tmstamp = time_us(&now) - time_us(&mcl->csp->base_time)
+ + mcl->csp->base_tmstamp;
+
+ return tmstamp;
+}
+
+uint32_t mcap_get_btclock(struct mcap_mcl *mcl)
+{
+ uint32_t btclock;
+ uint16_t accuracy;
+
+ if (!mcl->csp)
+ return MCAP_BTCLOCK_IMMEDIATE;
+
+ if (!read_btclock_retry(mcl, &btclock, &accuracy))
+ btclock = 0xffffffff;
+
+ return btclock;
+}
+
+static gboolean initialize_caps(struct mcap_mcl *mcl)
+{
+ struct timespec t1, t2;
+ int latencies[SAMPLE_COUNT];
+ int latency, avg, dev;
+ uint32_t btclock;
+ uint16_t btaccuracy;
+ int i;
+ int retries;
+
+ clock_getres(CLK, &t1);
+
+ _caps.ts_res = time_us(&t1);
+ if (_caps.ts_res < 1)
+ _caps.ts_res = 1;
+
+ _caps.ts_acc = 20; /* ppm, estimated */
+
+ /* A little exercise before measuing latency */
+ clock_gettime(CLK, &t1);
+ read_btclock_retry(mcl, &btclock, &btaccuracy);
+
+ /* Read clock a number of times and measure latency */
+ avg = 0;
+ i = 0;
+ retries = MAX_RETRIES;
+ while (i < SAMPLE_COUNT && retries > 0) {
+ clock_gettime(CLK, &t1);
+ if (!read_btclock(mcl, &btclock, &btaccuracy)) {
+ retries--;
+ continue;
+ }
+ clock_gettime(CLK, &t2);
+
+ latency = time_us(&t2) - time_us(&t1);
+ latencies[i] = latency;
+ avg += latency;
+ i++;
+ }
+
+ if (retries <= 0)
+ return FALSE;
+
+ /* Calculate average and deviation */
+ avg /= SAMPLE_COUNT;
+ dev = 0;
+ for (i = 0; i < SAMPLE_COUNT; ++i)
+ dev += abs(latencies[i] - avg);
+ dev /= SAMPLE_COUNT;
+
+ /* Calculate corrected average, without 'freak' latencies */
+ latency = 0;
+ for (i = 0; i < SAMPLE_COUNT; ++i) {
+ if (latencies[i] > (avg + dev * 6))
+ latency += avg;
+ else
+ latency += latencies[i];
+ }
+ latency /= SAMPLE_COUNT;
+
+ _caps.latency = latency;
+ _caps.preempt_thresh = latency * 4;
+ _caps.syncleadtime_ms = latency * 50 / 1000;
+
+ csp_caps_initialized = TRUE;
+ return TRUE;
+}
+
+static struct csp_caps *caps(struct mcap_mcl *mcl)
+{
+ if (!csp_caps_initialized)
+ if (!initialize_caps(mcl)) {
+ /* Temporary failure in reading BT clock */
+ return NULL;
+ }
+
+ return &_caps;
+}
+
+static int send_sync_cap_rsp(struct mcap_mcl *mcl, uint8_t rspcode,
+ uint8_t btclockres, uint16_t synclead,
+ uint16_t tmstampres, uint16_t tmstampacc)
+{
+ mcap_md_sync_cap_rsp *rsp;
+ int sent;
+
+ rsp = g_new0(mcap_md_sync_cap_rsp, 1);
+
+ rsp->op = MCAP_MD_SYNC_CAP_RSP;
+ rsp->rc = rspcode;
+
+ rsp->btclock = btclockres;
+ rsp->sltime = htons(synclead);
+ rsp->timestnr = htons(tmstampres);
+ rsp->timestna = htons(tmstampacc);
+
+ sent = send_sync_cmd(mcl, rsp, sizeof(*rsp));
+ g_free(rsp);
+
+ return sent;
+}
+
+static void proc_sync_cap_req(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+ mcap_md_sync_cap_req *req;
+ uint16_t required_accuracy;
+ uint16_t our_accuracy;
+ uint32_t btclock;
+ uint16_t btres;
+
+ if (len != sizeof(mcap_md_sync_cap_req)) {
+ send_sync_cap_rsp(mcl, MCAP_INVALID_PARAM_VALUE,
+ 0, 0, 0, 0);
+ return;
+ }
+
+ if (!caps(mcl)) {
+ send_sync_cap_rsp(mcl, MCAP_RESOURCE_UNAVAILABLE,
+ 0, 0, 0, 0);
+ return;
+ }
+
+ req = (mcap_md_sync_cap_req *) cmd;
+ required_accuracy = ntohs(req->timest);
+ our_accuracy = caps(mcl)->ts_acc;
+
+ if (required_accuracy < our_accuracy || required_accuracy < 1) {
+ send_sync_cap_rsp(mcl, MCAP_RESOURCE_UNAVAILABLE,
+ 0, 0, 0, 0);
+ return;
+ }
+
+ if (!read_btclock_retry(mcl, &btclock, &btres)) {
+ send_sync_cap_rsp(mcl, MCAP_RESOURCE_UNAVAILABLE,
+ 0, 0, 0, 0);
+ return;
+ }
+
+ mcl->csp->remote_caps = 1;
+ mcl->csp->rem_req_acc = required_accuracy;
+
+ send_sync_cap_rsp(mcl, MCAP_SUCCESS, btres,
+ caps(mcl)->syncleadtime_ms,
+ caps(mcl)->ts_res, our_accuracy);
+}
+
+static int send_sync_set_rsp(struct mcap_mcl *mcl, uint8_t rspcode,
+ uint32_t btclock, uint64_t timestamp,
+ uint16_t tmstampres)
+{
+ mcap_md_sync_set_rsp *rsp;
+ int sent;
+
+ rsp = g_new0(mcap_md_sync_set_rsp, 1);
+
+ rsp->op = MCAP_MD_SYNC_SET_RSP;
+ rsp->rc = rspcode;
+ rsp->btclock = htonl(btclock);
+ rsp->timestst = hton64(timestamp);
+ rsp->timestsa = htons(tmstampres);
+
+ sent = send_sync_cmd(mcl, rsp, sizeof(*rsp));
+ g_free(rsp);
+
+ return sent;
+}
+
+static gboolean get_all_clocks(struct mcap_mcl *mcl, uint32_t *btclock,
+ struct timespec *base_time,
+ uint64_t *timestamp)
+{
+ int latency;
+ int retry = 5;
+ uint16_t btres;
+ struct timespec t0;
+
+ if (!caps(mcl))
+ return FALSE;
+
+ latency = caps(mcl)->preempt_thresh + 1;
+
+ while (latency > caps(mcl)->preempt_thresh && --retry >= 0) {
+
+ clock_gettime(CLK, &t0);
+
+ if (!read_btclock(mcl, btclock, &btres))
+ continue;
+
+ clock_gettime(CLK, base_time);
+
+ /* Tries to detect preemption between clock_gettime
+ * and read_btclock by measuring transaction time
+ */
+ latency = time_us(base_time) - time_us(&t0);
+ }
+
+ *timestamp = mcap_get_timestamp(mcl, base_time);
+
+ return TRUE;
+}
+
+static gboolean sync_send_indication(gpointer user_data)
+{
+ struct mcap_mcl *mcl;
+ mcap_md_sync_info_ind *cmd;
+ uint32_t btclock;
+ uint64_t tmstamp;
+ struct timespec base_time;
+ int sent;
+
+ if (!user_data)
+ return FALSE;
+
+ mcl = user_data;
+
+ if (!caps(mcl))
+ return FALSE;
+
+ if (!get_all_clocks(mcl, &btclock, &base_time, &tmstamp))
+ return FALSE;
+
+ cmd = g_new0(mcap_md_sync_info_ind, 1);
+
+ cmd->op = MCAP_MD_SYNC_INFO_IND;
+ cmd->btclock = htonl(btclock);
+ cmd->timestst = hton64(tmstamp);
+ cmd->timestsa = htons(caps(mcl)->latency);
+
+ sent = send_sync_cmd(mcl, cmd, sizeof(*cmd));
+ g_free(cmd);
+
+ return !sent;
+}
+
+static gboolean proc_sync_set_req_phase2(gpointer user_data)
+{
+ struct mcap_mcl *mcl;
+ struct sync_set_data *data;
+ uint8_t update;
+ uint32_t sched_btclock;
+ uint64_t new_tmstamp;
+ int ind_freq;
+ int role;
+ uint32_t btclock;
+ uint64_t tmstamp;
+ struct timespec base_time;
+ uint16_t tmstampacc;
+ gboolean reset;
+ int delay;
+
+ if (!user_data)
+ return FALSE;
+
+ mcl = user_data;
+
+ if (!mcl->csp->set_data)
+ return FALSE;
+
+ data = mcl->csp->set_data;
+ update = data->update;
+ sched_btclock = data->sched_btclock;
+ new_tmstamp = data->timestamp;
+ ind_freq = data->ind_freq;
+ role = data->role;
+
+ if (!caps(mcl)) {
+ send_sync_set_rsp(mcl, MCAP_UNSPECIFIED_ERROR, 0, 0, 0);
+ return FALSE;
+ }
+
+ if (!get_all_clocks(mcl, &btclock, &base_time, &tmstamp)) {
+ send_sync_set_rsp(mcl, MCAP_UNSPECIFIED_ERROR, 0, 0, 0);
+ return FALSE;
+ }
+
+ if (get_btrole(mcl) != role) {
+ send_sync_set_rsp(mcl, MCAP_INVALID_OPERATION, 0, 0, 0);
+ return FALSE;
+ }
+
+ reset = (new_tmstamp != MCAP_TMSTAMP_DONTSET);
+
+ if (reset) {
+ if (sched_btclock != MCAP_BTCLOCK_IMMEDIATE) {
+ delay = bt2us(btdiff(sched_btclock, btclock));
+ if (delay >= 0 || ((new_tmstamp - delay) > 0)) {
+ new_tmstamp += delay;
+ DBG("CSP: reset w/ delay %dus, compensated",
+ delay);
+ } else
+ DBG("CSP: reset w/ delay %dus, uncompensated",
+ delay);
+ }
+
+ reset_tmstamp(mcl->csp, &base_time, new_tmstamp);
+ tmstamp = new_tmstamp;
+ }
+
+ tmstampacc = caps(mcl)->latency + caps(mcl)->ts_acc;
+
+ if (mcl->csp->ind_timer) {
+ g_source_remove(mcl->csp->ind_timer);
+ mcl->csp->ind_timer = 0;
+ }
+
+ if (update) {
+ int when = ind_freq + caps(mcl)->syncleadtime_ms;
+ mcl->csp->ind_timer = g_timeout_add(when,
+ sync_send_indication,
+ mcl);
+ }
+
+ send_sync_set_rsp(mcl, MCAP_SUCCESS, btclock, tmstamp, tmstampacc);
+
+ /* First indication after set is immediate */
+ if (update)
+ sync_send_indication(mcl);
+
+ return FALSE;
+}
+
+static void proc_sync_set_req(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+ mcap_md_sync_set_req *req;
+ uint32_t sched_btclock, cur_btclock;
+ uint16_t btres;
+ uint8_t update;
+ uint64_t timestamp;
+ struct sync_set_data *set_data;
+ int phase2_delay, ind_freq, when;
+
+ if (len != sizeof(mcap_md_sync_set_req)) {
+ send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE, 0, 0, 0);
+ return;
+ }
+
+ req = (mcap_md_sync_set_req *) cmd;
+ sched_btclock = ntohl(req->btclock);
+ update = req->timestui;
+ timestamp = ntoh64(req->timestst);
+
+ if (sched_btclock != MCAP_BTCLOCK_IMMEDIATE &&
+ !valid_btclock(sched_btclock)) {
+ send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE, 0, 0, 0);
+ return;
+ }
+
+ if (update > 1) {
+ send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE, 0, 0, 0);
+ return;
+ }
+
+ if (!mcl->csp->remote_caps) {
+ /* Remote side did not ask our capabilities yet */
+ send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE, 0, 0, 0);
+ return;
+ }
+
+ if (!caps(mcl)) {
+ send_sync_set_rsp(mcl, MCAP_UNSPECIFIED_ERROR, 0, 0, 0);
+ return;
+ }
+
+ if (!read_btclock_retry(mcl, &cur_btclock, &btres)) {
+ send_sync_set_rsp(mcl, MCAP_UNSPECIFIED_ERROR, 0, 0, 0);
+ return;
+ }
+
+ if (sched_btclock == MCAP_BTCLOCK_IMMEDIATE)
+ phase2_delay = 0;
+ else {
+ phase2_delay = btdiff(cur_btclock, sched_btclock);
+
+ if (phase2_delay < 0) {
+ /* can not reset in the past tense */
+ send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE,
+ 0, 0, 0);
+ return;
+ }
+
+ /* Convert to miliseconds */
+ phase2_delay = bt2ms(phase2_delay);
+
+ if (phase2_delay > 61*1000) {
+ /* More than 60 seconds in the future */
+ send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE,
+ 0, 0, 0);
+ return;
+ } else if (phase2_delay < caps(mcl)->latency / 1000) {
+ /* Too fast for us to do in time */
+ send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE,
+ 0, 0, 0);
+ return;
+ }
+ }
+
+ if (update) {
+ /* Indication frequency: required accuracy divided by ours */
+ /* Converted to milisseconds */
+ ind_freq = (1000 * mcl->csp->rem_req_acc) / caps(mcl)->ts_acc;
+
+ if (ind_freq < MAX(caps(mcl)->latency * 2 / 1000, 100)) {
+ /* Too frequent, we can't handle */
+ send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE,
+ 0, 0, 0);
+ return;
+ }
+
+ DBG("CSP: indication every %dms", ind_freq);
+ } else
+ ind_freq = 0;
+
+ if (mcl->csp->ind_timer) {
+ /* Old indications are no longer sent */
+ g_source_remove(mcl->csp->ind_timer);
+ mcl->csp->ind_timer = 0;
+ }
+
+ if (!mcl->csp->set_data)
+ mcl->csp->set_data = g_new0(struct sync_set_data, 1);
+
+ set_data = (struct sync_set_data *) mcl->csp->set_data;
+
+ set_data->update = update;
+ set_data->sched_btclock = sched_btclock;
+ set_data->timestamp = timestamp;
+ set_data->ind_freq = ind_freq;
+ set_data->role = get_btrole(mcl);
+
+ /* TODO is there some way to schedule a call based directly on
+ * a BT clock value, instead of this estimation that uses
+ * the SO clock? */
+
+ if (phase2_delay > 0) {
+ when = phase2_delay + caps(mcl)->syncleadtime_ms;
+ mcl->csp->set_timer = g_timeout_add(when,
+ proc_sync_set_req_phase2,
+ mcl);
+ } else
+ proc_sync_set_req_phase2(mcl);
+
+ /* First indication is immediate */
+ if (update)
+ sync_send_indication(mcl);
+}
+
+static void proc_sync_cap_rsp(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+ mcap_md_sync_cap_rsp *rsp;
+ uint8_t mcap_err;
+ uint8_t btclockres;
+ uint16_t synclead;
+ uint16_t tmstampres;
+ uint16_t tmstampacc;
+ struct mcap_sync_cap_cbdata *cbdata;
+ mcap_sync_cap_cb cb;
+ gpointer user_data;
+
+ if (mcl->csp->csp_req != MCAP_MD_SYNC_CAP_REQ) {
+ DBG("CSP: got unexpected cap respose");
+ return;
+ }
+
+ if (!mcl->csp->csp_priv_data) {
+ DBG("CSP: no priv data for cap respose");
+ return;
+ }
+
+ cbdata = mcl->csp->csp_priv_data;
+ cb = cbdata->cb;
+ user_data = cbdata->user_data;
+ g_free(cbdata);
+
+ mcl->csp->csp_priv_data = NULL;
+ mcl->csp->csp_req = 0;
+
+ if (len != sizeof(mcap_md_sync_cap_rsp)) {
+ DBG("CSP: got corrupted cap respose");
+ return;
+ }
+
+ rsp = (mcap_md_sync_cap_rsp *) cmd;
+ mcap_err = rsp->rc;
+ btclockres = rsp->btclock;
+ synclead = ntohs(rsp->sltime);
+ tmstampres = ntohs(rsp->timestnr);
+ tmstampacc = ntohs(rsp->timestna);
+
+ if (!mcap_err)
+ mcl->csp->local_caps = TRUE;
+
+ cb(mcl, mcap_err, btclockres, synclead, tmstampres, tmstampacc, NULL,
+ user_data);
+}
+
+static void proc_sync_set_rsp(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+ mcap_md_sync_set_rsp *rsp;
+ uint8_t mcap_err;
+ uint32_t btclock;
+ uint64_t timestamp;
+ uint16_t accuracy;
+ struct mcap_sync_set_cbdata *cbdata;
+ mcap_sync_set_cb cb;
+ gpointer user_data;
+
+ if (mcl->csp->csp_req != MCAP_MD_SYNC_SET_REQ) {
+ DBG("CSP: got unexpected set respose");
+ return;
+ }
+
+ if (!mcl->csp->csp_priv_data) {
+ DBG("CSP: no priv data for set respose");
+ return;
+ }
+
+ cbdata = mcl->csp->csp_priv_data;
+ cb = cbdata->cb;
+ user_data = cbdata->user_data;
+ g_free(cbdata);
+
+ mcl->csp->csp_priv_data = NULL;
+ mcl->csp->csp_req = 0;
+
+ if (len != sizeof(mcap_md_sync_set_rsp)) {
+ DBG("CSP: got corrupted set respose");
+ return;
+ }
+
+ rsp = (mcap_md_sync_set_rsp *) cmd;
+ mcap_err = rsp->rc;
+ btclock = ntohl(rsp->btclock);
+ timestamp = ntoh64(rsp->timestst);
+ accuracy = ntohs(rsp->timestsa);
+
+ if (!mcap_err && !valid_btclock(btclock))
+ mcap_err = MCAP_ERROR_INVALID_ARGS;
+
+ cb(mcl, mcap_err, btclock, timestamp, accuracy, NULL, user_data);
+}
+
+static void proc_sync_info_ind(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+ mcap_md_sync_info_ind *req;
+ struct sync_info_ind_data data;
+ uint32_t btclock;
+
+ if (!mcl->csp->ind_expected) {
+ DBG("CSP: received unexpected info indication");
+ return;
+ }
+
+ if (len != sizeof(mcap_md_sync_info_ind))
+ return;
+
+ req = (mcap_md_sync_info_ind *) cmd;
+
+ btclock = ntohl(req->btclock);
+
+ if (!valid_btclock(btclock))
+ return;
+
+ data.btclock = btclock;
+ data.timestamp = ntoh64(req->timestst);
+ data.accuracy = ntohs(req->timestsa);
+
+ if (mcl->mi->mcl_sync_infoind_cb)
+ mcl->mi->mcl_sync_infoind_cb(mcl, &data);
+}
+
+void proc_sync_cmd(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+ if (!mcl->mi->csp_enabled || !mcl->csp) {
+ switch (cmd[0]) {
+ case MCAP_MD_SYNC_CAP_REQ:
+ send_unsupported_cap_req(mcl);
+ break;
+ case MCAP_MD_SYNC_SET_REQ:
+ send_unsupported_set_req(mcl);
+ break;
+ }
+ return;
+ }
+
+ switch (cmd[0]) {
+ case MCAP_MD_SYNC_CAP_REQ:
+ proc_sync_cap_req(mcl, cmd, len);
+ break;
+ case MCAP_MD_SYNC_CAP_RSP:
+ proc_sync_cap_rsp(mcl, cmd, len);
+ break;
+ case MCAP_MD_SYNC_SET_REQ:
+ proc_sync_set_req(mcl, cmd, len);
+ break;
+ case MCAP_MD_SYNC_SET_RSP:
+ proc_sync_set_rsp(mcl, cmd, len);
+ break;
+ case MCAP_MD_SYNC_INFO_IND:
+ proc_sync_info_ind(mcl, cmd, len);
+ break;
+ }
+}
+
+void mcap_sync_cap_req(struct mcap_mcl *mcl, uint16_t reqacc,
+ mcap_sync_cap_cb cb, gpointer user_data,
+ GError **err)
+{
+ struct mcap_sync_cap_cbdata *cbdata;
+ mcap_md_sync_cap_req *cmd;
+
+ if (!mcl->mi->csp_enabled || !mcl->csp) {
+ g_set_error(err,
+ MCAP_CSP_ERROR,
+ MCAP_ERROR_RESOURCE_UNAVAILABLE,
+ "CSP not enabled for the instance");
+ return;
+ }
+
+ if (mcl->csp->csp_req) {
+ g_set_error(err,
+ MCAP_CSP_ERROR,
+ MCAP_ERROR_RESOURCE_UNAVAILABLE,
+ "Pending CSP request");
+ return;
+ }
+
+ mcl->csp->csp_req = MCAP_MD_SYNC_CAP_REQ;
+ cmd = g_new0(mcap_md_sync_cap_req, 1);
+
+ cmd->op = MCAP_MD_SYNC_CAP_REQ;
+ cmd->timest = htons(reqacc);
+
+ cbdata = g_new0(struct mcap_sync_cap_cbdata, 1);
+ cbdata->cb = cb;
+ cbdata->user_data = user_data;
+ mcl->csp->csp_priv_data = cbdata;
+
+ send_sync_cmd(mcl, cmd, sizeof(*cmd));
+
+ g_free(cmd);
+}
+
+void mcap_sync_set_req(struct mcap_mcl *mcl, uint8_t update, uint32_t btclock,
+ uint64_t timestamp, mcap_sync_set_cb cb,
+ gpointer user_data, GError **err)
+{
+ mcap_md_sync_set_req *cmd;
+ struct mcap_sync_set_cbdata *cbdata;
+
+ if (!mcl->mi->csp_enabled || !mcl->csp) {
+ g_set_error(err,
+ MCAP_CSP_ERROR,
+ MCAP_ERROR_RESOURCE_UNAVAILABLE,
+ "CSP not enabled for the instance");
+ return;
+ }
+
+ if (!mcl->csp->local_caps) {
+ g_set_error(err,
+ MCAP_CSP_ERROR,
+ MCAP_ERROR_RESOURCE_UNAVAILABLE,
+ "Did not get CSP caps from slave yet");
+ return;
+ }
+
+ if (mcl->csp->csp_req) {
+ g_set_error(err,
+ MCAP_CSP_ERROR,
+ MCAP_ERROR_RESOURCE_UNAVAILABLE,
+ "Pending CSP request");
+ return;
+ }
+
+ mcl->csp->csp_req = MCAP_MD_SYNC_SET_REQ;
+ cmd = g_new0(mcap_md_sync_set_req, 1);
+
+ cmd->op = MCAP_MD_SYNC_SET_REQ;
+ cmd->timestui = update;
+ cmd->btclock = htonl(btclock);
+ cmd->timestst = hton64(timestamp);
+
+ mcl->csp->ind_expected = update;
+
+ cbdata = g_new0(struct mcap_sync_set_cbdata, 1);
+ cbdata->cb = cb;
+ cbdata->user_data = user_data;
+ mcl->csp->csp_priv_data = cbdata;
+
+ send_sync_cmd(mcl, cmd, sizeof(*cmd));
+
+ g_free(cmd);
+}
+
+void mcap_enable_csp(struct mcap_instance *mi)
+{
+ mi->csp_enabled = TRUE;
+}
+
+void mcap_disable_csp(struct mcap_instance *mi)
+{
+ mi->csp_enabled = FALSE;
+}
diff --git a/input/device.c b/input/device.c
new file mode 100644
index 0000000..554f5ac
--- /dev/null
+++ b/input/device.c
@@ -0,0 +1,1265 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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 <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hidp.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+#include "textfile.h"
+#include "uinput.h"
+
+#include "../src/adapter.h"
+#include "../src/device.h"
+#include "../src/storage.h"
+#include "../src/manager.h"
+#include "../src/dbus-common.h"
+
+#include "device.h"
+#include "error.h"
+#include "fakehid.h"
+#include "btio.h"
+
+#define INPUT_DEVICE_INTERFACE "org.bluez.Input"
+
+#define BUF_SIZE 16
+
+#define UPDOWN_ENABLED 1
+
+#define FI_FLAG_CONNECTED 1
+
+struct input_conn {
+ struct fake_input *fake;
+ DBusMessage *pending_connect;
+ char *uuid;
+ char *alias;
+ GIOChannel *ctrl_io;
+ GIOChannel *intr_io;
+ guint ctrl_watch;
+ guint intr_watch;
+ int timeout;
+ struct input_device *idev;
+};
+
+struct input_device {
+ DBusConnection *conn;
+ char *path;
+ bdaddr_t src;
+ bdaddr_t dst;
+ uint32_t handle;
+ guint dc_id;
+ char *name;
+ struct btd_device *device;
+ GSList *connections;
+};
+
+GSList *devices = NULL;
+
+static struct input_device *find_device_by_path(GSList *list, const char *path)
+{
+ GSList *l;
+
+ for (l = list; l; l = l->next) {
+ struct input_device *idev = l->data;
+
+ if (!strcmp(idev->path, path))
+ return idev;
+ }
+
+ return NULL;
+}
+
+static struct input_conn *find_connection(GSList *list, const char *pattern)
+{
+ GSList *l;
+
+ for (l = list; l; l = l->next) {
+ struct input_conn *iconn = l->data;
+
+ if (!strcasecmp(iconn->uuid, pattern))
+ return iconn;
+
+ if (!strcasecmp(iconn->alias, pattern))
+ return iconn;
+ }
+
+ return NULL;
+}
+
+static void input_conn_free(struct input_conn *iconn)
+{
+ if (iconn->pending_connect)
+ dbus_message_unref(iconn->pending_connect);
+
+ if (iconn->ctrl_watch)
+ g_source_remove(iconn->ctrl_watch);
+
+ if (iconn->intr_watch)
+ g_source_remove(iconn->intr_watch);
+
+ if (iconn->intr_io)
+ g_io_channel_unref(iconn->intr_io);
+
+ if (iconn->ctrl_io)
+ g_io_channel_unref(iconn->ctrl_io);
+
+ g_free(iconn->uuid);
+ g_free(iconn->alias);
+ g_free(iconn->fake);
+ g_free(iconn);
+}
+
+static void input_device_free(struct input_device *idev)
+{
+ if (idev->dc_id)
+ device_remove_disconnect_watch(idev->device, idev->dc_id);
+
+ dbus_connection_unref(idev->conn);
+ btd_device_unref(idev->device);
+ g_free(idev->name);
+ g_free(idev->path);
+ g_free(idev);
+}
+
+static int uinput_create(char *name)
+{
+ struct uinput_dev dev;
+ int fd, err;
+
+ fd = open("/dev/uinput", O_RDWR);
+ if (fd < 0) {
+ fd = open("/dev/input/uinput", O_RDWR);
+ if (fd < 0) {
+ fd = open("/dev/misc/uinput", O_RDWR);
+ if (fd < 0) {
+ err = errno;
+ error("Can't open input device: %s (%d)",
+ strerror(err), err);
+ return -err;
+ }
+ }
+ }
+
+ memset(&dev, 0, sizeof(dev));
+ if (name)
+ strncpy(dev.name, name, UINPUT_MAX_NAME_SIZE - 1);
+
+ dev.id.bustype = BUS_BLUETOOTH;
+ dev.id.vendor = 0x0000;
+ dev.id.product = 0x0000;
+ dev.id.version = 0x0000;
+
+ if (write(fd, &dev, sizeof(dev)) < 0) {
+ err = errno;
+ error("Can't write device information: %s (%d)",
+ strerror(err), err);
+ close(fd);
+ errno = err;
+ return -err;
+ }
+
+ ioctl(fd, UI_SET_EVBIT, EV_KEY);
+ ioctl(fd, UI_SET_EVBIT, EV_REL);
+ ioctl(fd, UI_SET_EVBIT, EV_REP);
+
+ ioctl(fd, UI_SET_KEYBIT, KEY_UP);
+ ioctl(fd, UI_SET_KEYBIT, KEY_PAGEUP);
+ ioctl(fd, UI_SET_KEYBIT, KEY_DOWN);
+ ioctl(fd, UI_SET_KEYBIT, KEY_PAGEDOWN);
+
+ if (ioctl(fd, UI_DEV_CREATE, NULL) < 0) {
+ err = errno;
+ error("Can't create uinput device: %s (%d)",
+ strerror(err), err);
+ close(fd);
+ errno = err;
+ return -err;
+ }
+
+ return fd;
+}
+
+static int decode_key(const char *str)
+{
+ static int mode = UPDOWN_ENABLED, gain = 0;
+
+ uint16_t key;
+ int new_gain;
+
+ /* Switch from key up/down to page up/down */
+ if (strncmp("AT+CKPD=200", str, 11) == 0) {
+ mode = ~mode;
+ return KEY_RESERVED;
+ }
+
+ if (strncmp("AT+VG", str, 5))
+ return KEY_RESERVED;
+
+ /* Gain key pressed */
+ if (strlen(str) != 10)
+ return KEY_RESERVED;
+
+ new_gain = strtol(&str[7], NULL, 10);
+ if (new_gain <= gain)
+ key = (mode == UPDOWN_ENABLED ? KEY_UP : KEY_PAGEUP);
+ else
+ key = (mode == UPDOWN_ENABLED ? KEY_DOWN : KEY_PAGEDOWN);
+
+ gain = new_gain;
+
+ return key;
+}
+
+static void send_event(int fd, uint16_t type, uint16_t code, int32_t value)
+{
+ struct uinput_event event;
+ int err;
+
+ memset(&event, 0, sizeof(event));
+ event.type = type;
+ event.code = code;
+ event.value = value;
+
+ err = write(fd, &event, sizeof(event));
+}
+
+static void send_key(int fd, uint16_t key)
+{
+ /* Key press */
+ send_event(fd, EV_KEY, key, 1);
+ send_event(fd, EV_SYN, SYN_REPORT, 0);
+ /* Key release */
+ send_event(fd, EV_KEY, key, 0);
+ send_event(fd, EV_SYN, SYN_REPORT, 0);
+}
+
+static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+ struct fake_input *fake = data;
+ const char *ok = "\r\nOK\r\n";
+ char buf[BUF_SIZE];
+ ssize_t bread = 0, bwritten;
+ uint16_t key;
+ int fd;
+
+ if (cond & G_IO_NVAL)
+ return FALSE;
+
+ if (cond & (G_IO_HUP | G_IO_ERR)) {
+ error("Hangup or error on rfcomm server socket");
+ goto failed;
+ }
+
+ fd = g_io_channel_unix_get_fd(chan);
+
+ memset(buf, 0, BUF_SIZE);
+ bread = read(fd, buf, sizeof(buf) - 1);
+ if (bread < 0) {
+ error("IO Channel read error");
+ goto failed;
+ }
+
+ DBG("Received: %s", buf);
+
+ bwritten = write(fd, ok, 6);
+ if (bwritten < 0) {
+ error("IO Channel write error");
+ goto failed;
+ }
+
+ key = decode_key(buf);
+ if (key != KEY_RESERVED)
+ send_key(fake->uinput, key);
+
+ return TRUE;
+
+failed:
+ ioctl(fake->uinput, UI_DEV_DESTROY);
+ close(fake->uinput);
+ fake->uinput = -1;
+ g_io_channel_unref(fake->io);
+
+ return FALSE;
+}
+
+static void rfcomm_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+ struct input_conn *iconn = user_data;
+ struct input_device *idev = iconn->idev;
+ struct fake_input *fake = iconn->fake;
+ DBusMessage *reply;
+
+ if (err) {
+ reply = btd_error_failed(iconn->pending_connect, err->message);
+ goto failed;
+ }
+
+ fake->rfcomm = g_io_channel_unix_get_fd(chan);
+
+ /*
+ * FIXME: Some headsets required a sco connection
+ * first to report volume gain key events
+ */
+ fake->uinput = uinput_create(idev->name);
+ if (fake->uinput < 0) {
+ g_io_channel_shutdown(chan, TRUE, NULL);
+ reply = btd_error_failed(iconn->pending_connect,
+ strerror(errno));
+ goto failed;
+ }
+
+ fake->io = g_io_channel_unix_new(fake->rfcomm);
+ g_io_channel_set_close_on_unref(fake->io, TRUE);
+ g_io_add_watch(fake->io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ (GIOFunc) rfcomm_io_cb, fake);
+
+ /* Replying to the requestor */
+ reply = dbus_message_new_method_return(iconn->pending_connect);
+ g_dbus_send_message(idev->conn, reply);
+
+ dbus_message_unref(iconn->pending_connect);
+ iconn->pending_connect = NULL;
+
+ return;
+
+failed:
+ g_dbus_send_message(idev->conn, reply);
+ dbus_message_unref(iconn->pending_connect);
+ iconn->pending_connect = NULL;
+}
+
+static gboolean rfcomm_connect(struct input_conn *iconn, GError **err)
+{
+ struct input_device *idev = iconn->idev;
+ GIOChannel *io;
+
+ io = bt_io_connect(BT_IO_RFCOMM, rfcomm_connect_cb, iconn,
+ NULL, err,
+ BT_IO_OPT_SOURCE_BDADDR, &idev->src,
+ BT_IO_OPT_DEST_BDADDR, &idev->dst,
+ BT_IO_OPT_INVALID);
+ if (!io)
+ return FALSE;
+
+ g_io_channel_unref(io);
+
+ return TRUE;
+}
+
+static gboolean intr_watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+ struct input_conn *iconn = data;
+ struct input_device *idev = iconn->idev;
+ gboolean connected = FALSE;
+
+ /* Checking for ctrl_watch avoids a double g_io_channel_shutdown since
+ * it's likely that ctrl_watch_cb has been queued for dispatching in
+ * this mainloop iteration */
+ if ((cond & (G_IO_HUP | G_IO_ERR)) && iconn->ctrl_watch)
+ g_io_channel_shutdown(chan, TRUE, NULL);
+
+ emit_property_changed(idev->conn, idev->path, INPUT_DEVICE_INTERFACE,
+ "Connected", DBUS_TYPE_BOOLEAN, &connected);
+
+ device_remove_disconnect_watch(idev->device, idev->dc_id);
+ idev->dc_id = 0;
+
+ iconn->intr_watch = 0;
+
+ g_io_channel_unref(iconn->intr_io);
+ iconn->intr_io = NULL;
+
+ /* Close control channel */
+ if (iconn->ctrl_io && !(cond & G_IO_NVAL))
+ g_io_channel_shutdown(iconn->ctrl_io, TRUE, NULL);
+
+ return FALSE;
+}
+
+static gboolean ctrl_watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+ struct input_conn *iconn = data;
+
+ /* Checking for intr_watch avoids a double g_io_channel_shutdown since
+ * it's likely that intr_watch_cb has been queued for dispatching in
+ * this mainloop iteration */
+ if ((cond & (G_IO_HUP | G_IO_ERR)) && iconn->intr_watch)
+ g_io_channel_shutdown(chan, TRUE, NULL);
+
+ iconn->ctrl_watch = 0;
+
+ g_io_channel_unref(iconn->ctrl_io);
+ iconn->ctrl_io = NULL;
+
+ /* Close interrupt channel */
+ if (iconn->intr_io && !(cond & G_IO_NVAL))
+ g_io_channel_shutdown(iconn->intr_io, TRUE, NULL);
+
+ return FALSE;
+}
+
+static gboolean fake_hid_connect(struct input_conn *iconn, GError **err)
+{
+ struct fake_hid *fhid = iconn->fake->priv;
+
+ return fhid->connect(iconn->fake, err);
+}
+
+static int fake_hid_disconnect(struct input_conn *iconn)
+{
+ struct fake_hid *fhid = iconn->fake->priv;
+
+ return fhid->disconnect(iconn->fake);
+}
+
+static void epox_endian_quirk(unsigned char *data, int size)
+{
+ /* USAGE_PAGE (Keyboard) 05 07
+ * USAGE_MINIMUM (0) 19 00
+ * USAGE_MAXIMUM (65280) 2A 00 FF <= must be FF 00
+ * LOGICAL_MINIMUM (0) 15 00
+ * LOGICAL_MAXIMUM (65280) 26 00 FF <= must be FF 00
+ */
+ unsigned char pattern[] = { 0x05, 0x07, 0x19, 0x00, 0x2a, 0x00, 0xff,
+ 0x15, 0x00, 0x26, 0x00, 0xff };
+ unsigned int i;
+
+ if (!data)
+ return;
+
+ for (i = 0; i < size - sizeof(pattern); i++) {
+ if (!memcmp(data + i, pattern, sizeof(pattern))) {
+ data[i + 5] = 0xff;
+ data[i + 6] = 0x00;
+ data[i + 10] = 0xff;
+ data[i + 11] = 0x00;
+ }
+ }
+}
+
+static void extract_hid_record(sdp_record_t *rec, struct hidp_connadd_req *req)
+{
+ sdp_data_t *pdlist, *pdlist2;
+ uint8_t attr_val;
+
+ pdlist = sdp_data_get(rec, 0x0101);
+ pdlist2 = sdp_data_get(rec, 0x0102);
+ if (pdlist) {
+ if (pdlist2) {
+ if (strncmp(pdlist->val.str, pdlist2->val.str, 5)) {
+ strncpy(req->name, pdlist2->val.str, 127);
+ strcat(req->name, " ");
+ }
+ strncat(req->name, pdlist->val.str, 127 - strlen(req->name));
+ } else
+ strncpy(req->name, pdlist->val.str, 127);
+ } else {
+ pdlist2 = sdp_data_get(rec, 0x0100);
+ if (pdlist2)
+ strncpy(req->name, pdlist2->val.str, 127);
+ }
+
+ pdlist = sdp_data_get(rec, SDP_ATTR_HID_PARSER_VERSION);
+ req->parser = pdlist ? pdlist->val.uint16 : 0x0100;
+
+ pdlist = sdp_data_get(rec, SDP_ATTR_HID_DEVICE_SUBCLASS);
+ req->subclass = pdlist ? pdlist->val.uint8 : 0;
+
+ pdlist = sdp_data_get(rec, SDP_ATTR_HID_COUNTRY_CODE);
+ req->country = pdlist ? pdlist->val.uint8 : 0;
+
+ pdlist = sdp_data_get(rec, SDP_ATTR_HID_VIRTUAL_CABLE);
+ attr_val = pdlist ? pdlist->val.uint8 : 0;
+ if (attr_val)
+ req->flags |= (1 << HIDP_VIRTUAL_CABLE_UNPLUG);
+
+ pdlist = sdp_data_get(rec, SDP_ATTR_HID_BOOT_DEVICE);
+ attr_val = pdlist ? pdlist->val.uint8 : 0;
+ if (attr_val)
+ req->flags |= (1 << HIDP_BOOT_PROTOCOL_MODE);
+
+ pdlist = sdp_data_get(rec, SDP_ATTR_HID_DESCRIPTOR_LIST);
+ if (pdlist) {
+ pdlist = pdlist->val.dataseq;
+ pdlist = pdlist->val.dataseq;
+ pdlist = pdlist->next;
+
+ req->rd_data = g_try_malloc0(pdlist->unitSize);
+ if (req->rd_data) {
+ memcpy(req->rd_data, (unsigned char *) pdlist->val.str,
+ pdlist->unitSize);
+ req->rd_size = pdlist->unitSize;
+ epox_endian_quirk(req->rd_data, req->rd_size);
+ }
+ }
+}
+
+static int ioctl_connadd(struct hidp_connadd_req *req)
+{
+ int ctl, err = 0;
+
+ ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HIDP);
+ if (ctl < 0)
+ return -errno;
+
+ if (ioctl(ctl, HIDPCONNADD, req) < 0)
+ err = errno;
+
+ close(ctl);
+
+ return -err;
+}
+
+static void encrypt_completed(uint8_t status, gpointer user_data)
+{
+ struct hidp_connadd_req *req = user_data;
+ int err;
+
+ if (status) {
+ error("Encryption failed: %s(0x%x)",
+ strerror(bt_error(status)), status);
+ goto failed;
+ }
+
+ err = ioctl_connadd(req);
+ if (err == 0)
+ goto cleanup;
+
+ error("ioctl_connadd(): %s(%d)", strerror(-err), -err);
+failed:
+ close(req->intr_sock);
+ close(req->ctrl_sock);
+
+cleanup:
+ free(req->rd_data);
+
+ g_free(req);
+}
+
+static int hidp_add_connection(const struct input_device *idev,
+ const struct input_conn *iconn)
+{
+ struct hidp_connadd_req *req;
+ struct fake_hid *fake_hid;
+ struct fake_input *fake;
+ sdp_record_t *rec;
+ char src_addr[18], dst_addr[18];
+ int err;
+
+ req = g_new0(struct hidp_connadd_req, 1);
+ req->ctrl_sock = g_io_channel_unix_get_fd(iconn->ctrl_io);
+ req->intr_sock = g_io_channel_unix_get_fd(iconn->intr_io);
+ req->flags = 0;
+ req->idle_to = iconn->timeout;
+
+ ba2str(&idev->src, src_addr);
+ ba2str(&idev->dst, dst_addr);
+
+ rec = fetch_record(src_addr, dst_addr, idev->handle);
+ if (!rec) {
+ error("Rejected connection from unknown device %s", dst_addr);
+ err = -EPERM;
+ goto cleanup;
+ }
+
+ extract_hid_record(rec, req);
+ sdp_record_free(rec);
+
+ read_device_id(src_addr, dst_addr, NULL,
+ &req->vendor, &req->product, &req->version);
+
+ fake_hid = get_fake_hid(req->vendor, req->product);
+ if (fake_hid) {
+ err = 0;
+ fake = g_new0(struct fake_input, 1);
+ fake->connect = fake_hid_connect;
+ fake->disconnect = fake_hid_disconnect;
+ fake->priv = fake_hid;
+ fake->idev = idev;
+ fake = fake_hid_connadd(fake, iconn->intr_io, fake_hid);
+ if (fake == NULL)
+ err = -ENOMEM;
+ else
+ fake->flags |= FI_FLAG_CONNECTED;
+ goto cleanup;
+ }
+
+ if (idev->name)
+ strncpy(req->name, idev->name, sizeof(req->name) - 1);
+
+ /* Encryption is mandatory for keyboards */
+ if (req->subclass & 0x40) {
+ struct btd_adapter *adapter = device_get_adapter(idev->device);
+
+ err = btd_adapter_encrypt_link(adapter, (bdaddr_t *) &idev->dst,
+ encrypt_completed, req);
+ if (err == 0) {
+ /* Waiting async encryption */
+ return 0;
+ } else if (err != -EALREADY) {
+ error("encrypt_link: %s (%d)", strerror(-err), -err);
+ goto cleanup;
+ }
+ }
+
+ err = ioctl_connadd(req);
+
+cleanup:
+ free(req->rd_data);
+ g_free(req);
+
+ return err;
+}
+
+static int is_connected(struct input_conn *iconn)
+{
+ struct input_device *idev = iconn->idev;
+ struct fake_input *fake = iconn->fake;
+ struct hidp_conninfo ci;
+ int ctl;
+
+ /* Fake input */
+ if (fake)
+ return fake->flags & FI_FLAG_CONNECTED;
+
+ /* Standard HID */
+ ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HIDP);
+ if (ctl < 0)
+ return 0;
+
+ memset(&ci, 0, sizeof(ci));
+ bacpy(&ci.bdaddr, &idev->dst);
+ if (ioctl(ctl, HIDPGETCONNINFO, &ci) < 0) {
+ close(ctl);
+ return 0;
+ }
+
+ close(ctl);
+
+ if (ci.state != BT_CONNECTED)
+ return 0;
+ else
+ return 1;
+}
+
+static int connection_disconnect(struct input_conn *iconn, uint32_t flags)
+{
+ struct input_device *idev = iconn->idev;
+ struct fake_input *fake = iconn->fake;
+ struct hidp_conndel_req req;
+ struct hidp_conninfo ci;
+ int ctl, err;
+
+ /* Fake input disconnect */
+ if (fake) {
+ err = fake->disconnect(iconn);
+ if (err == 0)
+ fake->flags &= ~FI_FLAG_CONNECTED;
+ return err;
+ }
+
+ /* Standard HID disconnect */
+ if (iconn->intr_io)
+ g_io_channel_shutdown(iconn->intr_io, TRUE, NULL);
+ if (iconn->ctrl_io)
+ g_io_channel_shutdown(iconn->ctrl_io, TRUE, NULL);
+
+ ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HIDP);
+ if (ctl < 0) {
+ error("Can't open HIDP control socket");
+ return -errno;
+ }
+
+ memset(&ci, 0, sizeof(ci));
+ bacpy(&ci.bdaddr, &idev->dst);
+ if ((ioctl(ctl, HIDPGETCONNINFO, &ci) < 0) ||
+ (ci.state != BT_CONNECTED)) {
+ errno = ENOTCONN;
+ goto fail;
+ }
+
+ memset(&req, 0, sizeof(req));
+ bacpy(&req.bdaddr, &idev->dst);
+ req.flags = flags;
+ if (ioctl(ctl, HIDPCONNDEL, &req) < 0) {
+ error("Can't delete the HID device: %s(%d)",
+ strerror(errno), errno);
+ goto fail;
+ }
+
+ close(ctl);
+
+ return 0;
+
+fail:
+ err = errno;
+ close(ctl);
+ errno = err;
+
+ return -err;
+}
+
+static int disconnect(struct input_device *idev, uint32_t flags)
+{
+ struct input_conn *iconn = NULL;
+ GSList *l;
+
+ for (l = idev->connections; l; l = l->next) {
+ iconn = l->data;
+
+ if (is_connected(iconn))
+ break;
+ }
+
+ if (!iconn)
+ return -ENOTCONN;
+
+ return connection_disconnect(iconn, flags);
+}
+
+static void disconnect_cb(struct btd_device *device, gboolean removal,
+ void *user_data)
+{
+ struct input_device *idev = user_data;
+ int flags;
+
+ info("Input: disconnect %s", idev->path);
+
+ flags = removal ? (1 << HIDP_VIRTUAL_CABLE_UNPLUG) : 0;
+
+ disconnect(idev, flags);
+}
+
+static int input_device_connected(struct input_device *idev,
+ struct input_conn *iconn)
+{
+ dbus_bool_t connected;
+ int err;
+
+ if (iconn->intr_io == NULL || iconn->ctrl_io == NULL)
+ return -ENOTCONN;
+
+ err = hidp_add_connection(idev, iconn);
+ if (err < 0)
+ return err;
+
+ iconn->intr_watch = g_io_add_watch(iconn->intr_io,
+ G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ intr_watch_cb, iconn);
+ iconn->ctrl_watch = g_io_add_watch(iconn->ctrl_io,
+ G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ ctrl_watch_cb, iconn);
+
+ connected = TRUE;
+ emit_property_changed(idev->conn, idev->path, INPUT_DEVICE_INTERFACE,
+ "Connected", DBUS_TYPE_BOOLEAN, &connected);
+
+ idev->dc_id = device_add_disconnect_watch(idev->device, disconnect_cb,
+ idev, NULL);
+
+ return 0;
+}
+
+static void interrupt_connect_cb(GIOChannel *chan, GError *conn_err,
+ gpointer user_data)
+{
+ struct input_conn *iconn = user_data;
+ struct input_device *idev = iconn->idev;
+ DBusMessage *reply;
+ int err;
+ const char *err_msg;
+
+ if (conn_err) {
+ err_msg = conn_err->message;
+ goto failed;
+ }
+
+ err = input_device_connected(idev, iconn);
+ if (err < 0) {
+ err_msg = strerror(-err);
+ goto failed;
+ }
+
+ /* Replying to the requestor */
+ g_dbus_send_reply(idev->conn, iconn->pending_connect, DBUS_TYPE_INVALID);
+
+ dbus_message_unref(iconn->pending_connect);
+ iconn->pending_connect = NULL;
+
+ return;
+
+failed:
+ error("%s", err_msg);
+ reply = btd_error_failed(iconn->pending_connect, err_msg);
+ g_dbus_send_message(idev->conn, reply);
+
+ /* So we guarantee the interrupt channel is closed before the
+ * control channel (if we only do unref GLib will close it only
+ * after returning control to the mainloop */
+ if (!conn_err)
+ g_io_channel_shutdown(iconn->intr_io, FALSE, NULL);
+
+ g_io_channel_unref(iconn->intr_io);
+ iconn->intr_io = NULL;
+
+ if (iconn->ctrl_io) {
+ g_io_channel_unref(iconn->ctrl_io);
+ iconn->ctrl_io = NULL;
+ }
+}
+
+static void control_connect_cb(GIOChannel *chan, GError *conn_err,
+ gpointer user_data)
+{
+ struct input_conn *iconn = user_data;
+ struct input_device *idev = iconn->idev;
+ DBusMessage *reply;
+ GIOChannel *io;
+ GError *err = NULL;
+
+ if (conn_err) {
+ error("%s", conn_err->message);
+ reply = btd_error_failed(iconn->pending_connect,
+ conn_err->message);
+ goto failed;
+ }
+
+ /* Connect to the HID interrupt channel */
+ io = bt_io_connect(BT_IO_L2CAP, interrupt_connect_cb, iconn,
+ NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &idev->src,
+ BT_IO_OPT_DEST_BDADDR, &idev->dst,
+ BT_IO_OPT_PSM, L2CAP_PSM_HIDP_INTR,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_INVALID);
+ if (!io) {
+ error("%s", err->message);
+ reply = btd_error_failed(iconn->pending_connect,
+ err->message);
+ g_error_free(err);
+ goto failed;
+ }
+
+ iconn->intr_io = io;
+
+ return;
+
+failed:
+ g_io_channel_unref(iconn->ctrl_io);
+ iconn->ctrl_io = NULL;
+ g_dbus_send_message(idev->conn, reply);
+ dbus_message_unref(iconn->pending_connect);
+ iconn->pending_connect = NULL;
+}
+
+static int fake_disconnect(struct input_conn *iconn)
+{
+ struct fake_input *fake = iconn->fake;
+
+ if (!fake->io)
+ return -ENOTCONN;
+
+ g_io_channel_shutdown(fake->io, TRUE, NULL);
+ g_io_channel_unref(fake->io);
+ fake->io = NULL;
+
+ if (fake->uinput >= 0) {
+ ioctl(fake->uinput, UI_DEV_DESTROY);
+ close(fake->uinput);
+ fake->uinput = -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Input Device methods
+ */
+static DBusMessage *input_device_connect(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct input_device *idev = data;
+ struct input_conn *iconn;
+ struct fake_input *fake;
+ DBusMessage *reply;
+ GError *err = NULL;
+
+ iconn = find_connection(idev->connections, "HID");
+ if (!iconn)
+ return btd_error_not_supported(msg);
+
+ if (iconn->pending_connect)
+ return btd_error_in_progress(msg);
+
+ if (is_connected(iconn))
+ return btd_error_already_connected(msg);
+
+ iconn->pending_connect = dbus_message_ref(msg);
+ fake = iconn->fake;
+
+ if (fake) {
+ /* Fake input device */
+ if (fake->connect(iconn, &err))
+ fake->flags |= FI_FLAG_CONNECTED;
+ } else {
+ /* HID devices */
+ GIOChannel *io;
+
+ io = bt_io_connect(BT_IO_L2CAP, control_connect_cb, iconn,
+ NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &idev->src,
+ BT_IO_OPT_DEST_BDADDR, &idev->dst,
+ BT_IO_OPT_PSM, L2CAP_PSM_HIDP_CTRL,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_INVALID);
+ iconn->ctrl_io = io;
+ }
+
+ if (err == NULL)
+ return NULL;
+
+ error("%s", err->message);
+ dbus_message_unref(iconn->pending_connect);
+ iconn->pending_connect = NULL;
+ reply = btd_error_failed(msg, err->message);
+ g_error_free(err);
+ return reply;
+}
+
+static DBusMessage *input_device_disconnect(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct input_device *idev = data;
+ int err;
+
+ err = disconnect(idev, 0);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static void device_unregister(void *data)
+{
+ struct input_device *idev = data;
+
+ DBG("Unregistered interface %s on path %s", INPUT_DEVICE_INTERFACE,
+ idev->path);
+
+ devices = g_slist_remove(devices, idev);
+ input_device_free(idev);
+}
+
+static gint connected_cmp(gpointer a, gpointer b)
+{
+ struct input_conn *iconn = a;
+
+ return !is_connected(iconn);
+}
+
+static DBusMessage *input_device_get_properties(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct input_device *idev = data;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ dbus_bool_t connected;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ /* Connected */
+ connected = !!g_slist_find_custom(idev->connections, NULL,
+ (GCompareFunc) connected_cmp);
+ dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &connected);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static GDBusMethodTable device_methods[] = {
+ { "Connect", "", "", input_device_connect,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "Disconnect", "", "", input_device_disconnect },
+ { "GetProperties", "", "a{sv}",input_device_get_properties },
+ { }
+};
+
+static GDBusSignalTable device_signals[] = {
+ { "PropertyChanged", "sv" },
+ { }
+};
+
+static struct input_device *input_device_new(DBusConnection *conn,
+ struct btd_device *device, const char *path,
+ const bdaddr_t *src, const bdaddr_t *dst,
+ const uint32_t handle)
+{
+ struct input_device *idev;
+ char name[249], src_addr[18], dst_addr[18];
+
+ idev = g_new0(struct input_device, 1);
+ bacpy(&idev->src, src);
+ bacpy(&idev->dst, dst);
+ idev->device = btd_device_ref(device);
+ idev->path = g_strdup(path);
+ idev->conn = dbus_connection_ref(conn);
+ idev->handle = handle;
+
+ ba2str(src, src_addr);
+ ba2str(dst, dst_addr);
+ if (read_device_name(src_addr, dst_addr, name) == 0)
+ idev->name = g_strdup(name);
+
+ if (g_dbus_register_interface(conn, idev->path, INPUT_DEVICE_INTERFACE,
+ device_methods, device_signals, NULL,
+ idev, device_unregister) == FALSE) {
+ error("Failed to register interface %s on path %s",
+ INPUT_DEVICE_INTERFACE, path);
+ input_device_free(idev);
+ return NULL;
+ }
+
+ DBG("Registered interface %s on path %s",
+ INPUT_DEVICE_INTERFACE, idev->path);
+
+ return idev;
+}
+
+static struct input_conn *input_conn_new(struct input_device *idev,
+ const char *uuid, const char *alias,
+ int timeout)
+{
+ struct input_conn *iconn;
+
+ iconn = g_new0(struct input_conn, 1);
+ iconn->timeout = timeout;
+ iconn->uuid = g_strdup(uuid);
+ iconn->alias = g_strdup(alias);
+ iconn->idev = idev;
+
+ return iconn;
+}
+
+int input_device_register(DBusConnection *conn, struct btd_device *device,
+ const char *path, const bdaddr_t *src,
+ const bdaddr_t *dst, const char *uuid,
+ uint32_t handle, int timeout)
+{
+ struct input_device *idev;
+ struct input_conn *iconn;
+
+ idev = find_device_by_path(devices, path);
+ if (!idev) {
+ idev = input_device_new(conn, device, path, src, dst, handle);
+ if (!idev)
+ return -EINVAL;
+ devices = g_slist_append(devices, idev);
+ }
+
+ iconn = input_conn_new(idev, uuid, "hid", timeout);
+ if (!iconn)
+ return -EINVAL;
+
+ idev->connections = g_slist_append(idev->connections, iconn);
+
+ return 0;
+}
+
+int fake_input_register(DBusConnection *conn, struct btd_device *device,
+ const char *path, bdaddr_t *src, bdaddr_t *dst,
+ const char *uuid, uint8_t channel)
+{
+ struct input_device *idev;
+ struct input_conn *iconn;
+
+ idev = find_device_by_path(devices, path);
+ if (!idev) {
+ idev = input_device_new(conn, device, path, src, dst, 0);
+ if (!idev)
+ return -EINVAL;
+ devices = g_slist_append(devices, idev);
+ }
+
+ iconn = input_conn_new(idev, uuid, "hsp", 0);
+ if (!iconn)
+ return -EINVAL;
+
+ iconn->fake = g_new0(struct fake_input, 1);
+ iconn->fake->ch = channel;
+ iconn->fake->connect = rfcomm_connect;
+ iconn->fake->disconnect = fake_disconnect;
+
+ idev->connections = g_slist_append(idev->connections, iconn);
+
+ return 0;
+}
+
+static struct input_device *find_device(const bdaddr_t *src,
+ const bdaddr_t *dst)
+{
+ GSList *list;
+
+ for (list = devices; list != NULL; list = list->next) {
+ struct input_device *idev = list->data;
+
+ if (!bacmp(&idev->src, src) && !bacmp(&idev->dst, dst))
+ return idev;
+ }
+
+ return NULL;
+}
+
+int input_device_unregister(const char *path, const char *uuid)
+{
+ struct input_device *idev;
+ struct input_conn *iconn;
+
+ idev = find_device_by_path(devices, path);
+ if (idev == NULL)
+ return -EINVAL;
+
+ iconn = find_connection(idev->connections, uuid);
+ if (iconn == NULL)
+ return -EINVAL;
+
+ if (iconn->pending_connect) {
+ /* Pending connection running */
+ return -EBUSY;
+ }
+
+ idev->connections = g_slist_remove(idev->connections, iconn);
+ input_conn_free(iconn);
+ if (idev->connections)
+ return 0;
+
+ g_dbus_unregister_interface(idev->conn, path, INPUT_DEVICE_INTERFACE);
+
+ return 0;
+}
+
+static int input_device_connadd(struct input_device *idev,
+ struct input_conn *iconn)
+{
+ int err;
+
+ err = input_device_connected(idev, iconn);
+ if (err < 0)
+ goto error;
+
+ return 0;
+
+error:
+ if (iconn->ctrl_io) {
+ g_io_channel_shutdown(iconn->ctrl_io, FALSE, NULL);
+ g_io_channel_unref(iconn->ctrl_io);
+ iconn->ctrl_io = NULL;
+ }
+ if (iconn->intr_io) {
+ g_io_channel_shutdown(iconn->intr_io, FALSE, NULL);
+ g_io_channel_unref(iconn->intr_io);
+ iconn->intr_io = NULL;
+ }
+
+ return err;
+}
+
+int input_device_set_channel(const bdaddr_t *src, const bdaddr_t *dst, int psm,
+ GIOChannel *io)
+{
+ struct input_device *idev = find_device(src, dst);
+ struct input_conn *iconn;
+
+ if (!idev)
+ return -ENOENT;
+
+ iconn = find_connection(idev->connections, "hid");
+ if (!iconn)
+ return -ENOENT;
+
+ switch (psm) {
+ case L2CAP_PSM_HIDP_CTRL:
+ if (iconn->ctrl_io)
+ return -EALREADY;
+ iconn->ctrl_io = g_io_channel_ref(io);
+ break;
+ case L2CAP_PSM_HIDP_INTR:
+ if (iconn->intr_io)
+ return -EALREADY;
+ iconn->intr_io = g_io_channel_ref(io);
+ break;
+ }
+
+ if (iconn->intr_io && iconn->ctrl_io)
+ input_device_connadd(idev, iconn);
+
+ return 0;
+}
+
+int input_device_close_channels(const bdaddr_t *src, const bdaddr_t *dst)
+{
+ struct input_device *idev = find_device(src, dst);
+ struct input_conn *iconn;
+
+ if (!idev)
+ return -ENOENT;
+
+ iconn = find_connection(idev->connections, "hid");
+ if (!iconn)
+ return -ENOENT;
+
+ if (iconn->intr_io)
+ g_io_channel_shutdown(iconn->intr_io, TRUE, NULL);
+
+ if (iconn->ctrl_io)
+ g_io_channel_shutdown(iconn->ctrl_io, TRUE, NULL);
+
+ return 0;
+}
diff --git a/input/device.h b/input/device.h
new file mode 100644
index 0000000..14c0f97
--- /dev/null
+++ b/input/device.h
@@ -0,0 +1,56 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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
+ *
+ */
+
+#define HSP_HS_UUID "00001108-0000-1000-8000-00805F9B34FB"
+#define HID_UUID "00001124-0000-1000-8000-00805f9b34fb"
+
+#define L2CAP_PSM_HIDP_CTRL 0x11
+#define L2CAP_PSM_HIDP_INTR 0x13
+
+struct input_device;
+struct input_conn;
+
+struct fake_input {
+ int flags;
+ GIOChannel *io;
+ int uinput; /* uinput socket */
+ int rfcomm; /* RFCOMM socket */
+ uint8_t ch; /* RFCOMM channel number */
+ gboolean (*connect) (struct input_conn *iconn, GError **err);
+ int (*disconnect) (struct input_conn *iconn);
+ void *priv;
+ const struct input_device *idev;
+};
+
+int fake_input_register(DBusConnection *conn, struct btd_device *device,
+ const char *path, bdaddr_t *src, bdaddr_t *dst,
+ const char *uuid, uint8_t channel);
+int input_device_register(DBusConnection *conn, struct btd_device *device,
+ const char *path, const bdaddr_t *src,
+ const bdaddr_t *dst, const char *uuid,
+ uint32_t handle, int timeout);
+int input_device_unregister(const char *path, const char *uuid);
+
+int input_device_set_channel(const bdaddr_t *src, const bdaddr_t *dst, int psm,
+ GIOChannel *io);
+int input_device_close_channels(const bdaddr_t *src, const bdaddr_t *dst);
diff --git a/input/fakehid.c b/input/fakehid.c
new file mode 100644
index 0000000..eebca05
--- /dev/null
+++ b/input/fakehid.c
@@ -0,0 +1,411 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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 <sys/types.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hidp.h>
+#include <bluetooth/sdp.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "../src/adapter.h"
+#include "../src/device.h"
+
+#include "log.h"
+#include "device.h"
+#include "fakehid.h"
+#include "uinput.h"
+
+#define PS3_FLAGS_MASK 0xFFFFFF00
+
+enum ps3remote_special_keys {
+ PS3R_BIT_PS = 0,
+ PS3R_BIT_ENTER = 3,
+ PS3R_BIT_L2 = 8,
+ PS3R_BIT_R2 = 9,
+ PS3R_BIT_L1 = 10,
+ PS3R_BIT_R1 = 11,
+ PS3R_BIT_TRIANGLE = 12,
+ PS3R_BIT_CIRCLE = 13,
+ PS3R_BIT_CROSS = 14,
+ PS3R_BIT_SQUARE = 15,
+ PS3R_BIT_SELECT = 16,
+ PS3R_BIT_L3 = 17,
+ PS3R_BIT_R3 = 18,
+ PS3R_BIT_START = 19,
+ PS3R_BIT_UP = 20,
+ PS3R_BIT_RIGHT = 21,
+ PS3R_BIT_DOWN = 22,
+ PS3R_BIT_LEFT = 23,
+};
+
+static unsigned int ps3remote_bits[] = {
+ [PS3R_BIT_ENTER] = 0x0b,
+ [PS3R_BIT_PS] = 0x43,
+ [PS3R_BIT_SQUARE] = 0x5f,
+ [PS3R_BIT_CROSS] = 0x5e,
+ [PS3R_BIT_CIRCLE] = 0x5d,
+ [PS3R_BIT_TRIANGLE] = 0x5c,
+ [PS3R_BIT_R1] = 0x5b,
+ [PS3R_BIT_L1] = 0x5a,
+ [PS3R_BIT_R2] = 0x59,
+ [PS3R_BIT_L2] = 0x58,
+ [PS3R_BIT_LEFT] = 0x57,
+ [PS3R_BIT_DOWN] = 0x56,
+ [PS3R_BIT_RIGHT] = 0x55,
+ [PS3R_BIT_UP] = 0x54,
+ [PS3R_BIT_START] = 0x53,
+ [PS3R_BIT_R3] = 0x52,
+ [PS3R_BIT_L3] = 0x51,
+ [PS3R_BIT_SELECT] = 0x50,
+};
+
+static unsigned int ps3remote_keymap[] = {
+ [0x16] = KEY_EJECTCD,
+ [0x64] = KEY_AUDIO,
+ [0x65] = KEY_ANGLE,
+ [0x63] = KEY_SUBTITLE,
+ [0x0f] = KEY_CLEAR,
+ [0x28] = KEY_TIME,
+ [0x00] = KEY_1,
+ [0x01] = KEY_2,
+ [0x02] = KEY_3,
+ [0x03] = KEY_4,
+ [0x04] = KEY_5,
+ [0x05] = KEY_6,
+ [0x06] = KEY_7,
+ [0x07] = KEY_8,
+ [0x08] = KEY_9,
+ [0x09] = KEY_0,
+ [0x81] = KEY_RED,
+ [0x82] = KEY_GREEN,
+ [0x80] = KEY_BLUE,
+ [0x83] = KEY_YELLOW,
+ [0x70] = KEY_INFO, /* display */
+ [0x1a] = KEY_MENU, /* top menu */
+ [0x40] = KEY_CONTEXT_MENU, /* pop up/menu */
+ [0x0e] = KEY_ESC, /* return */
+ [0x5c] = KEY_OPTION, /* options/triangle */
+ [0x5d] = KEY_BACK, /* back/circle */
+ [0x5f] = KEY_SCREEN, /* view/square */
+ [0x5e] = BTN_0, /* cross */
+ [0x54] = KEY_UP,
+ [0x56] = KEY_DOWN,
+ [0x57] = KEY_LEFT,
+ [0x55] = KEY_RIGHT,
+ [0x0b] = KEY_ENTER,
+ [0x5a] = BTN_TL, /* L1 */
+ [0x58] = BTN_TL2, /* L2 */
+ [0x51] = BTN_THUMBL, /* L3 */
+ [0x5b] = BTN_TR, /* R1 */
+ [0x59] = BTN_TR2, /* R2 */
+ [0x52] = BTN_THUMBR, /* R3 */
+ [0x43] = KEY_HOMEPAGE, /* PS button */
+ [0x50] = KEY_SELECT,
+ [0x53] = BTN_START,
+ [0x33] = KEY_REWIND, /* scan back */
+ [0x32] = KEY_PLAY,
+ [0x34] = KEY_FORWARD, /* scan forward */
+ [0x30] = KEY_PREVIOUS,
+ [0x38] = KEY_STOP,
+ [0x31] = KEY_NEXT,
+ [0x60] = KEY_FRAMEBACK, /* slow/step back */
+ [0x39] = KEY_PAUSE,
+ [0x61] = KEY_FRAMEFORWARD, /* slow/step forward */
+ [0xff] = KEY_MAX,
+};
+
+static int ps3remote_decode(char *buff, int size, unsigned int *value)
+{
+ static unsigned int lastkey = 0;
+ static unsigned int lastmask = 0;
+ unsigned int i, mask;
+ int retval;
+ guint8 key;
+
+ if (size < 12) {
+ error("Got a shorter packet! (size %i)\n", size);
+ return KEY_RESERVED;
+ }
+
+ mask = (buff[2] << 16) + (buff[3] << 8) + buff[4];
+ key = buff[5];
+
+ /* first, check flags */
+ for (i = 0; i < 24; i++) {
+ if ((lastmask & (1 << i)) == (mask & (1 << i)))
+ continue;
+ if (ps3remote_bits[i] == 0)
+ goto error;
+ retval = ps3remote_keymap[ps3remote_bits[i]];
+ if (mask & (1 << i))
+ /* key pressed */
+ *value = 1;
+ else
+ /* key released */
+ *value = 0;
+
+ goto out;
+ }
+
+ *value = buff[11];
+ if (buff[11] == 1) {
+ retval = ps3remote_keymap[key];
+ } else
+ retval = lastkey;
+
+ if (retval == KEY_RESERVED)
+ goto error;
+ if (retval == KEY_MAX)
+ return retval;
+
+ lastkey = retval;
+
+out:
+ fflush(stdout);
+
+ lastmask = mask;
+
+ return retval;
+
+error:
+ error("ps3remote: unrecognized sequence [%#x][%#x][%#x][%#x] [%#x],"
+ "last: [%#x][%#x][%#x][%#x]",
+ buff[2], buff[3], buff[4], buff[5], buff[11],
+ lastmask >> 16, lastmask >> 8 & 0xff,
+ lastmask & 0xff, lastkey);
+ return -1;
+}
+
+static gboolean ps3remote_event(GIOChannel *chan, GIOCondition cond,
+ gpointer data)
+{
+ struct fake_input *fake = data;
+ struct uinput_event event;
+ unsigned int key, value = 0;
+ ssize_t size;
+ char buff[50];
+ int fd;
+
+ if (cond & G_IO_NVAL)
+ return FALSE;
+
+ if (cond & (G_IO_HUP | G_IO_ERR)) {
+ error("Hangup or error on rfcomm server socket");
+ goto failed;
+ }
+
+ fd = g_io_channel_unix_get_fd(chan);
+
+ memset(buff, 0, sizeof(buff));
+ size = read(fd, buff, sizeof(buff));
+ if (size < 0) {
+ error("IO Channel read error");
+ goto failed;
+ }
+
+ key = ps3remote_decode(buff, size, &value);
+ if (key == KEY_RESERVED) {
+ error("Got invalid key from decode");
+ goto failed;
+ } else if (key == KEY_MAX)
+ return TRUE;
+
+ memset(&event, 0, sizeof(event));
+ gettimeofday(&event.time, NULL);
+ event.type = EV_KEY;
+ event.code = key;
+ event.value = value;
+ if (write(fake->uinput, &event, sizeof(event)) != sizeof(event)) {
+ error("Error writing to uinput device");
+ goto failed;
+ }
+
+ memset(&event, 0, sizeof(event));
+ gettimeofday(&event.time, NULL);
+ event.type = EV_SYN;
+ event.code = SYN_REPORT;
+ if (write(fake->uinput, &event, sizeof(event)) != sizeof(event)) {
+ error("Error writing to uinput device");
+ goto failed;
+ }
+
+ return TRUE;
+
+failed:
+ ioctl(fake->uinput, UI_DEV_DESTROY);
+ close(fake->uinput);
+ fake->uinput = -1;
+ g_io_channel_unref(fake->io);
+
+ return FALSE;
+}
+
+static int ps3remote_setup_uinput(struct fake_input *fake,
+ struct fake_hid *fake_hid)
+{
+ struct uinput_dev dev;
+ int i;
+
+ fake->uinput = open("/dev/input/uinput", O_RDWR);
+ if (fake->uinput < 0) {
+ fake->uinput = open("/dev/uinput", O_RDWR);
+ if (fake->uinput < 0) {
+ fake->uinput = open("/dev/misc/uinput", O_RDWR);
+ if (fake->uinput < 0) {
+ error("Error opening uinput device file");
+ return 1;
+ }
+ }
+ }
+
+ memset(&dev, 0, sizeof(dev));
+ snprintf(dev.name, sizeof(dev.name), "%s", "PS3 Remote Controller");
+ dev.id.bustype = BUS_BLUETOOTH;
+ dev.id.vendor = fake_hid->vendor;
+ dev.id.product = fake_hid->product;
+
+ if (write(fake->uinput, &dev, sizeof(dev)) != sizeof(dev)) {
+ error("Error creating uinput device");
+ goto err;
+ }
+
+ /* enabling key events */
+ if (ioctl(fake->uinput, UI_SET_EVBIT, EV_KEY) < 0) {
+ error("Error enabling uinput device key events");
+ goto err;
+ }
+
+ /* enabling keys */
+ for (i = 0; i < 256; i++)
+ if (ps3remote_keymap[i] != KEY_RESERVED)
+ if (ioctl(fake->uinput, UI_SET_KEYBIT,
+ ps3remote_keymap[i]) < 0) {
+ error("Error enabling uinput key %i",
+ ps3remote_keymap[i]);
+ goto err;
+ }
+
+ /* creating the device */
+ if (ioctl(fake->uinput, UI_DEV_CREATE) < 0) {
+ error("Error creating uinput device");
+ goto err;
+ }
+
+ return 0;
+
+err:
+ close(fake->uinput);
+ return 1;
+}
+
+static gboolean fake_hid_common_connect(struct fake_input *fake, GError **err)
+{
+ return TRUE;
+}
+
+static int fake_hid_common_disconnect(struct fake_input *fake)
+{
+ return 0;
+}
+
+static struct fake_hid fake_hid_table[] = {
+ /* Sony PS3 remote device */
+ {
+ .vendor = 0x054c,
+ .product = 0x0306,
+ .connect = fake_hid_common_connect,
+ .disconnect = fake_hid_common_disconnect,
+ .event = ps3remote_event,
+ .setup_uinput = ps3remote_setup_uinput,
+ .devices = NULL,
+ },
+
+ { },
+};
+
+static inline int fake_hid_match_device(uint16_t vendor, uint16_t product,
+ struct fake_hid *fhid)
+{
+ return vendor == fhid->vendor && product == fhid->product;
+}
+
+struct fake_hid *get_fake_hid(uint16_t vendor, uint16_t product)
+{
+ int i;
+
+ for (i = 0; fake_hid_table[i].vendor != 0; i++)
+ if (fake_hid_match_device(vendor, product, &fake_hid_table[i]))
+ return &fake_hid_table[i];
+
+ return NULL;
+}
+
+struct fake_input *fake_hid_connadd(struct fake_input *fake,
+ GIOChannel *intr_io,
+ struct fake_hid *fake_hid)
+{
+ GList *l;
+ struct fake_input *old = NULL;
+
+ /* Look for an already setup device */
+ for (l = fake_hid->devices; l != NULL; l = l->next) {
+ old = l->data;
+ if (old->idev == fake->idev) {
+ g_free(fake);
+ fake = old;
+ fake_hid->connect(fake, NULL);
+ break;
+ }
+ old = NULL;
+ }
+
+ /* New device? Add it to the list of known devices,
+ * and create the uinput necessary */
+ if (old == NULL) {
+ if (fake_hid->setup_uinput(fake, fake_hid)) {
+ error("Error setting up uinput");
+ g_free(fake);
+ return NULL;
+ }
+ fake_hid->devices = g_list_append(fake_hid->devices, fake);
+ }
+
+ fake->io = g_io_channel_ref(intr_io);
+ g_io_channel_set_close_on_unref(fake->io, TRUE);
+ g_io_add_watch(fake->io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ (GIOFunc) fake_hid->event, fake);
+
+ return fake;
+}
diff --git a/input/fakehid.h b/input/fakehid.h
new file mode 100644
index 0000000..3a5d7c4
--- /dev/null
+++ b/input/fakehid.h
@@ -0,0 +1,40 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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
+ *
+ */
+
+struct fake_hid;
+struct fake_input;
+
+struct fake_hid {
+ uint16_t vendor;
+ uint16_t product;
+ gboolean (*connect) (struct fake_input *fake_input, GError **err);
+ int (*disconnect) (struct fake_input *fake_input);
+ gboolean (*event) (GIOChannel *chan, GIOCondition cond, gpointer data);
+ int (*setup_uinput) (struct fake_input *fake, struct fake_hid *fake_hid);
+ GList *devices;
+};
+
+struct fake_hid *get_fake_hid(uint16_t vendor, uint16_t product);
+
+struct fake_input *fake_hid_connadd(struct fake_input *fake, GIOChannel *intr_io,
+ struct fake_hid *fake_hid);
diff --git a/input/input.conf b/input/input.conf
new file mode 100644
index 0000000..abfb64f
--- /dev/null
+++ b/input/input.conf
@@ -0,0 +1,9 @@
+# Configuration file for the input service
+
+# This section contains options which are not specific to any
+# particular interface
+[General]
+
+# Set idle timeout (in minutes) before the connection will
+# be disconnect (defaults to 0 for no timeout)
+#IdleTimeout=30
diff --git a/input/main.c b/input/main.c
new file mode 100644
index 0000000..e165ab4
--- /dev/null
+++ b/input/main.c
@@ -0,0 +1,86 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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 <errno.h>
+
+#include <bluetooth/bluetooth.h>
+
+#include <gdbus.h>
+
+#include "plugin.h"
+#include "log.h"
+#include "manager.h"
+
+static GKeyFile *load_config_file(const char *file)
+{
+ GKeyFile *keyfile;
+ GError *err = NULL;
+
+ keyfile = g_key_file_new();
+
+ if (!g_key_file_load_from_file(keyfile, file, 0, &err)) {
+ error("Parsing %s failed: %s", file, err->message);
+ g_error_free(err);
+ g_key_file_free(keyfile);
+ return NULL;
+ }
+
+ return keyfile;
+}
+
+static DBusConnection *connection;
+
+static int input_init(void)
+{
+ GKeyFile *config;
+
+ connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+ if (connection == NULL)
+ return -EIO;
+
+ config = load_config_file(CONFIGDIR "/input.conf");
+
+ if (input_manager_init(connection, config) < 0) {
+ dbus_connection_unref(connection);
+ return -EIO;
+ }
+
+ if (config)
+ g_key_file_free(config);
+
+ return 0;
+}
+
+static void input_exit(void)
+{
+ input_manager_exit();
+
+ dbus_connection_unref(connection);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(input, VERSION,
+ BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, input_init, input_exit)
diff --git a/input/manager.c b/input/manager.c
new file mode 100644
index 0000000..a98a080
--- /dev/null
+++ b/input/manager.c
@@ -0,0 +1,207 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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 <errno.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <gdbus.h>
+
+#include "log.h"
+#include "../src/adapter.h"
+#include "../src/device.h"
+
+#include "device.h"
+#include "server.h"
+#include "manager.h"
+
+static int idle_timeout = 0;
+
+static DBusConnection *connection = NULL;
+static GSList *adapters = NULL;
+
+static void input_remove(struct btd_device *device, const char *uuid)
+{
+ const gchar *path = device_get_path(device);
+
+ DBG("path %s", path);
+
+ input_device_unregister(path, uuid);
+}
+
+static int hid_device_probe(struct btd_device *device, GSList *uuids)
+{
+ struct btd_adapter *adapter = device_get_adapter(device);
+ const gchar *path = device_get_path(device);
+ const sdp_record_t *rec = btd_device_get_record(device, uuids->data);
+ bdaddr_t src, dst;
+
+ DBG("path %s", path);
+
+ if (!rec)
+ return -1;
+
+ adapter_get_address(adapter, &src);
+ device_get_address(device, &dst);
+
+ return input_device_register(connection, device, path, &src, &dst,
+ HID_UUID, rec->handle, idle_timeout * 60);
+}
+
+static void hid_device_remove(struct btd_device *device)
+{
+ input_remove(device, HID_UUID);
+}
+
+static int headset_probe(struct btd_device *device, GSList *uuids)
+{
+ struct btd_adapter *adapter = device_get_adapter(device);
+ const gchar *path = device_get_path(device);
+ const sdp_record_t *record;
+ sdp_list_t *protos;
+ uint8_t ch;
+ bdaddr_t src, dst;
+
+ DBG("path %s", path);
+
+ if (!g_slist_find_custom(uuids, HSP_HS_UUID,
+ (GCompareFunc) strcasecmp))
+ return -EINVAL;
+
+ record = btd_device_get_record(device, uuids->data);
+
+ if (!record || sdp_get_access_protos(record, &protos) < 0) {
+ error("Invalid record");
+ return -EINVAL;
+ }
+
+ ch = sdp_get_proto_port(protos, RFCOMM_UUID);
+ sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL);
+ sdp_list_free(protos, NULL);
+
+ if (ch <= 0) {
+ error("Invalid RFCOMM channel");
+ return -EINVAL;
+ }
+
+ adapter_get_address(adapter, &src);
+ device_get_address(device, &dst);
+
+ return fake_input_register(connection, device, path, &src, &dst,
+ HSP_HS_UUID, ch);
+}
+
+static void headset_remove(struct btd_device *device)
+{
+ input_remove(device, HSP_HS_UUID);
+}
+
+static int hid_server_probe(struct btd_adapter *adapter)
+{
+ bdaddr_t src;
+ int ret;
+
+ adapter_get_address(adapter, &src);
+
+ ret = server_start(&src);
+ if (ret < 0)
+ return ret;
+
+ adapters = g_slist_append(adapters, btd_adapter_ref(adapter));
+
+ return 0;
+}
+
+static void hid_server_remove(struct btd_adapter *adapter)
+{
+ bdaddr_t src;
+
+ adapter_get_address(adapter, &src);
+
+ server_stop(&src);
+
+ adapters = g_slist_remove(adapters, adapter);
+ btd_adapter_unref(adapter);
+}
+
+static struct btd_device_driver input_hid_driver = {
+ .name = "input-hid",
+ .uuids = BTD_UUIDS(HID_UUID),
+ .probe = hid_device_probe,
+ .remove = hid_device_remove,
+};
+
+static struct btd_device_driver input_headset_driver = {
+ .name = "input-headset",
+ .uuids = BTD_UUIDS(HSP_HS_UUID),
+ .probe = headset_probe,
+ .remove = headset_remove,
+};
+
+static struct btd_adapter_driver input_server_driver = {
+ .name = "input-server",
+ .probe = hid_server_probe,
+ .remove = hid_server_remove,
+};
+
+int input_manager_init(DBusConnection *conn, GKeyFile *config)
+{
+ GError *err = NULL;
+
+ if (config) {
+ idle_timeout = g_key_file_get_integer(config, "General",
+ "IdleTimeout", &err);
+ if (err) {
+ DBG("input.conf: %s", err->message);
+ g_error_free(err);
+ }
+ }
+
+ connection = dbus_connection_ref(conn);
+
+ btd_register_adapter_driver(&input_server_driver);
+
+ btd_register_device_driver(&input_hid_driver);
+ btd_register_device_driver(&input_headset_driver);
+
+ return 0;
+}
+
+void input_manager_exit(void)
+{
+ btd_unregister_device_driver(&input_hid_driver);
+ btd_unregister_device_driver(&input_headset_driver);
+
+ btd_unregister_adapter_driver(&input_server_driver);
+
+ dbus_connection_unref(connection);
+
+ connection = NULL;
+}
diff --git a/input/manager.h b/input/manager.h
new file mode 100644
index 0000000..7b93c5b
--- /dev/null
+++ b/input/manager.h
@@ -0,0 +1,25 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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
+ *
+ */
+
+int input_manager_init(DBusConnection *conn, GKeyFile *config);
+void input_manager_exit(void);
diff --git a/input/server.c b/input/server.c
new file mode 100644
index 0000000..d98018b
--- /dev/null
+++ b/input/server.c
@@ -0,0 +1,237 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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 <unistd.h>
+#include <errno.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "log.h"
+
+#include "glib-helper.h"
+#include "btio.h"
+#include "adapter.h"
+#include "device.h"
+#include "server.h"
+
+static GSList *servers = NULL;
+struct input_server {
+ bdaddr_t src;
+ GIOChannel *ctrl;
+ GIOChannel *intr;
+ GIOChannel *confirm;
+};
+
+static gint server_cmp(gconstpointer s, gconstpointer user_data)
+{
+ const struct input_server *server = s;
+ const bdaddr_t *src = user_data;
+
+ return bacmp(&server->src, src);
+}
+
+static void connect_event_cb(GIOChannel *chan, GError *err, gpointer data)
+{
+ uint16_t psm;
+ bdaddr_t src, dst;
+ GError *gerr = NULL;
+ int ret;
+
+ if (err) {
+ error("%s", err->message);
+ return;
+ }
+
+ bt_io_get(chan, BT_IO_L2CAP, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, &src,
+ BT_IO_OPT_DEST_BDADDR, &dst,
+ BT_IO_OPT_PSM, &psm,
+ BT_IO_OPT_INVALID);
+ if (gerr) {
+ error("%s", gerr->message);
+ g_error_free(gerr);
+ g_io_channel_shutdown(chan, TRUE, NULL);
+ return;
+ }
+
+ DBG("Incoming connection on PSM %d", psm);
+
+ ret = input_device_set_channel(&src, &dst, psm, chan);
+ if (ret == 0)
+ return;
+
+ /* Send unplug virtual cable to unknown devices */
+ if (ret == -ENOENT && psm == L2CAP_PSM_HIDP_CTRL) {
+ unsigned char unplug = 0x15;
+ int err, sk = g_io_channel_unix_get_fd(chan);
+ err = write(sk, &unplug, sizeof(unplug));
+ }
+
+ g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+static void auth_callback(DBusError *derr, void *user_data)
+{
+ struct input_server *server = user_data;
+ bdaddr_t src, dst;
+ GError *err = NULL;
+
+ bt_io_get(server->confirm, BT_IO_L2CAP, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &src,
+ BT_IO_OPT_DEST_BDADDR, &dst,
+ BT_IO_OPT_INVALID);
+ if (err) {
+ error("%s", err->message);
+ g_error_free(err);
+ goto reject;
+ }
+
+ if (derr) {
+ error("Access denied: %s", derr->message);
+ goto reject;
+ }
+
+ if (!bt_io_accept(server->confirm, connect_event_cb, server,
+ NULL, &err)) {
+ error("bt_io_accept: %s", err->message);
+ g_error_free(err);
+ goto reject;
+ }
+
+ g_io_channel_unref(server->confirm);
+ server->confirm = NULL;
+
+ return;
+
+reject:
+ g_io_channel_shutdown(server->confirm, TRUE, NULL);
+ g_io_channel_unref(server->confirm);
+ server->confirm = NULL;
+ input_device_close_channels(&src, &dst);
+}
+
+static void confirm_event_cb(GIOChannel *chan, gpointer user_data)
+{
+ struct input_server *server = user_data;
+ bdaddr_t src, dst;
+ GError *err = NULL;
+ int ret;
+
+ bt_io_get(chan, BT_IO_L2CAP, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &src,
+ BT_IO_OPT_DEST_BDADDR, &dst,
+ BT_IO_OPT_INVALID);
+ if (err) {
+ error("%s", err->message);
+ g_error_free(err);
+ goto drop;
+ }
+
+ if (server->confirm) {
+ error("Refusing connection: setup in progress");
+ goto drop;
+ }
+
+ server->confirm = g_io_channel_ref(chan);
+
+ ret = btd_request_authorization(&src, &dst, HID_UUID,
+ auth_callback, server);
+ if (ret == 0)
+ return;
+
+ g_io_channel_unref(server->confirm);
+ server->confirm = NULL;
+
+drop:
+ input_device_close_channels(&src, &dst);
+ g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+int server_start(const bdaddr_t *src)
+{
+ struct input_server *server;
+ GError *err = NULL;
+
+ server = g_new0(struct input_server, 1);
+ bacpy(&server->src, src);
+
+ server->ctrl = bt_io_listen(BT_IO_L2CAP, connect_event_cb, NULL,
+ server, NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, src,
+ BT_IO_OPT_PSM, L2CAP_PSM_HIDP_CTRL,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_INVALID);
+ if (!server->ctrl) {
+ error("Failed to listen on control channel");
+ g_error_free(err);
+ g_free(server);
+ return -1;
+ }
+
+ server->intr = bt_io_listen(BT_IO_L2CAP, NULL, confirm_event_cb,
+ server, NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, src,
+ BT_IO_OPT_PSM, L2CAP_PSM_HIDP_INTR,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_INVALID);
+ if (!server->intr) {
+ error("Failed to listen on interrupt channel");
+ g_io_channel_unref(server->ctrl);
+ g_error_free(err);
+ g_free(server);
+ return -1;
+ }
+
+ servers = g_slist_append(servers, server);
+
+ return 0;
+}
+
+void server_stop(const bdaddr_t *src)
+{
+ struct input_server *server;
+ GSList *l;
+
+ l = g_slist_find_custom(servers, src, server_cmp);
+ if (!l)
+ return;
+
+ server = l->data;
+
+ g_io_channel_shutdown(server->intr, TRUE, NULL);
+ g_io_channel_unref(server->intr);
+
+ g_io_channel_shutdown(server->ctrl, TRUE, NULL);
+ g_io_channel_unref(server->ctrl);
+
+ servers = g_slist_remove(servers, server);
+ g_free(server);
+}
diff --git a/input/server.h b/input/server.h
new file mode 100644
index 0000000..74159bb
--- /dev/null
+++ b/input/server.h
@@ -0,0 +1,25 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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
+ *
+ */
+
+int server_start(const bdaddr_t *src);
+void server_stop(const bdaddr_t *src);
diff --git a/install-sh b/install-sh
new file mode 100755
index 0000000..6781b98
--- /dev/null
+++ b/install-sh
@@ -0,0 +1,520 @@
+#!/bin/sh
+# install - install a program, script, or datafile
+
+scriptversion=2009-04-28.21; # UTC
+
+# This originates from X11R5 (mit/util/scripts/install.sh), which was
+# later released in X11R6 (xc/config/util/install.sh) with the
+# following copyright and license.
+#
+# Copyright (C) 1994 X Consortium
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
+# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+# Except as contained in this notice, the name of the X Consortium shall not
+# be used in advertising or otherwise to promote the sale, use or other deal-
+# ings in this Software without prior written authorization from the X Consor-
+# tium.
+#
+#
+# FSF changes to this file are in the public domain.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+
+nl='
+'
+IFS=" "" $nl"
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit=${DOITPROG-}
+if test -z "$doit"; then
+ doit_exec=exec
+else
+ doit_exec=$doit
+fi
+
+# Put in absolute file names if you don't have them in your path;
+# or use environment vars.
+
+chgrpprog=${CHGRPPROG-chgrp}
+chmodprog=${CHMODPROG-chmod}
+chownprog=${CHOWNPROG-chown}
+cmpprog=${CMPPROG-cmp}
+cpprog=${CPPROG-cp}
+mkdirprog=${MKDIRPROG-mkdir}
+mvprog=${MVPROG-mv}
+rmprog=${RMPROG-rm}
+stripprog=${STRIPPROG-strip}
+
+posix_glob='?'
+initialize_posix_glob='
+ test "$posix_glob" != "?" || {
+ if (set -f) 2>/dev/null; then
+ posix_glob=
+ else
+ posix_glob=:
+ fi
+ }
+'
+
+posix_mkdir=
+
+# Desired mode of installed file.
+mode=0755
+
+chgrpcmd=
+chmodcmd=$chmodprog
+chowncmd=
+mvcmd=$mvprog
+rmcmd="$rmprog -f"
+stripcmd=
+
+src=
+dst=
+dir_arg=
+dst_arg=
+
+copy_on_change=false
+no_target_directory=
+
+usage="\
+Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
+ or: $0 [OPTION]... SRCFILES... DIRECTORY
+ or: $0 [OPTION]... -t DIRECTORY SRCFILES...
+ or: $0 [OPTION]... -d DIRECTORIES...
+
+In the 1st form, copy SRCFILE to DSTFILE.
+In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
+In the 4th, create DIRECTORIES.
+
+Options:
+ --help display this help and exit.
+ --version display version info and exit.
+
+ -c (ignored)
+ -C install only if different (preserve the last data modification time)
+ -d create directories instead of installing files.
+ -g GROUP $chgrpprog installed files to GROUP.
+ -m MODE $chmodprog installed files to MODE.
+ -o USER $chownprog installed files to USER.
+ -s $stripprog installed files.
+ -t DIRECTORY install into DIRECTORY.
+ -T report an error if DSTFILE is a directory.
+
+Environment variables override the default commands:
+ CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
+ RMPROG STRIPPROG
+"
+
+while test $# -ne 0; do
+ case $1 in
+ -c) ;;
+
+ -C) copy_on_change=true;;
+
+ -d) dir_arg=true;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift;;
+
+ --help) echo "$usage"; exit $?;;
+
+ -m) mode=$2
+ case $mode in
+ *' '* | *' '* | *'
+'* | *'*'* | *'?'* | *'['*)
+ echo "$0: invalid mode: $mode" >&2
+ exit 1;;
+ esac
+ shift;;
+
+ -o) chowncmd="$chownprog $2"
+ shift;;
+
+ -s) stripcmd=$stripprog;;
+
+ -t) dst_arg=$2
+ shift;;
+
+ -T) no_target_directory=true;;
+
+ --version) echo "$0 $scriptversion"; exit $?;;
+
+ --) shift
+ break;;
+
+ -*) echo "$0: invalid option: $1" >&2
+ exit 1;;
+
+ *) break;;
+ esac
+ shift
+done
+
+if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
+ # When -d is used, all remaining arguments are directories to create.
+ # When -t is used, the destination is already specified.
+ # Otherwise, the last argument is the destination. Remove it from $@.
+ for arg
+ do
+ if test -n "$dst_arg"; then
+ # $@ is not empty: it contains at least $arg.
+ set fnord "$@" "$dst_arg"
+ shift # fnord
+ fi
+ shift # arg
+ dst_arg=$arg
+ done
+fi
+
+if test $# -eq 0; then
+ if test -z "$dir_arg"; then
+ echo "$0: no input file specified." >&2
+ exit 1
+ fi
+ # It's OK to call `install-sh -d' without argument.
+ # This can happen when creating conditional directories.
+ exit 0
+fi
+
+if test -z "$dir_arg"; then
+ trap '(exit $?); exit' 1 2 13 15
+
+ # Set umask so as not to create temps with too-generous modes.
+ # However, 'strip' requires both read and write access to temps.
+ case $mode in
+ # Optimize common cases.
+ *644) cp_umask=133;;
+ *755) cp_umask=22;;
+
+ *[0-7])
+ if test -z "$stripcmd"; then
+ u_plus_rw=
+ else
+ u_plus_rw='% 200'
+ fi
+ cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
+ *)
+ if test -z "$stripcmd"; then
+ u_plus_rw=
+ else
+ u_plus_rw=,u+rw
+ fi
+ cp_umask=$mode$u_plus_rw;;
+ esac
+fi
+
+for src
+do
+ # Protect names starting with `-'.
+ case $src in
+ -*) src=./$src;;
+ esac
+
+ if test -n "$dir_arg"; then
+ dst=$src
+ dstdir=$dst
+ test -d "$dstdir"
+ dstdir_status=$?
+ else
+
+ # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
+ # might cause directories to be created, which would be especially bad
+ # if $src (and thus $dsttmp) contains '*'.
+ if test ! -f "$src" && test ! -d "$src"; then
+ echo "$0: $src does not exist." >&2
+ exit 1
+ fi
+
+ if test -z "$dst_arg"; then
+ echo "$0: no destination specified." >&2
+ exit 1
+ fi
+
+ dst=$dst_arg
+ # Protect names starting with `-'.
+ case $dst in
+ -*) dst=./$dst;;
+ esac
+
+ # If destination is a directory, append the input filename; won't work
+ # if double slashes aren't ignored.
+ if test -d "$dst"; then
+ if test -n "$no_target_directory"; then
+ echo "$0: $dst_arg: Is a directory" >&2
+ exit 1
+ fi
+ dstdir=$dst
+ dst=$dstdir/`basename "$src"`
+ dstdir_status=0
+ else
+ # Prefer dirname, but fall back on a substitute if dirname fails.
+ dstdir=`
+ (dirname "$dst") 2>/dev/null ||
+ expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$dst" : 'X\(//\)[^/]' \| \
+ X"$dst" : 'X\(//\)$' \| \
+ X"$dst" : 'X\(/\)' \| . 2>/dev/null ||
+ echo X"$dst" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'
+ `
+
+ test -d "$dstdir"
+ dstdir_status=$?
+ fi
+ fi
+
+ obsolete_mkdir_used=false
+
+ if test $dstdir_status != 0; then
+ case $posix_mkdir in
+ '')
+ # Create intermediate dirs using mode 755 as modified by the umask.
+ # This is like FreeBSD 'install' as of 1997-10-28.
+ umask=`umask`
+ case $stripcmd.$umask in
+ # Optimize common cases.
+ *[2367][2367]) mkdir_umask=$umask;;
+ .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
+
+ *[0-7])
+ mkdir_umask=`expr $umask + 22 \
+ - $umask % 100 % 40 + $umask % 20 \
+ - $umask % 10 % 4 + $umask % 2
+ `;;
+ *) mkdir_umask=$umask,go-w;;
+ esac
+
+ # With -d, create the new directory with the user-specified mode.
+ # Otherwise, rely on $mkdir_umask.
+ if test -n "$dir_arg"; then
+ mkdir_mode=-m$mode
+ else
+ mkdir_mode=
+ fi
+
+ posix_mkdir=false
+ case $umask in
+ *[123567][0-7][0-7])
+ # POSIX mkdir -p sets u+wx bits regardless of umask, which
+ # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
+ ;;
+ *)
+ tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
+ trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
+
+ if (umask $mkdir_umask &&
+ exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
+ then
+ if test -z "$dir_arg" || {
+ # Check for POSIX incompatibilities with -m.
+ # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
+ # other-writeable bit of parent directory when it shouldn't.
+ # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
+ ls_ld_tmpdir=`ls -ld "$tmpdir"`
+ case $ls_ld_tmpdir in
+ d????-?r-*) different_mode=700;;
+ d????-?--*) different_mode=755;;
+ *) false;;
+ esac &&
+ $mkdirprog -m$different_mode -p -- "$tmpdir" && {
+ ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
+ test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
+ }
+ }
+ then posix_mkdir=:
+ fi
+ rmdir "$tmpdir/d" "$tmpdir"
+ else
+ # Remove any dirs left behind by ancient mkdir implementations.
+ rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
+ fi
+ trap '' 0;;
+ esac;;
+ esac
+
+ if
+ $posix_mkdir && (
+ umask $mkdir_umask &&
+ $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
+ )
+ then :
+ else
+
+ # The umask is ridiculous, or mkdir does not conform to POSIX,
+ # or it failed possibly due to a race condition. Create the
+ # directory the slow way, step by step, checking for races as we go.
+
+ case $dstdir in
+ /*) prefix='/';;
+ -*) prefix='./';;
+ *) prefix='';;
+ esac
+
+ eval "$initialize_posix_glob"
+
+ oIFS=$IFS
+ IFS=/
+ $posix_glob set -f
+ set fnord $dstdir
+ shift
+ $posix_glob set +f
+ IFS=$oIFS
+
+ prefixes=
+
+ for d
+ do
+ test -z "$d" && continue
+
+ prefix=$prefix$d
+ if test -d "$prefix"; then
+ prefixes=
+ else
+ if $posix_mkdir; then
+ (umask=$mkdir_umask &&
+ $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
+ # Don't fail if two instances are running concurrently.
+ test -d "$prefix" || exit 1
+ else
+ case $prefix in
+ *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
+ *) qprefix=$prefix;;
+ esac
+ prefixes="$prefixes '$qprefix'"
+ fi
+ fi
+ prefix=$prefix/
+ done
+
+ if test -n "$prefixes"; then
+ # Don't fail if two instances are running concurrently.
+ (umask $mkdir_umask &&
+ eval "\$doit_exec \$mkdirprog $prefixes") ||
+ test -d "$dstdir" || exit 1
+ obsolete_mkdir_used=true
+ fi
+ fi
+ fi
+
+ if test -n "$dir_arg"; then
+ { test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
+ { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
+ { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
+ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
+ else
+
+ # Make a couple of temp file names in the proper directory.
+ dsttmp=$dstdir/_inst.$$_
+ rmtmp=$dstdir/_rm.$$_
+
+ # Trap to clean up those temp files at exit.
+ trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
+
+ # Copy the file name to the temp name.
+ (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
+
+ # and set any options; do chmod last to preserve setuid bits.
+ #
+ # If any of these fail, we abort the whole thing. If we want to
+ # ignore errors from any of these, just make sure not to ignore
+ # errors from the above "$doit $cpprog $src $dsttmp" command.
+ #
+ { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
+ { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
+ { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
+ { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
+
+ # If -C, don't bother to copy if it wouldn't change the file.
+ if $copy_on_change &&
+ old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
+ new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
+
+ eval "$initialize_posix_glob" &&
+ $posix_glob set -f &&
+ set X $old && old=:$2:$4:$5:$6 &&
+ set X $new && new=:$2:$4:$5:$6 &&
+ $posix_glob set +f &&
+
+ test "$old" = "$new" &&
+ $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
+ then
+ rm -f "$dsttmp"
+ else
+ # Rename the file to the real destination.
+ $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
+
+ # The rename failed, perhaps because mv can't rename something else
+ # to itself, or perhaps because mv is so ancient that it does not
+ # support -f.
+ {
+ # Now remove or move aside any old file at destination location.
+ # We try this two ways since rm can't unlink itself on some
+ # systems and the destination file might be busy for other
+ # reasons. In this case, the final cleanup might fail but the new
+ # file should still install successfully.
+ {
+ test ! -f "$dst" ||
+ $doit $rmcmd -f "$dst" 2>/dev/null ||
+ { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
+ { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
+ } ||
+ { echo "$0: cannot unlink or rename $dst" >&2
+ (exit 1); exit 1
+ }
+ } &&
+
+ # Now rename the file to the real destination.
+ $doit $mvcmd "$dsttmp" "$dst"
+ }
+ fi || exit 1
+
+ trap '' 0
+ fi
+done
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/lib/bluetooth.c b/lib/bluetooth.c
new file mode 100644
index 0000000..72ee456
--- /dev/null
+++ b/lib/bluetooth.c
@@ -0,0 +1,470 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2000-2001 Qualcomm Incorporated
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 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 <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include "bluetooth.h"
+#include "hci.h"
+
+void baswap(bdaddr_t *dst, const bdaddr_t *src)
+{
+ register unsigned char *d = (unsigned char *) dst;
+ register const unsigned char *s = (const unsigned char *) src;
+ register int i;
+
+ for (i = 0; i < 6; i++)
+ d[i] = s[5-i];
+}
+
+char *batostr(const bdaddr_t *ba)
+{
+ char *str = bt_malloc(18);
+ if (!str)
+ return NULL;
+
+ sprintf(str, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
+ ba->b[0], ba->b[1], ba->b[2],
+ ba->b[3], ba->b[4], ba->b[5]);
+
+ return str;
+}
+
+bdaddr_t *strtoba(const char *str)
+{
+ bdaddr_t b;
+ bdaddr_t *ba = bt_malloc(sizeof(*ba));
+
+ if (ba) {
+ str2ba(str, &b);
+ baswap(ba, &b);
+ }
+
+ return ba;
+}
+
+int ba2str(const bdaddr_t *ba, char *str)
+{
+ return sprintf(str, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
+ ba->b[5], ba->b[4], ba->b[3], ba->b[2], ba->b[1], ba->b[0]);
+}
+
+int str2ba(const char *str, bdaddr_t *ba)
+{
+ bdaddr_t b;
+ int i;
+
+ if (bachk(str) < 0) {
+ memset(ba, 0, sizeof(*ba));
+ return -1;
+ }
+
+ for (i = 0; i < 6; i++, str += 3)
+ b.b[i] = strtol(str, NULL, 16);
+
+ baswap(ba, &b);
+
+ return 0;
+}
+
+int ba2oui(const bdaddr_t *ba, char *str)
+{
+ return sprintf(str, "%2.2X-%2.2X-%2.2X", ba->b[5], ba->b[4], ba->b[3]);
+}
+
+int bachk(const char *str)
+{
+ if (!str)
+ return -1;
+
+ if (strlen(str) != 17)
+ return -1;
+
+ while (*str) {
+ if (!isxdigit(*str++))
+ return -1;
+
+ if (!isxdigit(*str++))
+ return -1;
+
+ if (*str == 0)
+ break;
+
+ if (*str++ != ':')
+ return -1;
+ }
+
+ return 0;
+}
+
+int baprintf(const char *format, ...)
+{
+ va_list ap;
+ int len;
+
+ va_start(ap, format);
+ len = vprintf(format, ap);
+ va_end(ap);
+
+ return len;
+}
+
+int bafprintf(FILE *stream, const char *format, ...)
+{
+ va_list ap;
+ int len;
+
+ va_start(ap, format);
+ len = vfprintf(stream, format, ap);
+ va_end(ap);
+
+ return len;
+}
+
+int basprintf(char *str, const char *format, ...)
+{
+ va_list ap;
+ int len;
+
+ va_start(ap, format);
+ len = vsnprintf(str, (~0U) >> 1, format, ap);
+ va_end(ap);
+
+ return len;
+}
+
+int basnprintf(char *str, size_t size, const char *format, ...)
+{
+ va_list ap;
+ int len;
+
+ va_start(ap, format);
+ len = vsnprintf(str, size, format, ap);
+ va_end(ap);
+
+ return len;
+}
+
+void *bt_malloc(size_t size)
+{
+ return malloc(size);
+}
+
+void bt_free(void *ptr)
+{
+ free(ptr);
+}
+
+/* Bluetooth error codes to Unix errno mapping */
+int bt_error(uint16_t code)
+{
+ switch (code) {
+ case 0:
+ return 0;
+ case HCI_UNKNOWN_COMMAND:
+ return EBADRQC;
+ case HCI_NO_CONNECTION:
+ return ENOTCONN;
+ case HCI_HARDWARE_FAILURE:
+ return EIO;
+ case HCI_PAGE_TIMEOUT:
+ return EHOSTDOWN;
+ case HCI_AUTHENTICATION_FAILURE:
+ return EACCES;
+ case HCI_PIN_OR_KEY_MISSING:
+ return EINVAL;
+ case HCI_MEMORY_FULL:
+ return ENOMEM;
+ case HCI_CONNECTION_TIMEOUT:
+ return ETIMEDOUT;
+ case HCI_MAX_NUMBER_OF_CONNECTIONS:
+ case HCI_MAX_NUMBER_OF_SCO_CONNECTIONS:
+ return EMLINK;
+ case HCI_ACL_CONNECTION_EXISTS:
+ return EALREADY;
+ case HCI_COMMAND_DISALLOWED:
+ case HCI_TRANSACTION_COLLISION:
+ case HCI_ROLE_SWITCH_PENDING:
+ return EBUSY;
+ case HCI_REJECTED_LIMITED_RESOURCES:
+ case HCI_REJECTED_PERSONAL:
+ case HCI_QOS_REJECTED:
+ return ECONNREFUSED;
+ case HCI_HOST_TIMEOUT:
+ return ETIMEDOUT;
+ case HCI_UNSUPPORTED_FEATURE:
+ case HCI_QOS_NOT_SUPPORTED:
+ case HCI_PAIRING_NOT_SUPPORTED:
+ case HCI_CLASSIFICATION_NOT_SUPPORTED:
+ case HCI_UNSUPPORTED_LMP_PARAMETER_VALUE:
+ case HCI_PARAMETER_OUT_OF_RANGE:
+ case HCI_QOS_UNACCEPTABLE_PARAMETER:
+ return EOPNOTSUPP;
+ case HCI_INVALID_PARAMETERS:
+ case HCI_SLOT_VIOLATION:
+ return EINVAL;
+ case HCI_OE_USER_ENDED_CONNECTION:
+ case HCI_OE_LOW_RESOURCES:
+ case HCI_OE_POWER_OFF:
+ return ECONNRESET;
+ case HCI_CONNECTION_TERMINATED:
+ return ECONNABORTED;
+ case HCI_REPEATED_ATTEMPTS:
+ return ELOOP;
+ case HCI_REJECTED_SECURITY:
+ case HCI_PAIRING_NOT_ALLOWED:
+ case HCI_INSUFFICIENT_SECURITY:
+ return EACCES;
+ case HCI_UNSUPPORTED_REMOTE_FEATURE:
+ return EPROTONOSUPPORT;
+ case HCI_SCO_OFFSET_REJECTED:
+ return ECONNREFUSED;
+ case HCI_UNKNOWN_LMP_PDU:
+ case HCI_INVALID_LMP_PARAMETERS:
+ case HCI_LMP_ERROR_TRANSACTION_COLLISION:
+ case HCI_LMP_PDU_NOT_ALLOWED:
+ case HCI_ENCRYPTION_MODE_NOT_ACCEPTED:
+ return EPROTO;
+ default:
+ return ENOSYS;
+ }
+}
+
+char *bt_compidtostr(int compid)
+{
+ switch (compid) {
+ case 0:
+ return "Ericsson Technology Licensing";
+ case 1:
+ return "Nokia Mobile Phones";
+ case 2:
+ return "Intel Corp.";
+ case 3:
+ return "IBM Corp.";
+ case 4:
+ return "Toshiba Corp.";
+ case 5:
+ return "3Com";
+ case 6:
+ return "Microsoft";
+ case 7:
+ return "Lucent";
+ case 8:
+ return "Motorola";
+ case 9:
+ return "Infineon Technologies AG";
+ case 10:
+ return "Cambridge Silicon Radio";
+ case 11:
+ return "Silicon Wave";
+ case 12:
+ return "Digianswer A/S";
+ case 13:
+ return "Texas Instruments Inc.";
+ case 14:
+ return "Parthus Technologies Inc.";
+ case 15:
+ return "Broadcom Corporation";
+ case 16:
+ return "Mitel Semiconductor";
+ case 17:
+ return "Widcomm, Inc.";
+ case 18:
+ return "Zeevo, Inc.";
+ case 19:
+ return "Atmel Corporation";
+ case 20:
+ return "Mitsubishi Electric Corporation";
+ case 21:
+ return "RTX Telecom A/S";
+ case 22:
+ return "KC Technology Inc.";
+ case 23:
+ return "Newlogic";
+ case 24:
+ return "Transilica, Inc.";
+ case 25:
+ return "Rohde & Schwartz GmbH & Co. KG";
+ case 26:
+ return "TTPCom Limited";
+ case 27:
+ return "Signia Technologies, Inc.";
+ case 28:
+ return "Conexant Systems Inc.";
+ case 29:
+ return "Qualcomm";
+ case 30:
+ return "Inventel";
+ case 31:
+ return "AVM Berlin";
+ case 32:
+ return "BandSpeed, Inc.";
+ case 33:
+ return "Mansella Ltd";
+ case 34:
+ return "NEC Corporation";
+ case 35:
+ return "WavePlus Technology Co., Ltd.";
+ case 36:
+ return "Alcatel";
+ case 37:
+ return "Philips Semiconductors";
+ case 38:
+ return "C Technologies";
+ case 39:
+ return "Open Interface";
+ case 40:
+ return "R F Micro Devices";
+ case 41:
+ return "Hitachi Ltd";
+ case 42:
+ return "Symbol Technologies, Inc.";
+ case 43:
+ return "Tenovis";
+ case 44:
+ return "Macronix International Co. Ltd.";
+ case 45:
+ return "GCT Semiconductor";
+ case 46:
+ return "Norwood Systems";
+ case 47:
+ return "MewTel Technology Inc.";
+ case 48:
+ return "ST Microelectronics";
+ case 49:
+ return "Synopsys";
+ case 50:
+ return "Red-M (Communications) Ltd";
+ case 51:
+ return "Commil Ltd";
+ case 52:
+ return "Computer Access Technology Corporation (CATC)";
+ case 53:
+ return "Eclipse (HQ Espana) S.L.";
+ case 54:
+ return "Renesas Technology Corp.";
+ case 55:
+ return "Mobilian Corporation";
+ case 56:
+ return "Terax";
+ case 57:
+ return "Integrated System Solution Corp.";
+ case 58:
+ return "Matsushita Electric Industrial Co., Ltd.";
+ case 59:
+ return "Gennum Corporation";
+ case 60:
+ return "Research In Motion";
+ case 61:
+ return "IPextreme, Inc.";
+ case 62:
+ return "Systems and Chips, Inc";
+ case 63:
+ return "Bluetooth SIG, Inc";
+ case 64:
+ return "Seiko Epson Corporation";
+ case 65:
+ return "Integrated Silicon Solution Taiwain, Inc.";
+ case 66:
+ return "CONWISE Technology Corporation Ltd";
+ case 67:
+ return "PARROT SA";
+ case 68:
+ return "Socket Communications";
+ case 69:
+ return "Atheros Communications, Inc.";
+ case 70:
+ return "MediaTek, Inc.";
+ case 71:
+ return "Bluegiga";
+ case 72:
+ return "Marvell Technology Group Ltd.";
+ case 73:
+ return "3DSP Corporation";
+ case 74:
+ return "Accel Semiconductor Ltd.";
+ case 75:
+ return "Continental Automotive Systems";
+ case 76:
+ return "Apple, Inc.";
+ case 77:
+ return "Staccato Communications, Inc.";
+ case 78:
+ return "Avago Technologies";
+ case 79:
+ return "APT Ltd.";
+ case 80:
+ return "SiRF Technology, Inc.";
+ case 81:
+ return "Tzero Technologies, Inc.";
+ case 82:
+ return "J&M Corporation";
+ case 83:
+ return "Free2move AB";
+ case 84:
+ return "3DiJoy Corporation";
+ case 85:
+ return "Plantronics, Inc.";
+ case 86:
+ return "Sony Ericsson Mobile Communications";
+ case 87:
+ return "Harman International Industries, Inc.";
+ case 88:
+ return "Vizio, Inc.";
+ case 89:
+ return "Nordic Semiconductor ASA";
+ case 90:
+ return "EM Microelectronic-Marin SA";
+ case 91:
+ return "Ralink Technology Corporation";
+ case 92:
+ return "Belkin International, Inc.";
+ case 93:
+ return "Realtek Semiconductor Corporation";
+ case 94:
+ return "Stonestreet One, LLC";
+ case 95:
+ return "Wicentric, Inc.";
+ case 96:
+ return "RivieraWaves S.A.S";
+ case 97:
+ return "RDA Microelectronics";
+ case 98:
+ return "Gibson Guitars";
+ case 65535:
+ return "internal use";
+ default:
+ return "not assigned";
+ }
+}
diff --git a/lib/bluetooth.h b/lib/bluetooth.h
new file mode 100644
index 0000000..738e07a
--- /dev/null
+++ b/lib/bluetooth.h
@@ -0,0 +1,219 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2000-2001 Qualcomm Incorporated
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 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
+ *
+ */
+
+#ifndef __BLUETOOTH_H
+#define __BLUETOOTH_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <endian.h>
+#include <byteswap.h>
+#include <netinet/in.h>
+
+#ifndef AF_BLUETOOTH
+#define AF_BLUETOOTH 31
+#define PF_BLUETOOTH AF_BLUETOOTH
+#endif
+
+#define BTPROTO_L2CAP 0
+#define BTPROTO_HCI 1
+#define BTPROTO_SCO 2
+#define BTPROTO_RFCOMM 3
+#define BTPROTO_BNEP 4
+#define BTPROTO_CMTP 5
+#define BTPROTO_HIDP 6
+#define BTPROTO_AVDTP 7
+
+#define SOL_HCI 0
+#define SOL_L2CAP 6
+#define SOL_SCO 17
+#define SOL_RFCOMM 18
+
+#ifndef SOL_BLUETOOTH
+#define SOL_BLUETOOTH 274
+#endif
+
+#define BT_SECURITY 4
+struct bt_security {
+ uint8_t level;
+};
+#define BT_SECURITY_SDP 0
+#define BT_SECURITY_LOW 1
+#define BT_SECURITY_MEDIUM 2
+#define BT_SECURITY_HIGH 3
+
+#define BT_DEFER_SETUP 7
+
+#define BT_FLUSHABLE 8
+
+#define BT_FLUSHABLE_OFF 0
+#define BT_FLUSHABLE_ON 1
+
+/* Connection and socket states */
+enum {
+ BT_CONNECTED = 1, /* Equal to TCP_ESTABLISHED to make net code happy */
+ BT_OPEN,
+ BT_BOUND,
+ BT_LISTEN,
+ BT_CONNECT,
+ BT_CONNECT2,
+ BT_CONFIG,
+ BT_DISCONN,
+ BT_CLOSED
+};
+
+/* Byte order conversions */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define htobs(d) (d)
+#define htobl(d) (d)
+#define btohs(d) (d)
+#define btohl(d) (d)
+#elif __BYTE_ORDER == __BIG_ENDIAN
+#define htobs(d) bswap_16(d)
+#define htobl(d) bswap_32(d)
+#define btohs(d) bswap_16(d)
+#define btohl(d) bswap_32(d)
+#else
+#error "Unknown byte order"
+#endif
+
+/* Bluetooth unaligned access */
+#define bt_get_unaligned(ptr) \
+({ \
+ struct __attribute__((packed)) { \
+ typeof(*(ptr)) __v; \
+ } *__p = (void *) (ptr); \
+ __p->__v; \
+})
+
+#define bt_put_unaligned(val, ptr) \
+do { \
+ struct __attribute__((packed)) { \
+ typeof(*(ptr)) __v; \
+ } *__p = (void *) (ptr); \
+ __p->__v = (val); \
+} while(0)
+
+/* BD Address */
+typedef struct {
+ uint8_t b[6];
+} __attribute__((packed)) bdaddr_t;
+
+#define BDADDR_ANY (&(bdaddr_t) {{0, 0, 0, 0, 0, 0}})
+#define BDADDR_ALL (&(bdaddr_t) {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}})
+#define BDADDR_LOCAL (&(bdaddr_t) {{0, 0, 0, 0xff, 0xff, 0xff}})
+
+/* Copy, swap, convert BD Address */
+static inline int bacmp(const bdaddr_t *ba1, const bdaddr_t *ba2)
+{
+ return memcmp(ba1, ba2, sizeof(bdaddr_t));
+}
+static inline void bacpy(bdaddr_t *dst, const bdaddr_t *src)
+{
+ memcpy(dst, src, sizeof(bdaddr_t));
+}
+
+void baswap(bdaddr_t *dst, const bdaddr_t *src);
+bdaddr_t *strtoba(const char *str);
+char *batostr(const bdaddr_t *ba);
+int ba2str(const bdaddr_t *ba, char *str);
+int str2ba(const char *str, bdaddr_t *ba);
+int ba2oui(const bdaddr_t *ba, char *oui);
+int bachk(const char *str);
+
+int baprintf(const char *format, ...);
+int bafprintf(FILE *stream, const char *format, ...);
+int basprintf(char *str, const char *format, ...);
+int basnprintf(char *str, size_t size, const char *format, ...);
+
+void *bt_malloc(size_t size);
+void bt_free(void *ptr);
+
+int bt_error(uint16_t code);
+char *bt_compidtostr(int id);
+
+typedef struct {
+ uint8_t data[16];
+} uint128_t;
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+
+#define ntoh64(x) (x)
+
+static inline void ntoh128(const uint128_t *src, uint128_t *dst)
+{
+ memcpy(dst, src, sizeof(uint128_t));
+}
+
+static inline void btoh128(const uint128_t *src, uint128_t *dst)
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ dst->data[15 - i] = src->data[i];
+}
+
+#else
+
+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;
+}
+
+static inline void ntoh128(const uint128_t *src, uint128_t *dst)
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ dst->data[15 - i] = src->data[i];
+}
+
+static inline void btoh128(const uint128_t *src, uint128_t *dst)
+{
+ memcpy(dst, src, sizeof(uint128_t));
+}
+
+#endif
+
+#define hton64(x) ntoh64(x)
+#define hton128(x, y) ntoh128(x, y)
+#define htob128(x, y) btoh128(x, y)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __BLUETOOTH_H */
diff --git a/lib/bnep.h b/lib/bnep.h
new file mode 100644
index 0000000..2bbfb17
--- /dev/null
+++ b/lib/bnep.h
@@ -0,0 +1,153 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 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
+ *
+ */
+
+#ifndef __BNEP_H
+#define __BNEP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <bluetooth/bluetooth.h>
+
+#ifndef ETH_ALEN
+#define ETH_ALEN 6 /* from <net/ethernet.h> */
+#endif
+
+/* BNEP UUIDs */
+#define BNEP_BASE_UUID 0x0000000000001000800000805F9B34FB
+#define BNEP_UUID16 0x02
+#define BNEP_UUID32 0x04
+#define BNEP_UUID128 0x16
+
+#define BNEP_SVC_PANU 0x1115
+#define BNEP_SVC_NAP 0x1116
+#define BNEP_SVC_GN 0x1117
+
+/* BNEP packet types */
+#define BNEP_GENERAL 0x00
+#define BNEP_CONTROL 0x01
+#define BNEP_COMPRESSED 0x02
+#define BNEP_COMPRESSED_SRC_ONLY 0x03
+#define BNEP_COMPRESSED_DST_ONLY 0x04
+
+/* BNEP control types */
+#define BNEP_CMD_NOT_UNDERSTOOD 0x00
+#define BNEP_SETUP_CONN_REQ 0x01
+#define BNEP_SETUP_CONN_RSP 0x02
+#define BNEP_FILTER_NET_TYPE_SET 0x03
+#define BNEP_FILTER_NET_TYPE_RSP 0x04
+#define BNEP_FILTER_MULT_ADDR_SET 0x05
+#define BNEP_FILTER_MULT_ADDR_RSP 0x06
+
+/* BNEP response messages */
+#define BNEP_SUCCESS 0x00
+
+#define BNEP_CONN_INVALID_DST 0x01
+#define BNEP_CONN_INVALID_SRC 0x02
+#define BNEP_CONN_INVALID_SVC 0x03
+#define BNEP_CONN_NOT_ALLOWED 0x04
+
+#define BNEP_FILTER_UNSUPPORTED_REQ 0x01
+#define BNEP_FILTER_INVALID_RANGE 0x02
+#define BNEP_FILTER_INVALID_MCADDR 0x02
+#define BNEP_FILTER_LIMIT_REACHED 0x03
+#define BNEP_FILTER_DENIED_SECURITY 0x04
+
+/* L2CAP settings */
+#define BNEP_MTU 1691
+#define BNEP_FLUSH_TO 0xffff
+#define BNEP_CONNECT_TO 15
+#define BNEP_FILTER_TO 15
+
+#ifndef BNEP_PSM
+#define BNEP_PSM 0x0f
+#endif
+
+/* BNEP headers */
+#define BNEP_TYPE_MASK 0x7f
+#define BNEP_EXT_HEADER 0x80
+
+struct bnep_setup_conn_req {
+ uint8_t type;
+ uint8_t ctrl;
+ uint8_t uuid_size;
+ uint8_t service[0];
+} __attribute__((packed));
+
+struct bnep_set_filter_req {
+ uint8_t type;
+ uint8_t ctrl;
+ uint16_t len;
+ uint8_t list[0];
+} __attribute__((packed));
+
+struct bnep_control_rsp {
+ uint8_t type;
+ uint8_t ctrl;
+ uint16_t resp;
+} __attribute__((packed));
+
+struct bnep_ext_hdr {
+ uint8_t type;
+ uint8_t len;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* BNEP ioctl defines */
+#define BNEPCONNADD _IOW('B', 200, int)
+#define BNEPCONNDEL _IOW('B', 201, int)
+#define BNEPGETCONNLIST _IOR('B', 210, int)
+#define BNEPGETCONNINFO _IOR('B', 211, int)
+
+struct bnep_connadd_req {
+ int sock; /* Connected socket */
+ uint32_t flags;
+ uint16_t role;
+ char device[16]; /* Name of the Ethernet device */
+};
+
+struct bnep_conndel_req {
+ uint32_t flags;
+ uint8_t dst[ETH_ALEN];
+};
+
+struct bnep_conninfo {
+ uint32_t flags;
+ uint16_t role;
+ uint16_t state;
+ uint8_t dst[ETH_ALEN];
+ char device[16];
+};
+
+struct bnep_connlist_req {
+ uint32_t cnum;
+ struct bnep_conninfo *ci;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __BNEP_H */
diff --git a/lib/cmtp.h b/lib/cmtp.h
new file mode 100644
index 0000000..ce937bd
--- /dev/null
+++ b/lib/cmtp.h
@@ -0,0 +1,69 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2010 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
+ *
+ */
+
+#ifndef __CMTP_H
+#define __CMTP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* CMTP defaults */
+#define CMTP_MINIMUM_MTU 152
+#define CMTP_DEFAULT_MTU 672
+
+/* CMTP ioctl defines */
+#define CMTPCONNADD _IOW('C', 200, int)
+#define CMTPCONNDEL _IOW('C', 201, int)
+#define CMTPGETCONNLIST _IOR('C', 210, int)
+#define CMTPGETCONNINFO _IOR('C', 211, int)
+
+#define CMTP_LOOPBACK 0
+
+struct cmtp_connadd_req {
+ int sock; /* Connected socket */
+ uint32_t flags;
+};
+
+struct cmtp_conndel_req {
+ bdaddr_t bdaddr;
+ uint32_t flags;
+};
+
+struct cmtp_conninfo {
+ bdaddr_t bdaddr;
+ uint32_t flags;
+ uint16_t state;
+ int num;
+};
+
+struct cmtp_connlist_req {
+ uint32_t cnum;
+ struct cmtp_conninfo *ci;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CMTP_H */
diff --git a/lib/hci.c b/lib/hci.c
new file mode 100644
index 0000000..eb00730
--- /dev/null
+++ b/lib/hci.c
@@ -0,0 +1,2913 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2000-2001 Qualcomm Incorporated
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 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 <sys/param.h>
+#include <sys/uio.h>
+#include <sys/poll.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include "bluetooth.h"
+#include "hci.h"
+#include "hci_lib.h"
+
+#ifndef MIN
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+#endif
+
+typedef struct {
+ char *str;
+ unsigned int val;
+} hci_map;
+
+static char *hci_bit2str(hci_map *m, unsigned int val)
+{
+ char *str = malloc(120);
+ char *ptr = str;
+
+ if (!str)
+ return NULL;
+
+ *ptr = 0;
+ while (m->str) {
+ if ((unsigned int) m->val & val)
+ ptr += sprintf(ptr, "%s ", m->str);
+ m++;
+ }
+ return str;
+}
+
+static int hci_str2bit(hci_map *map, char *str, unsigned int *val)
+{
+ char *t, *ptr;
+ hci_map *m;
+ int set;
+
+ if (!str || !(str = ptr = strdup(str)))
+ return 0;
+
+ *val = set = 0;
+
+ while ((t = strsep(&ptr, ","))) {
+ for (m = map; m->str; m++) {
+ if (!strcasecmp(m->str, t)) {
+ *val |= (unsigned int) m->val;
+ set = 1;
+ }
+ }
+ }
+ free(str);
+
+ return set;
+}
+
+static char *hci_uint2str(hci_map *m, unsigned int val)
+{
+ char *str = malloc(50);
+ char *ptr = str;
+
+ if (!str)
+ return NULL;
+
+ *ptr = 0;
+ while (m->str) {
+ if ((unsigned int) m->val == val) {
+ ptr += sprintf(ptr, "%s", m->str);
+ break;
+ }
+ m++;
+ }
+ return str;
+}
+
+static int hci_str2uint(hci_map *map, char *str, unsigned int *val)
+{
+ char *t, *ptr;
+ hci_map *m;
+ int set = 0;
+
+ if (!str)
+ return 0;
+
+ str = ptr = strdup(str);
+
+ while ((t = strsep(&ptr, ","))) {
+ for (m = map; m->str; m++) {
+ if (!strcasecmp(m->str,t)) {
+ *val = (unsigned int) m->val;
+ set = 1;
+ break;
+ }
+ }
+ }
+ free(str);
+
+ return set;
+}
+
+char *hci_bustostr(int bus)
+{
+ switch (bus) {
+ case HCI_VIRTUAL:
+ return "VIRTUAL";
+ case HCI_USB:
+ return "USB";
+ case HCI_PCCARD:
+ return "PCCARD";
+ case HCI_UART:
+ return "UART";
+ case HCI_RS232:
+ return "RS232";
+ case HCI_PCI:
+ return "PCI";
+ case HCI_SDIO:
+ return "SDIO";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+char *hci_dtypetostr(int type)
+{
+ return hci_bustostr(type & 0x0f);
+}
+
+char *hci_typetostr(int type)
+{
+ switch (type) {
+ case HCI_BREDR:
+ return "BR/EDR";
+ case HCI_AMP:
+ return "AMP";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+/* HCI dev flags mapping */
+static hci_map dev_flags_map[] = {
+ { "UP", HCI_UP },
+ { "INIT", HCI_INIT },
+ { "RUNNING", HCI_RUNNING },
+ { "RAW", HCI_RAW },
+ { "PSCAN", HCI_PSCAN },
+ { "ISCAN", HCI_ISCAN },
+ { "INQUIRY", HCI_INQUIRY },
+ { "AUTH", HCI_AUTH },
+ { "ENCRYPT", HCI_ENCRYPT },
+ { NULL }
+};
+
+char *hci_dflagstostr(uint32_t flags)
+{
+ char *str = bt_malloc(50);
+ char *ptr = str;
+ hci_map *m = dev_flags_map;
+
+ if (!str)
+ return NULL;
+
+ *ptr = 0;
+
+ if (!hci_test_bit(HCI_UP, &flags))
+ ptr += sprintf(ptr, "DOWN ");
+
+ while (m->str) {
+ if (hci_test_bit(m->val, &flags))
+ ptr += sprintf(ptr, "%s ", m->str);
+ m++;
+ }
+ return str;
+}
+
+/* HCI packet type mapping */
+static hci_map pkt_type_map[] = {
+ { "DM1", HCI_DM1 },
+ { "DM3", HCI_DM3 },
+ { "DM5", HCI_DM5 },
+ { "DH1", HCI_DH1 },
+ { "DH3", HCI_DH3 },
+ { "DH5", HCI_DH5 },
+ { "HV1", HCI_HV1 },
+ { "HV2", HCI_HV2 },
+ { "HV3", HCI_HV3 },
+ { "2-DH1", HCI_2DH1 },
+ { "2-DH3", HCI_2DH3 },
+ { "2-DH5", HCI_2DH5 },
+ { "3-DH1", HCI_3DH1 },
+ { "3-DH3", HCI_3DH3 },
+ { "3-DH5", HCI_3DH5 },
+ { NULL }
+};
+
+static hci_map sco_ptype_map[] = {
+ { "HV1", 0x0001 },
+ { "HV2", 0x0002 },
+ { "HV3", 0x0004 },
+ { "EV3", HCI_EV3 },
+ { "EV4", HCI_EV4 },
+ { "EV5", HCI_EV5 },
+ { "2-EV3", HCI_2EV3 },
+ { "2-EV5", HCI_2EV5 },
+ { "3-EV3", HCI_3EV3 },
+ { "3-EV5", HCI_3EV5 },
+ { NULL }
+};
+
+char *hci_ptypetostr(unsigned int ptype)
+{
+ return hci_bit2str(pkt_type_map, ptype);
+}
+
+int hci_strtoptype(char *str, unsigned int *val)
+{
+ return hci_str2bit(pkt_type_map, str, val);
+}
+
+char *hci_scoptypetostr(unsigned int ptype)
+{
+ return hci_bit2str(sco_ptype_map, ptype);
+}
+
+int hci_strtoscoptype(char *str, unsigned int *val)
+{
+ return hci_str2bit(sco_ptype_map, str, val);
+}
+
+/* Link policy mapping */
+static hci_map link_policy_map[] = {
+ { "NONE", 0 },
+ { "RSWITCH", HCI_LP_RSWITCH },
+ { "HOLD", HCI_LP_HOLD },
+ { "SNIFF", HCI_LP_SNIFF },
+ { "PARK", HCI_LP_PARK },
+ { NULL }
+};
+
+char *hci_lptostr(unsigned int lp)
+{
+ return hci_bit2str(link_policy_map, lp);
+}
+
+int hci_strtolp(char *str, unsigned int *val)
+{
+ return hci_str2bit(link_policy_map, str, val);
+}
+
+/* Link mode mapping */
+static hci_map link_mode_map[] = {
+ { "NONE", 0 },
+ { "ACCEPT", HCI_LM_ACCEPT },
+ { "MASTER", HCI_LM_MASTER },
+ { "AUTH", HCI_LM_AUTH },
+ { "ENCRYPT", HCI_LM_ENCRYPT },
+ { "TRUSTED", HCI_LM_TRUSTED },
+ { "RELIABLE", HCI_LM_RELIABLE },
+ { "SECURE", HCI_LM_SECURE },
+ { NULL }
+};
+
+char *hci_lmtostr(unsigned int lm)
+{
+ char *s, *str = bt_malloc(50);
+ if (!str)
+ return NULL;
+
+ *str = 0;
+ if (!(lm & HCI_LM_MASTER))
+ strcpy(str, "SLAVE ");
+
+ s = hci_bit2str(link_mode_map, lm);
+ if (!s) {
+ bt_free(str);
+ return NULL;
+ }
+
+ strcat(str, s);
+ free(s);
+ return str;
+}
+
+int hci_strtolm(char *str, unsigned int *val)
+{
+ return hci_str2bit(link_mode_map, str, val);
+}
+
+/* Command mapping */
+static hci_map commands_map[] = {
+ { "Inquiry", 0 },
+ { "Inquiry Cancel", 1 },
+ { "Periodic Inquiry Mode", 2 },
+ { "Exit Periodic Inquiry Mode", 3 },
+ { "Create Connection", 4 },
+ { "Disconnect", 5 },
+ { "Add SCO Connection", 6 },
+ { "Cancel Create Connection", 7 },
+
+ { "Accept Connection Request", 8 },
+ { "Reject Connection Request", 9 },
+ { "Link Key Request Reply", 10 },
+ { "Link Key Request Negative Reply", 11 },
+ { "PIN Code Request Reply", 12 },
+ { "PIN Code Request Negative Reply", 13 },
+ { "Change Connection Packet Type", 14 },
+ { "Authentication Requested", 15 },
+
+ { "Set Connection Encryption", 16 },
+ { "Change Connection Link Key", 17 },
+ { "Master Link Key", 18 },
+ { "Remote Name Request", 19 },
+ { "Cancel Remote Name Request", 20 },
+ { "Read Remote Supported Features", 21 },
+ { "Read Remote Extended Features", 22 },
+ { "Read Remote Version Information", 23 },
+
+ { "Read Clock Offset", 24 },
+ { "Read LMP Handle", 25 },
+ { "Reserved", 26 },
+ { "Reserved", 27 },
+ { "Reserved", 28 },
+ { "Reserved", 29 },
+ { "Reserved", 30 },
+ { "Reserved", 31 },
+
+ { "Reserved", 32 },
+ { "Hold Mode", 33 },
+ { "Sniff Mode", 34 },
+ { "Exit Sniff Mode", 35 },
+ { "Park State", 36 },
+ { "Exit Park State", 37 },
+ { "QoS Setup", 38 },
+ { "Role Discovery", 39 },
+
+ { "Switch Role", 40 },
+ { "Read Link Policy Settings", 41 },
+ { "Write Link Policy Settings", 42 },
+ { "Read Default Link Policy Settings", 43 },
+ { "Write Default Link Policy Settings", 44 },
+ { "Flow Specification", 45 },
+ { "Set Event Mask", 46 },
+ { "Reset", 47 },
+
+ { "Set Event Filter", 48 },
+ { "Flush", 49 },
+ { "Read PIN Type", 50 },
+ { "Write PIN Type", 51 },
+ { "Create New Unit Key", 52 },
+ { "Read Stored Link Key", 53 },
+ { "Write Stored Link Key", 54 },
+ { "Delete Stored Link Key", 55 },
+
+ { "Write Local Name", 56 },
+ { "Read Local Name", 57 },
+ { "Read Connection Accept Timeout", 58 },
+ { "Write Connection Accept Timeout", 59 },
+ { "Read Page Timeout", 60 },
+ { "Write Page Timeout", 61 },
+ { "Read Scan Enable", 62 },
+ { "Write Scan Enable", 63 },
+
+ { "Read Page Scan Activity", 64 },
+ { "Write Page Scan Activity", 65 },
+ { "Read Inquiry Scan Activity", 66 },
+ { "Write Inquiry Scan Activity", 67 },
+ { "Read Authentication Enable", 68 },
+ { "Write Authentication Enable", 69 },
+ { "Read Encryption Mode", 70 },
+ { "Write Encryption Mode", 71 },
+
+ { "Read Class Of Device", 72 },
+ { "Write Class Of Device", 73 },
+ { "Read Voice Setting", 74 },
+ { "Write Voice Setting", 75 },
+ { "Read Automatic Flush Timeout", 76 },
+ { "Write Automatic Flush Timeout", 77 },
+ { "Read Num Broadcast Retransmissions", 78 },
+ { "Write Num Broadcast Retransmissions", 79 },
+
+ { "Read Hold Mode Activity", 80 },
+ { "Write Hold Mode Activity", 81 },
+ { "Read Transmit Power Level", 82 },
+ { "Read Synchronous Flow Control Enable", 83 },
+ { "Write Synchronous Flow Control Enable", 84 },
+ { "Set Host Controller To Host Flow Control", 85 },
+ { "Host Buffer Size", 86 },
+ { "Host Number Of Completed Packets", 87 },
+
+ { "Read Link Supervision Timeout", 88 },
+ { "Write Link Supervision Timeout", 89 },
+ { "Read Number of Supported IAC", 90 },
+ { "Read Current IAC LAP", 91 },
+ { "Write Current IAC LAP", 92 },
+ { "Read Page Scan Period Mode", 93 },
+ { "Write Page Scan Period Mode", 94 },
+ { "Read Page Scan Mode", 95 },
+
+ { "Write Page Scan Mode", 96 },
+ { "Set AFH Channel Classification", 97 },
+ { "Reserved", 98 },
+ { "Reserved", 99 },
+ { "Read Inquiry Scan Type", 100 },
+ { "Write Inquiry Scan Type", 101 },
+ { "Read Inquiry Mode", 102 },
+ { "Write Inquiry Mode", 103 },
+
+ { "Read Page Scan Type", 104 },
+ { "Write Page Scan Type", 105 },
+ { "Read AFH Channel Assessment Mode", 106 },
+ { "Write AFH Channel Assessment Mode", 107 },
+ { "Reserved", 108 },
+ { "Reserved", 109 },
+ { "Reserved", 110 },
+ { "Reserved", 111 },
+
+ { "Reserved", 112 },
+ { "Reserved", 113 },
+ { "Reserved", 114 },
+ { "Read Local Version Information", 115 },
+ { "Read Local Supported Commands", 116 },
+ { "Read Local Supported Features", 117 },
+ { "Read Local Extended Features", 118 },
+ { "Read Buffer Size", 119 },
+
+ { "Read Country Code", 120 },
+ { "Read BD ADDR", 121 },
+ { "Read Failed Contact Counter", 122 },
+ { "Reset Failed Contact Counter", 123 },
+ { "Get Link Quality", 124 },
+ { "Read RSSI", 125 },
+ { "Read AFH Channel Map", 126 },
+ { "Read BD Clock", 127 },
+
+ { "Read Loopback Mode", 128 },
+ { "Write Loopback Mode", 129 },
+ { "Enable Device Under Test Mode", 130 },
+ { "Setup Synchronous Connection", 131 },
+ { "Accept Synchronous Connection", 132 },
+ { "Reject Synchronous Connection", 133 },
+ { "Reserved", 134 },
+ { "Reserved", 135 },
+
+ { "Read Extended Inquiry Response", 136 },
+ { "Write Extended Inquiry Response", 137 },
+ { "Refresh Encryption Key", 138 },
+ { "Reserved", 139 },
+ { "Sniff Subrating", 140 },
+ { "Read Simple Pairing Mode", 141 },
+ { "Write Simple Pairing Mode", 142 },
+ { "Read Local OOB Data", 143 },
+
+ { "Read Inquiry Response Transmit Power Level", 144 },
+ { "Write Inquiry Transmit Power Level", 145 },
+ { "Read Default Erroneous Data Reporting", 146 },
+ { "Write Default Erroneous Data Reporting", 147 },
+ { "Reserved", 148 },
+ { "Reserved", 149 },
+ { "Reserved", 150 },
+ { "IO Capability Request Reply", 151 },
+
+ { "User Confirmation Request Reply", 152 },
+ { "User Confirmation Request Negative Reply", 153 },
+ { "User Passkey Request Reply", 154 },
+ { "User Passkey Request Negative Reply", 155 },
+ { "Remote OOB Data Request Reply", 156 },
+ { "Write Simple Pairing Debug Mode", 157 },
+ { "Enhanced Flush", 158 },
+ { "Remote OOB Data Request Negative Reply", 159 },
+
+ { "Reserved", 160 },
+ { "Reserved", 161 },
+ { "Send Keypress Notification", 162 },
+ { "IO Capability Request Negative Reply", 163 },
+ { "Read Encryption Key Size", 164 },
+ { "Reserved", 165 },
+ { "Reserved", 166 },
+ { "Reserved", 167 },
+
+ { "Create Physical Link", 168 },
+ { "Accept Physical Link", 169 },
+ { "Disconnect Physical Link", 170 },
+ { "Create Logical Link", 171 },
+ { "Accept Logical Link", 172 },
+ { "Disconnect Logical Link", 173 },
+ { "Logical Link Cancel", 174 },
+ { "Flow Specification Modify", 175 },
+
+ { "Read Logical Link Accept Timeout", 176 },
+ { "Write Logical Link Accept Timeout", 177 },
+ { "Set Event Mask Page 2", 178 },
+ { "Read Location Data", 179 },
+ { "Write Location Data", 180 },
+ { "Read Local AMP Info", 181 },
+ { "Read Local AMP_ASSOC", 182 },
+ { "Write Remote AMP_ASSOC", 183 },
+
+ { "Read Flow Control Mode", 184 },
+ { "Write Flow Control Mode", 185 },
+ { "Read Data Block Size", 186 },
+ { "Reserved", 187 },
+ { "Reserved", 188 },
+ { "Enable AMP Receiver Reports", 189 },
+ { "AMP Test End", 190 },
+ { "AMP Test Command", 191 },
+
+ { "Read Enhanced Transmit Power Level", 192 },
+ { "Reserved", 193 },
+ { "Read Best Effort Flush Timeout", 194 },
+ { "Write Best Effort Flush Timeout", 195 },
+ { "Short Range Mode", 196 },
+ { "Read LE Host Support", 197 },
+ { "Write LE Host Support", 198 },
+ { "Reserved", 199 },
+
+ { "LE Set Event Mask", 200 },
+ { "LE Read Buffer Size", 201 },
+ { "LE Read Local Supported Features", 202 },
+ { "Reserved", 203 },
+ { "LE Set Random Address", 204 },
+ { "LE Set Advertising Parameters", 205 },
+ { "LE Read Advertising Channel TX Power", 206 },
+ { "LE Set Advertising Data", 207 },
+
+ { "LE Set Scan Response Data", 208 },
+ { "LE Set Advertise Enable", 209 },
+ { "LE Set Scan Parameters", 210 },
+ { "LE Set Scan Enable", 211 },
+ { "LE Create Connection", 212 },
+ { "LE Create Connection Cancel", 213 },
+ { "LE Read White List Size", 214 },
+ { "LE Clear White List", 215 },
+
+ { "LE Add Device To White List", 216 },
+ { "LE Remove Device From White List", 217 },
+ { "LE Connection Update", 218 },
+ { "LE Set Host Channel Classification", 219 },
+ { "LE Read Channel Map", 220 },
+ { "LE Read Remote Used Features", 221 },
+ { "LE Encrypt", 222 },
+ { "LE Rand", 223 },
+
+ { "LE Start Encryption", 224 },
+ { "LE Long Term Key Request Reply", 225 },
+ { "LE Long Term Key Request Negative Reply", 226 },
+ { "LE Read Supported States", 227 },
+ { "LE Receiver Test", 228 },
+ { "LE Transmitter Test", 229 },
+ { "LE Test End", 230 },
+ { "Reserved", 231 },
+
+ { NULL }
+};
+
+char *hci_cmdtostr(unsigned int cmd)
+{
+ return hci_uint2str(commands_map, cmd);
+}
+
+char *hci_commandstostr(uint8_t *commands, char *pref, int width)
+{
+ unsigned int maxwidth = width - 3;
+ hci_map *m;
+ char *off, *ptr, *str;
+ int size = 10;
+
+ m = commands_map;
+
+ while (m->str) {
+ if (commands[m->val / 8] & (1 << (m->val % 8)))
+ size += strlen(m->str) + (pref ? strlen(pref) : 0) + 3;
+ m++;
+ }
+
+ str = bt_malloc(size);
+ if (!str)
+ return NULL;
+
+ ptr = str; *ptr = '\0';
+
+ if (pref)
+ ptr += sprintf(ptr, "%s", pref);
+
+ off = ptr;
+
+ m = commands_map;
+
+ while (m->str) {
+ if (commands[m->val / 8] & (1 << (m->val % 8))) {
+ if (strlen(off) + strlen(m->str) > maxwidth) {
+ ptr += sprintf(ptr, "\n%s", pref ? pref : "");
+ off = ptr;
+ }
+ ptr += sprintf(ptr, "'%s' ", m->str);
+ }
+ m++;
+ }
+
+ return str;
+}
+
+/* Version mapping */
+static hci_map ver_map[] = {
+ { "1.0b", 0x00 },
+ { "1.1", 0x01 },
+ { "1.2", 0x02 },
+ { "2.0", 0x03 },
+ { "2.1", 0x04 },
+ { "3.0", 0x05 },
+ { "4.0", 0x06 },
+ { NULL }
+};
+
+char *hci_vertostr(unsigned int ver)
+{
+ return hci_uint2str(ver_map, ver);
+}
+
+int hci_strtover(char *str, unsigned int *ver)
+{
+ return hci_str2uint(ver_map, str, ver);
+}
+
+char *lmp_vertostr(unsigned int ver)
+{
+ return hci_uint2str(ver_map, ver);
+}
+
+int lmp_strtover(char *str, unsigned int *ver)
+{
+ return hci_str2uint(ver_map, str, ver);
+}
+
+/* LMP features mapping */
+static hci_map lmp_features_map[8][9] = {
+ { /* Byte 0 */
+ { "<3-slot packets>", LMP_3SLOT }, /* Bit 0 */
+ { "<5-slot packets>", LMP_5SLOT }, /* Bit 1 */
+ { "<encryption>", LMP_ENCRYPT }, /* Bit 2 */
+ { "<slot offset>", LMP_SOFFSET }, /* Bit 3 */
+ { "<timing accuracy>", LMP_TACCURACY }, /* Bit 4 */
+ { "<role switch>", LMP_RSWITCH }, /* Bit 5 */
+ { "<hold mode>", LMP_HOLD }, /* Bit 6 */
+ { "<sniff mode>", LMP_SNIFF }, /* Bit 7 */
+ { NULL }
+ },
+ { /* Byte 1 */
+ { "<park state>", LMP_PARK }, /* Bit 0 */
+ { "<RSSI>", LMP_RSSI }, /* Bit 1 */
+ { "<channel quality>", LMP_QUALITY }, /* Bit 2 */
+ { "<SCO link>", LMP_SCO }, /* Bit 3 */
+ { "<HV2 packets>", LMP_HV2 }, /* Bit 4 */
+ { "<HV3 packets>", LMP_HV3 }, /* Bit 5 */
+ { "<u-law log>", LMP_ULAW }, /* Bit 6 */
+ { "<A-law log>", LMP_ALAW }, /* Bit 7 */
+ { NULL }
+ },
+ { /* Byte 2 */
+ { "<CVSD>", LMP_CVSD }, /* Bit 0 */
+ { "<paging scheme>", LMP_PSCHEME }, /* Bit 1 */
+ { "<power control>", LMP_PCONTROL }, /* Bit 2 */
+ { "<transparent SCO>", LMP_TRSP_SCO }, /* Bit 3 */
+ { "<broadcast encrypt>",LMP_BCAST_ENC }, /* Bit 7 */
+ { NULL }
+ },
+ { /* Byte 3 */
+ { "<no. 24>", 0x01 }, /* Bit 0 */
+ { "<EDR ACL 2 Mbps>", LMP_EDR_ACL_2M }, /* Bit 1 */
+ { "<EDR ACL 3 Mbps>", LMP_EDR_ACL_3M }, /* Bit 2 */
+ { "<enhanced iscan>", LMP_ENH_ISCAN }, /* Bit 3 */
+ { "<interlaced iscan>", LMP_ILACE_ISCAN }, /* Bit 4 */
+ { "<interlaced pscan>", LMP_ILACE_PSCAN }, /* Bit 5 */
+ { "<inquiry with RSSI>",LMP_RSSI_INQ }, /* Bit 6 */
+ { "<extended SCO>", LMP_ESCO }, /* Bit 7 */
+ { NULL }
+ },
+ { /* Byte 4 */
+ { "<EV4 packets>", LMP_EV4 }, /* Bit 0 */
+ { "<EV5 packets>", LMP_EV5 }, /* Bit 1 */
+ { "<no. 34>", 0x04 }, /* Bit 2 */
+ { "<AFH cap. slave>", LMP_AFH_CAP_SLV }, /* Bit 3 */
+ { "<AFH class. slave>", LMP_AFH_CLS_SLV }, /* Bit 4 */
+ { "<BR/EDR not supp.>", LMP_NO_BREDR }, /* Bit 5 */
+ { "<LE support>", LMP_LE }, /* Bit 6 */
+ { "<3-slot EDR ACL>", LMP_EDR_3SLOT }, /* Bit 7 */
+ { NULL }
+ },
+ { /* Byte 5 */
+ { "<5-slot EDR ACL>", LMP_EDR_5SLOT }, /* Bit 0 */
+ { "<sniff subrating>", LMP_SNIFF_SUBR }, /* Bit 1 */
+ { "<pause encryption>", LMP_PAUSE_ENC }, /* Bit 2 */
+ { "<AFH cap. master>", LMP_AFH_CAP_MST }, /* Bit 3 */
+ { "<AFH class. master>",LMP_AFH_CLS_MST }, /* Bit 4 */
+ { "<EDR eSCO 2 Mbps>", LMP_EDR_ESCO_2M }, /* Bit 5 */
+ { "<EDR eSCO 3 Mbps>", LMP_EDR_ESCO_3M }, /* Bit 6 */
+ { "<3-slot EDR eSCO>", LMP_EDR_3S_ESCO }, /* Bit 7 */
+ { NULL }
+ },
+ { /* Byte 6 */
+ { "<extended inquiry>", LMP_EXT_INQ }, /* Bit 0 */
+ { "<LE and BR/EDR>", LMP_LE_BREDR }, /* Bit 1 */
+ { "<no. 50>", 0x04 }, /* Bit 2 */
+ { "<simple pairing>", LMP_SIMPLE_PAIR }, /* Bit 3 */
+ { "<encapsulated PDU>", LMP_ENCAPS_PDU }, /* Bit 4 */
+ { "<err. data report>", LMP_ERR_DAT_REP }, /* Bit 5 */
+ { "<non-flush flag>", LMP_NFLUSH_PKTS }, /* Bit 6 */
+ { "<no. 55>", 0x80 }, /* Bit 7 */
+ { NULL }
+ },
+ { /* Byte 7 */
+ { "<LSTO>", LMP_LSTO }, /* Bit 1 */
+ { "<inquiry TX power>", LMP_INQ_TX_PWR }, /* Bit 1 */
+ { "<EPC>", LMP_EPC }, /* Bit 2 */
+ { "<no. 59>", 0x08 }, /* Bit 3 */
+ { "<no. 60>", 0x10 }, /* Bit 4 */
+ { "<no. 61>", 0x20 }, /* Bit 5 */
+ { "<no. 62>", 0x40 }, /* Bit 6 */
+ { "<extended features>",LMP_EXT_FEAT }, /* Bit 7 */
+ { NULL }
+ },
+};
+
+char *lmp_featurestostr(uint8_t *features, char *pref, int width)
+{
+ unsigned int maxwidth = width - 1;
+ char *off, *ptr, *str;
+ int i, size = 10;
+
+ for (i = 0; i < 8; i++) {
+ hci_map *m = lmp_features_map[i];
+
+ while (m->str) {
+ if (m->val & features[i])
+ size += strlen(m->str) +
+ (pref ? strlen(pref) : 0) + 1;
+ m++;
+ }
+ }
+
+ str = bt_malloc(size);
+ if (!str)
+ return NULL;
+
+ ptr = str; *ptr = '\0';
+
+ if (pref)
+ ptr += sprintf(ptr, "%s", pref);
+
+ off = ptr;
+
+ for (i = 0; i < 8; i++) {
+ hci_map *m = lmp_features_map[i];
+
+ while (m->str) {
+ if (m->val & features[i]) {
+ if (strlen(off) + strlen(m->str) > maxwidth) {
+ ptr += sprintf(ptr, "\n%s",
+ pref ? pref : "");
+ off = ptr;
+ }
+ ptr += sprintf(ptr, "%s ", m->str);
+ }
+ m++;
+ }
+ }
+
+ return str;
+}
+
+/* HCI functions that do not require open device */
+int hci_for_each_dev(int flag, int (*func)(int dd, int dev_id, long arg),
+ long arg)
+{
+ struct hci_dev_list_req *dl;
+ struct hci_dev_req *dr;
+ int dev_id = -1;
+ int i, sk, err = 0;
+
+ sk = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
+ if (sk < 0)
+ return -1;
+
+ dl = malloc(HCI_MAX_DEV * sizeof(*dr) + sizeof(*dl));
+ if (!dl) {
+ err = errno;
+ goto done;
+ }
+
+ memset(dl, 0, HCI_MAX_DEV * sizeof(*dr) + sizeof(*dl));
+
+ dl->dev_num = HCI_MAX_DEV;
+ dr = dl->dev_req;
+
+ if (ioctl(sk, HCIGETDEVLIST, (void *) dl) < 0) {
+ err = errno;
+ goto free;
+ }
+
+ for (i = 0; i < dl->dev_num; i++, dr++) {
+ if (hci_test_bit(flag, &dr->dev_opt))
+ if (!func || func(sk, dr->dev_id, arg)) {
+ dev_id = dr->dev_id;
+ break;
+ }
+ }
+
+ if (dev_id < 0)
+ err = ENODEV;
+
+free:
+ free(dl);
+
+done:
+ close(sk);
+ errno = err;
+
+ return dev_id;
+}
+
+static int __other_bdaddr(int dd, int dev_id, long arg)
+{
+ struct hci_dev_info di = { dev_id: dev_id };
+
+ if (ioctl(dd, HCIGETDEVINFO, (void *) &di))
+ return 0;
+
+ if (hci_test_bit(HCI_RAW, &di.flags))
+ return 0;
+
+ return bacmp((bdaddr_t *) arg, &di.bdaddr);
+}
+
+static int __same_bdaddr(int dd, int dev_id, long arg)
+{
+ struct hci_dev_info di = { dev_id: dev_id };
+
+ if (ioctl(dd, HCIGETDEVINFO, (void *) &di))
+ return 0;
+
+ return !bacmp((bdaddr_t *) arg, &di.bdaddr);
+}
+
+int hci_get_route(bdaddr_t *bdaddr)
+{
+ return hci_for_each_dev(HCI_UP, __other_bdaddr,
+ (long) (bdaddr ? bdaddr : BDADDR_ANY));
+}
+
+int hci_devid(const char *str)
+{
+ bdaddr_t ba;
+ int id = -1;
+
+ if (!strncmp(str, "hci", 3) && strlen(str) >= 4) {
+ id = atoi(str + 3);
+ if (hci_devba(id, &ba) < 0)
+ return -1;
+ } else {
+ errno = ENODEV;
+ str2ba(str, &ba);
+ id = hci_for_each_dev(HCI_UP, __same_bdaddr, (long) &ba);
+ }
+
+ return id;
+}
+
+int hci_devinfo(int dev_id, struct hci_dev_info *di)
+{
+ int dd, err, ret;
+
+ dd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
+ if (dd < 0)
+ return dd;
+
+ memset(di, 0, sizeof(struct hci_dev_info));
+
+ di->dev_id = dev_id;
+ ret = ioctl(dd, HCIGETDEVINFO, (void *) di);
+
+ err = errno;
+ close(dd);
+ errno = err;
+
+ return ret;
+}
+
+int hci_devba(int dev_id, bdaddr_t *bdaddr)
+{
+ struct hci_dev_info di;
+
+ memset(&di, 0, sizeof(di));
+
+ if (hci_devinfo(dev_id, &di))
+ return -1;
+
+ if (!hci_test_bit(HCI_UP, &di.flags)) {
+ errno = ENETDOWN;
+ return -1;
+ }
+
+ bacpy(bdaddr, &di.bdaddr);
+
+ return 0;
+}
+
+int hci_inquiry(int dev_id, int len, int nrsp, const uint8_t *lap,
+ inquiry_info **ii, long flags)
+{
+ struct hci_inquiry_req *ir;
+ uint8_t num_rsp = nrsp;
+ void *buf;
+ int dd, size, err, ret = -1;
+
+ if (nrsp <= 0) {
+ num_rsp = 0;
+ nrsp = 255;
+ }
+
+ if (dev_id < 0) {
+ dev_id = hci_get_route(NULL);
+ if (dev_id < 0) {
+ errno = ENODEV;
+ return -1;
+ }
+ }
+
+ dd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
+ if (dd < 0)
+ return dd;
+
+ buf = malloc(sizeof(*ir) + (sizeof(inquiry_info) * (nrsp)));
+ if (!buf)
+ goto done;
+
+ ir = buf;
+ ir->dev_id = dev_id;
+ ir->num_rsp = num_rsp;
+ ir->length = len;
+ ir->flags = flags;
+
+ if (lap) {
+ memcpy(ir->lap, lap, 3);
+ } else {
+ ir->lap[0] = 0x33;
+ ir->lap[1] = 0x8b;
+ ir->lap[2] = 0x9e;
+ }
+
+ ret = ioctl(dd, HCIINQUIRY, (unsigned long) buf);
+ if (ret < 0)
+ goto free;
+
+ size = sizeof(inquiry_info) * ir->num_rsp;
+
+ if (!*ii)
+ *ii = malloc(size);
+
+ if (*ii) {
+ memcpy((void *) *ii, buf + sizeof(*ir), size);
+ ret = ir->num_rsp;
+ } else
+ ret = -1;
+
+free:
+ free(buf);
+
+done:
+ err = errno;
+ close(dd);
+ errno = err;
+
+ return ret;
+}
+
+/* Open HCI device.
+ * Returns device descriptor (dd). */
+int hci_open_dev(int dev_id)
+{
+ struct sockaddr_hci a;
+ int dd, err;
+
+ /* Create HCI socket */
+ dd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
+ if (dd < 0)
+ return dd;
+
+ /* Bind socket to the HCI device */
+ memset(&a, 0, sizeof(a));
+ a.hci_family = AF_BLUETOOTH;
+ a.hci_dev = dev_id;
+ if (bind(dd, (struct sockaddr *) &a, sizeof(a)) < 0)
+ goto failed;
+
+ return dd;
+
+failed:
+ err = errno;
+ close(dd);
+ errno = err;
+
+ return -1;
+}
+
+int hci_close_dev(int dd)
+{
+ return close(dd);
+}
+
+/* HCI functions that require open device
+ * dd - Device descriptor returned by hci_open_dev. */
+
+int hci_send_cmd(int dd, uint16_t ogf, uint16_t ocf, uint8_t plen, void *param)
+{
+ uint8_t type = HCI_COMMAND_PKT;
+ hci_command_hdr hc;
+ struct iovec iv[3];
+ int ivn;
+
+ hc.opcode = htobs(cmd_opcode_pack(ogf, ocf));
+ hc.plen= plen;
+
+ iv[0].iov_base = &type;
+ iv[0].iov_len = 1;
+ iv[1].iov_base = &hc;
+ iv[1].iov_len = HCI_COMMAND_HDR_SIZE;
+ ivn = 2;
+
+ if (plen) {
+ iv[2].iov_base = param;
+ iv[2].iov_len = plen;
+ ivn = 3;
+ }
+
+ while (writev(dd, iv, ivn) < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ return -1;
+ }
+ return 0;
+}
+
+int hci_send_req(int dd, struct hci_request *r, int to)
+{
+ unsigned char buf[HCI_MAX_EVENT_SIZE], *ptr;
+ uint16_t opcode = htobs(cmd_opcode_pack(r->ogf, r->ocf));
+ struct hci_filter nf, of;
+ socklen_t olen;
+ hci_event_hdr *hdr;
+ int err, try;
+
+ olen = sizeof(of);
+ if (getsockopt(dd, SOL_HCI, HCI_FILTER, &of, &olen) < 0)
+ return -1;
+
+ hci_filter_clear(&nf);
+ hci_filter_set_ptype(HCI_EVENT_PKT, &nf);
+ hci_filter_set_event(EVT_CMD_STATUS, &nf);
+ hci_filter_set_event(EVT_CMD_COMPLETE, &nf);
+ hci_filter_set_event(EVT_LE_META_EVENT, &nf);
+ hci_filter_set_event(r->event, &nf);
+ hci_filter_set_opcode(opcode, &nf);
+ if (setsockopt(dd, SOL_HCI, HCI_FILTER, &nf, sizeof(nf)) < 0)
+ return -1;
+
+ if (hci_send_cmd(dd, r->ogf, r->ocf, r->clen, r->cparam) < 0)
+ goto failed;
+
+ try = 10;
+ while (try--) {
+ evt_cmd_complete *cc;
+ evt_cmd_status *cs;
+ evt_remote_name_req_complete *rn;
+ evt_le_meta_event *me;
+ remote_name_req_cp *cp;
+ int len;
+
+ if (to) {
+ struct pollfd p;
+ int n;
+
+ p.fd = dd; p.events = POLLIN;
+ while ((n = poll(&p, 1, to)) < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ goto failed;
+ }
+
+ if (!n) {
+ errno = ETIMEDOUT;
+ goto failed;
+ }
+
+ to -= 10;
+ if (to < 0)
+ to = 0;
+
+ }
+
+ while ((len = read(dd, buf, sizeof(buf))) < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ goto failed;
+ }
+
+ hdr = (void *) (buf + 1);
+ ptr = buf + (1 + HCI_EVENT_HDR_SIZE);
+ len -= (1 + HCI_EVENT_HDR_SIZE);
+
+ switch (hdr->evt) {
+ case EVT_CMD_STATUS:
+ cs = (void *) ptr;
+
+ if (cs->opcode != opcode)
+ continue;
+
+ if (r->event != EVT_CMD_STATUS) {
+ if (cs->status) {
+ errno = EIO;
+ goto failed;
+ }
+ break;
+ }
+
+ r->rlen = MIN(len, r->rlen);
+ memcpy(r->rparam, ptr, r->rlen);
+ goto done;
+
+ case EVT_CMD_COMPLETE:
+ cc = (void *) ptr;
+
+ if (cc->opcode != opcode)
+ continue;
+
+ ptr += EVT_CMD_COMPLETE_SIZE;
+ len -= EVT_CMD_COMPLETE_SIZE;
+
+ r->rlen = MIN(len, r->rlen);
+ memcpy(r->rparam, ptr, r->rlen);
+ goto done;
+
+ case EVT_REMOTE_NAME_REQ_COMPLETE:
+ if (hdr->evt != r->event)
+ break;
+
+ rn = (void *) ptr;
+ cp = r->cparam;
+
+ if (bacmp(&rn->bdaddr, &cp->bdaddr))
+ continue;
+
+ r->rlen = MIN(len, r->rlen);
+ memcpy(r->rparam, ptr, r->rlen);
+ goto done;
+
+ case EVT_LE_META_EVENT:
+ me = (void *) ptr;
+
+ if (me->subevent != r->event)
+ continue;
+
+ len -= 1;
+ r->rlen = MIN(len, r->rlen);
+ memcpy(r->rparam, me->data, r->rlen);
+ goto done;
+
+ default:
+ if (hdr->evt != r->event)
+ break;
+
+ r->rlen = MIN(len, r->rlen);
+ memcpy(r->rparam, ptr, r->rlen);
+ goto done;
+ }
+ }
+ errno = ETIMEDOUT;
+
+failed:
+ err = errno;
+ setsockopt(dd, SOL_HCI, HCI_FILTER, &of, sizeof(of));
+ errno = err;
+ return -1;
+
+done:
+ setsockopt(dd, SOL_HCI, HCI_FILTER, &of, sizeof(of));
+ return 0;
+}
+
+int hci_create_connection(int dd, const bdaddr_t *bdaddr, uint16_t ptype,
+ uint16_t clkoffset, uint8_t rswitch,
+ uint16_t *handle, int to)
+{
+ evt_conn_complete rp;
+ create_conn_cp cp;
+ struct hci_request rq;
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.bdaddr, bdaddr);
+ cp.pkt_type = ptype;
+ cp.pscan_rep_mode = 0x02;
+ cp.clock_offset = clkoffset;
+ cp.role_switch = rswitch;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_LINK_CTL;
+ rq.ocf = OCF_CREATE_CONN;
+ rq.event = EVT_CONN_COMPLETE;
+ rq.cparam = &cp;
+ rq.clen = CREATE_CONN_CP_SIZE;
+ rq.rparam = &rp;
+ rq.rlen = EVT_CONN_COMPLETE_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ *handle = rp.handle;
+ return 0;
+}
+
+int hci_disconnect(int dd, uint16_t handle, uint8_t reason, int to)
+{
+ evt_disconn_complete rp;
+ disconnect_cp cp;
+ struct hci_request rq;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.handle = handle;
+ cp.reason = reason;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_LINK_CTL;
+ rq.ocf = OCF_DISCONNECT;
+ rq.event = EVT_DISCONN_COMPLETE;
+ rq.cparam = &cp;
+ rq.clen = DISCONNECT_CP_SIZE;
+ rq.rparam = &rp;
+ rq.rlen = EVT_DISCONN_COMPLETE_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+ return 0;
+}
+
+int hci_le_add_white_list(int dd, const bdaddr_t *bdaddr, uint8_t type, int to)
+{
+ struct hci_request rq;
+ le_add_device_to_white_list_cp cp;
+ uint8_t status;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.bdaddr_type = type;
+ bacpy(&cp.bdaddr, bdaddr);
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_LE_CTL;
+ rq.ocf = OCF_LE_ADD_DEVICE_TO_WHITE_LIST;
+ rq.cparam = &cp;
+ rq.clen = LE_ADD_DEVICE_TO_WHITE_LIST_CP_SIZE;
+ rq.rparam = &status;
+ rq.rlen = 1;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (status) {
+ errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
+
+int hci_le_rm_white_list(int dd, const bdaddr_t *bdaddr, uint8_t type, int to)
+{
+ struct hci_request rq;
+ le_remove_device_from_white_list_cp cp;
+ uint8_t status;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.bdaddr_type = type;
+ bacpy(&cp.bdaddr, bdaddr);
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_LE_CTL;
+ rq.ocf = OCF_LE_REMOVE_DEVICE_FROM_WHITE_LIST;
+ rq.cparam = &cp;
+ rq.clen = LE_REMOVE_DEVICE_FROM_WHITE_LIST_CP_SIZE;
+ rq.rparam = &status;
+ rq.rlen = 1;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (status) {
+ errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
+
+int hci_le_read_white_list_size(int dd, uint8_t *size, int to)
+{
+ struct hci_request rq;
+ le_read_white_list_size_rp rp;
+
+ memset(&rp, 0, sizeof(rp));
+ memset(&rq, 0, sizeof(rq));
+
+ rq.ogf = OGF_LE_CTL;
+ rq.ocf = OCF_LE_READ_WHITE_LIST_SIZE;
+ rq.rparam = &rp;
+ rq.rlen = LE_READ_WHITE_LIST_SIZE_RP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ if (size)
+ *size = rp.size;
+
+ return 0;
+}
+
+int hci_le_clear_white_list(int dd, int to)
+{
+ struct hci_request rq;
+ uint8_t status;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_LE_CTL;
+ rq.ocf = OCF_LE_CLEAR_WHITE_LIST;
+ rq.rparam = &status;
+ rq.rlen = 1;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (status) {
+ errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
+
+int hci_read_local_name(int dd, int len, char *name, int to)
+{
+ read_local_name_rp rp;
+ struct hci_request rq;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_READ_LOCAL_NAME;
+ rq.rparam = &rp;
+ rq.rlen = READ_LOCAL_NAME_RP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ rp.name[247] = '\0';
+ strncpy(name, (char *) rp.name, len);
+ return 0;
+}
+
+int hci_write_local_name(int dd, const char *name, int to)
+{
+ change_local_name_cp cp;
+ struct hci_request rq;
+
+ memset(&cp, 0, sizeof(cp));
+ strncpy((char *) cp.name, name, sizeof(cp.name));
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_CHANGE_LOCAL_NAME;
+ rq.cparam = &cp;
+ rq.clen = CHANGE_LOCAL_NAME_CP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ return 0;
+}
+
+int hci_read_remote_name_with_clock_offset(int dd, const bdaddr_t *bdaddr,
+ uint8_t pscan_rep_mode,
+ uint16_t clkoffset,
+ int len, char *name, int to)
+{
+ evt_remote_name_req_complete rn;
+ remote_name_req_cp cp;
+ struct hci_request rq;
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.bdaddr, bdaddr);
+ cp.pscan_rep_mode = pscan_rep_mode;
+ cp.clock_offset = clkoffset;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_LINK_CTL;
+ rq.ocf = OCF_REMOTE_NAME_REQ;
+ rq.cparam = &cp;
+ rq.clen = REMOTE_NAME_REQ_CP_SIZE;
+ rq.event = EVT_REMOTE_NAME_REQ_COMPLETE;
+ rq.rparam = &rn;
+ rq.rlen = EVT_REMOTE_NAME_REQ_COMPLETE_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rn.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ rn.name[247] = '\0';
+ strncpy(name, (char *) rn.name, len);
+ return 0;
+}
+
+int hci_read_remote_name(int dd, const bdaddr_t *bdaddr, int len, char *name,
+ int to)
+{
+ return hci_read_remote_name_with_clock_offset(dd, bdaddr, 0x02, 0x0000,
+ len, name, to);
+}
+
+int hci_read_remote_name_cancel(int dd, const bdaddr_t *bdaddr, int to)
+{
+ remote_name_req_cancel_cp cp;
+ struct hci_request rq;
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.bdaddr, bdaddr);
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_LINK_CTL;
+ rq.ocf = OCF_REMOTE_NAME_REQ_CANCEL;
+ rq.cparam = &cp;
+ rq.clen = REMOTE_NAME_REQ_CANCEL_CP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ return 0;
+}
+
+int hci_read_remote_version(int dd, uint16_t handle, struct hci_version *ver,
+ int to)
+{
+ evt_read_remote_version_complete rp;
+ read_remote_version_cp cp;
+ struct hci_request rq;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.handle = handle;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_LINK_CTL;
+ rq.ocf = OCF_READ_REMOTE_VERSION;
+ rq.event = EVT_READ_REMOTE_VERSION_COMPLETE;
+ rq.cparam = &cp;
+ rq.clen = READ_REMOTE_VERSION_CP_SIZE;
+ rq.rparam = &rp;
+ rq.rlen = EVT_READ_REMOTE_VERSION_COMPLETE_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ ver->manufacturer = btohs(rp.manufacturer);
+ ver->lmp_ver = rp.lmp_ver;
+ ver->lmp_subver = btohs(rp.lmp_subver);
+ return 0;
+}
+
+int hci_read_remote_features(int dd, uint16_t handle, uint8_t *features, int to)
+{
+ evt_read_remote_features_complete rp;
+ read_remote_features_cp cp;
+ struct hci_request rq;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.handle = handle;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_LINK_CTL;
+ rq.ocf = OCF_READ_REMOTE_FEATURES;
+ rq.event = EVT_READ_REMOTE_FEATURES_COMPLETE;
+ rq.cparam = &cp;
+ rq.clen = READ_REMOTE_FEATURES_CP_SIZE;
+ rq.rparam = &rp;
+ rq.rlen = EVT_READ_REMOTE_FEATURES_COMPLETE_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ if (features)
+ memcpy(features, rp.features, 8);
+
+ return 0;
+}
+
+int hci_read_remote_ext_features(int dd, uint16_t handle, uint8_t page,
+ uint8_t *max_page, uint8_t *features,
+ int to)
+{
+ evt_read_remote_ext_features_complete rp;
+ read_remote_ext_features_cp cp;
+ struct hci_request rq;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.handle = handle;
+ cp.page_num = page;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_LINK_CTL;
+ rq.ocf = OCF_READ_REMOTE_EXT_FEATURES;
+ rq.event = EVT_READ_REMOTE_EXT_FEATURES_COMPLETE;
+ rq.cparam = &cp;
+ rq.clen = READ_REMOTE_EXT_FEATURES_CP_SIZE;
+ rq.rparam = &rp;
+ rq.rlen = EVT_READ_REMOTE_EXT_FEATURES_COMPLETE_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ if (max_page)
+ *max_page = rp.max_page_num;
+
+ if (features)
+ memcpy(features, rp.features, 8);
+
+ return 0;
+}
+
+int hci_read_clock_offset(int dd, uint16_t handle, uint16_t *clkoffset, int to)
+{
+ evt_read_clock_offset_complete rp;
+ read_clock_offset_cp cp;
+ struct hci_request rq;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.handle = handle;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_LINK_CTL;
+ rq.ocf = OCF_READ_CLOCK_OFFSET;
+ rq.event = EVT_READ_CLOCK_OFFSET_COMPLETE;
+ rq.cparam = &cp;
+ rq.clen = READ_CLOCK_OFFSET_CP_SIZE;
+ rq.rparam = &rp;
+ rq.rlen = EVT_READ_CLOCK_OFFSET_COMPLETE_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ *clkoffset = rp.clock_offset;
+ return 0;
+}
+
+int hci_read_local_version(int dd, struct hci_version *ver, int to)
+{
+ read_local_version_rp rp;
+ struct hci_request rq;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_INFO_PARAM;
+ rq.ocf = OCF_READ_LOCAL_VERSION;
+ rq.rparam = &rp;
+ rq.rlen = READ_LOCAL_VERSION_RP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ ver->manufacturer = btohs(rp.manufacturer);
+ ver->hci_ver = rp.hci_ver;
+ ver->hci_rev = btohs(rp.hci_rev);
+ ver->lmp_ver = rp.lmp_ver;
+ ver->lmp_subver = btohs(rp.lmp_subver);
+ return 0;
+}
+
+int hci_read_local_commands(int dd, uint8_t *commands, int to)
+{
+ read_local_commands_rp rp;
+ struct hci_request rq;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_INFO_PARAM;
+ rq.ocf = OCF_READ_LOCAL_COMMANDS;
+ rq.rparam = &rp;
+ rq.rlen = READ_LOCAL_COMMANDS_RP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ if (commands)
+ memcpy(commands, rp.commands, 64);
+
+ return 0;
+}
+
+int hci_read_local_features(int dd, uint8_t *features, int to)
+{
+ read_local_features_rp rp;
+ struct hci_request rq;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_INFO_PARAM;
+ rq.ocf = OCF_READ_LOCAL_FEATURES;
+ rq.rparam = &rp;
+ rq.rlen = READ_LOCAL_FEATURES_RP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ if (features)
+ memcpy(features, rp.features, 8);
+
+ return 0;
+}
+
+int hci_read_local_ext_features(int dd, uint8_t page, uint8_t *max_page,
+ uint8_t *features, int to)
+{
+ read_local_ext_features_cp cp;
+ read_local_ext_features_rp rp;
+ struct hci_request rq;
+
+ cp.page_num = page;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_INFO_PARAM;
+ rq.ocf = OCF_READ_LOCAL_EXT_FEATURES;
+ rq.cparam = &cp;
+ rq.clen = READ_LOCAL_EXT_FEATURES_CP_SIZE;
+ rq.rparam = &rp;
+ rq.rlen = READ_LOCAL_EXT_FEATURES_RP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ if (max_page)
+ *max_page = rp.max_page_num;
+
+ if (features)
+ memcpy(features, rp.features, 8);
+
+ return 0;
+}
+
+int hci_read_bd_addr(int dd, bdaddr_t *bdaddr, int to)
+{
+ read_bd_addr_rp rp;
+ struct hci_request rq;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_INFO_PARAM;
+ rq.ocf = OCF_READ_BD_ADDR;
+ rq.rparam = &rp;
+ rq.rlen = READ_BD_ADDR_RP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ if (bdaddr)
+ bacpy(bdaddr, &rp.bdaddr);
+
+ return 0;
+}
+
+int hci_read_class_of_dev(int dd, uint8_t *cls, int to)
+{
+ read_class_of_dev_rp rp;
+ struct hci_request rq;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_READ_CLASS_OF_DEV;
+ rq.rparam = &rp;
+ rq.rlen = READ_CLASS_OF_DEV_RP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ memcpy(cls, rp.dev_class, 3);
+ return 0;
+}
+
+int hci_write_class_of_dev(int dd, uint32_t cls, int to)
+{
+ write_class_of_dev_cp cp;
+ struct hci_request rq;
+
+ memset(&rq, 0, sizeof(rq));
+ cp.dev_class[0] = cls & 0xff;
+ cp.dev_class[1] = (cls >> 8) & 0xff;
+ cp.dev_class[2] = (cls >> 16) & 0xff;
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_WRITE_CLASS_OF_DEV;
+ rq.cparam = &cp;
+ rq.clen = WRITE_CLASS_OF_DEV_CP_SIZE;
+ return hci_send_req(dd, &rq, to);
+}
+
+int hci_read_voice_setting(int dd, uint16_t *vs, int to)
+{
+ read_voice_setting_rp rp;
+ struct hci_request rq;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_READ_VOICE_SETTING;
+ rq.rparam = &rp;
+ rq.rlen = READ_VOICE_SETTING_RP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ *vs = rp.voice_setting;
+ return 0;
+}
+
+int hci_write_voice_setting(int dd, uint16_t vs, int to)
+{
+ write_voice_setting_cp cp;
+ struct hci_request rq;
+
+ memset(&rq, 0, sizeof(rq));
+ cp.voice_setting = vs;
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_WRITE_VOICE_SETTING;
+ rq.cparam = &cp;
+ rq.clen = WRITE_VOICE_SETTING_CP_SIZE;
+
+ return hci_send_req(dd, &rq, to);
+}
+
+int hci_read_current_iac_lap(int dd, uint8_t *num_iac, uint8_t *lap, int to)
+{
+ read_current_iac_lap_rp rp;
+ struct hci_request rq;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_READ_CURRENT_IAC_LAP;
+ rq.rparam = &rp;
+ rq.rlen = READ_CURRENT_IAC_LAP_RP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ *num_iac = rp.num_current_iac;
+ memcpy(lap, rp.lap, rp.num_current_iac * 3);
+ return 0;
+}
+
+int hci_write_current_iac_lap(int dd, uint8_t num_iac, uint8_t *lap, int to)
+{
+ write_current_iac_lap_cp cp;
+ struct hci_request rq;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.num_current_iac = num_iac;
+ memcpy(&cp.lap, lap, num_iac * 3);
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_WRITE_CURRENT_IAC_LAP;
+ rq.cparam = &cp;
+ rq.clen = num_iac * 3 + 1;
+
+ return hci_send_req(dd, &rq, to);
+}
+
+int hci_read_stored_link_key(int dd, bdaddr_t *bdaddr, uint8_t all, int to)
+{
+ read_stored_link_key_cp cp;
+ struct hci_request rq;
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.bdaddr, bdaddr);
+ cp.read_all = all;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_READ_STORED_LINK_KEY;
+ rq.cparam = &cp;
+ rq.clen = READ_STORED_LINK_KEY_CP_SIZE;
+
+ return hci_send_req(dd, &rq, to);
+}
+
+int hci_write_stored_link_key(int dd, bdaddr_t *bdaddr, uint8_t *key, int to)
+{
+ unsigned char cp[WRITE_STORED_LINK_KEY_CP_SIZE + 6 + 16];
+ struct hci_request rq;
+
+ memset(&cp, 0, sizeof(cp));
+ cp[0] = 1;
+ bacpy((bdaddr_t *) (cp + 1), bdaddr);
+ memcpy(cp + 7, key, 16);
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_WRITE_STORED_LINK_KEY;
+ rq.cparam = &cp;
+ rq.clen = WRITE_STORED_LINK_KEY_CP_SIZE + 6 + 16;
+
+ return hci_send_req(dd, &rq, to);
+}
+
+int hci_delete_stored_link_key(int dd, bdaddr_t *bdaddr, uint8_t all, int to)
+{
+ delete_stored_link_key_cp cp;
+ struct hci_request rq;
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.bdaddr, bdaddr);
+ cp.delete_all = all;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_DELETE_STORED_LINK_KEY;
+ rq.cparam = &cp;
+ rq.clen = DELETE_STORED_LINK_KEY_CP_SIZE;
+
+ return hci_send_req(dd, &rq, to);
+}
+
+int hci_authenticate_link(int dd, uint16_t handle, int to)
+{
+ auth_requested_cp cp;
+ evt_auth_complete rp;
+ struct hci_request rq;
+
+ cp.handle = handle;
+
+ rq.ogf = OGF_LINK_CTL;
+ rq.ocf = OCF_AUTH_REQUESTED;
+ rq.event = EVT_AUTH_COMPLETE;
+ rq.cparam = &cp;
+ rq.clen = AUTH_REQUESTED_CP_SIZE;
+ rq.rparam = &rp;
+ rq.rlen = EVT_AUTH_COMPLETE_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
+
+int hci_encrypt_link(int dd, uint16_t handle, uint8_t encrypt, int to)
+{
+ set_conn_encrypt_cp cp;
+ evt_encrypt_change rp;
+ struct hci_request rq;
+
+ cp.handle = handle;
+ cp.encrypt = encrypt;
+
+ rq.ogf = OGF_LINK_CTL;
+ rq.ocf = OCF_SET_CONN_ENCRYPT;
+ rq.event = EVT_ENCRYPT_CHANGE;
+ rq.cparam = &cp;
+ rq.clen = SET_CONN_ENCRYPT_CP_SIZE;
+ rq.rparam = &rp;
+ rq.rlen = EVT_ENCRYPT_CHANGE_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
+
+int hci_change_link_key(int dd, uint16_t handle, int to)
+{
+ change_conn_link_key_cp cp;
+ evt_change_conn_link_key_complete rp;
+ struct hci_request rq;
+
+ cp.handle = handle;
+
+ rq.ogf = OGF_LINK_CTL;
+ rq.ocf = OCF_CHANGE_CONN_LINK_KEY;
+ rq.event = EVT_CHANGE_CONN_LINK_KEY_COMPLETE;
+ rq.cparam = &cp;
+ rq.clen = CHANGE_CONN_LINK_KEY_CP_SIZE;
+ rq.rparam = &rp;
+ rq.rlen = EVT_CHANGE_CONN_LINK_KEY_COMPLETE_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
+
+int hci_switch_role(int dd, bdaddr_t *bdaddr, uint8_t role, int to)
+{
+ switch_role_cp cp;
+ evt_role_change rp;
+ struct hci_request rq;
+
+ bacpy(&cp.bdaddr, bdaddr);
+ cp.role = role;
+ rq.ogf = OGF_LINK_POLICY;
+ rq.ocf = OCF_SWITCH_ROLE;
+ rq.cparam = &cp;
+ rq.clen = SWITCH_ROLE_CP_SIZE;
+ rq.rparam = &rp;
+ rq.rlen = EVT_ROLE_CHANGE_SIZE;
+ rq.event = EVT_ROLE_CHANGE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
+
+int hci_park_mode(int dd, uint16_t handle, uint16_t max_interval,
+ uint16_t min_interval, int to)
+{
+ park_mode_cp cp;
+ evt_mode_change rp;
+ struct hci_request rq;
+
+ memset(&cp, 0, sizeof (cp));
+ cp.handle = handle;
+ cp.max_interval = max_interval;
+ cp.min_interval = min_interval;
+
+ memset(&rq, 0, sizeof (rq));
+ rq.ogf = OGF_LINK_POLICY;
+ rq.ocf = OCF_PARK_MODE;
+ rq.event = EVT_MODE_CHANGE;
+ rq.cparam = &cp;
+ rq.clen = PARK_MODE_CP_SIZE;
+ rq.rparam = &rp;
+ rq.rlen = EVT_MODE_CHANGE_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
+
+int hci_exit_park_mode(int dd, uint16_t handle, int to)
+{
+ exit_park_mode_cp cp;
+ evt_mode_change rp;
+ struct hci_request rq;
+
+ memset(&cp, 0, sizeof (cp));
+ cp.handle = handle;
+
+ memset (&rq, 0, sizeof (rq));
+ rq.ogf = OGF_LINK_POLICY;
+ rq.ocf = OCF_EXIT_PARK_MODE;
+ rq.event = EVT_MODE_CHANGE;
+ rq.cparam = &cp;
+ rq.clen = EXIT_PARK_MODE_CP_SIZE;
+ rq.rparam = &rp;
+ rq.rlen = EVT_MODE_CHANGE_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
+
+int hci_read_inquiry_scan_type(int dd, uint8_t *type, int to)
+{
+ read_inquiry_scan_type_rp rp;
+ struct hci_request rq;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_READ_INQUIRY_SCAN_TYPE;
+ rq.rparam = &rp;
+ rq.rlen = READ_INQUIRY_SCAN_TYPE_RP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ *type = rp.type;
+ return 0;
+}
+
+int hci_write_inquiry_scan_type(int dd, uint8_t type, int to)
+{
+ write_inquiry_scan_type_cp cp;
+ write_inquiry_scan_type_rp rp;
+ struct hci_request rq;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.type = type;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_WRITE_INQUIRY_SCAN_TYPE;
+ rq.cparam = &cp;
+ rq.clen = WRITE_INQUIRY_SCAN_TYPE_CP_SIZE;
+ rq.rparam = &rp;
+ rq.rlen = WRITE_INQUIRY_SCAN_TYPE_RP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
+
+int hci_read_inquiry_mode(int dd, uint8_t *mode, int to)
+{
+ read_inquiry_mode_rp rp;
+ struct hci_request rq;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_READ_INQUIRY_MODE;
+ rq.rparam = &rp;
+ rq.rlen = READ_INQUIRY_MODE_RP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ *mode = rp.mode;
+ return 0;
+}
+
+int hci_write_inquiry_mode(int dd, uint8_t mode, int to)
+{
+ write_inquiry_mode_cp cp;
+ write_inquiry_mode_rp rp;
+ struct hci_request rq;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.mode = mode;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_WRITE_INQUIRY_MODE;
+ rq.cparam = &cp;
+ rq.clen = WRITE_INQUIRY_MODE_CP_SIZE;
+ rq.rparam = &rp;
+ rq.rlen = WRITE_INQUIRY_MODE_RP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
+
+int hci_read_afh_mode(int dd, uint8_t *mode, int to)
+{
+ read_afh_mode_rp rp;
+ struct hci_request rq;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_READ_AFH_MODE;
+ rq.rparam = &rp;
+ rq.rlen = READ_AFH_MODE_RP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ *mode = rp.mode;
+ return 0;
+}
+
+int hci_write_afh_mode(int dd, uint8_t mode, int to)
+{
+ write_afh_mode_cp cp;
+ write_afh_mode_rp rp;
+ struct hci_request rq;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.mode = mode;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_WRITE_AFH_MODE;
+ rq.cparam = &cp;
+ rq.clen = WRITE_AFH_MODE_CP_SIZE;
+ rq.rparam = &rp;
+ rq.rlen = WRITE_AFH_MODE_RP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
+
+int hci_read_ext_inquiry_response(int dd, uint8_t *fec, uint8_t *data, int to)
+{
+ read_ext_inquiry_response_rp rp;
+ struct hci_request rq;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_READ_EXT_INQUIRY_RESPONSE;
+ rq.rparam = &rp;
+ rq.rlen = READ_EXT_INQUIRY_RESPONSE_RP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ *fec = rp.fec;
+ memcpy(data, rp.data, 240);
+
+ return 0;
+}
+
+int hci_write_ext_inquiry_response(int dd, uint8_t fec, uint8_t *data, int to)
+{
+ write_ext_inquiry_response_cp cp;
+ write_ext_inquiry_response_rp rp;
+ struct hci_request rq;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.fec = fec;
+ memcpy(cp.data, data, 240);
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_WRITE_EXT_INQUIRY_RESPONSE;
+ rq.cparam = &cp;
+ rq.clen = WRITE_EXT_INQUIRY_RESPONSE_CP_SIZE;
+ rq.rparam = &rp;
+ rq.rlen = WRITE_EXT_INQUIRY_RESPONSE_RP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
+
+int hci_read_simple_pairing_mode(int dd, uint8_t *mode, int to)
+{
+ read_simple_pairing_mode_rp rp;
+ struct hci_request rq;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_READ_SIMPLE_PAIRING_MODE;
+ rq.rparam = &rp;
+ rq.rlen = READ_SIMPLE_PAIRING_MODE_RP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ *mode = rp.mode;
+ return 0;
+}
+
+int hci_write_simple_pairing_mode(int dd, uint8_t mode, int to)
+{
+ write_simple_pairing_mode_cp cp;
+ write_simple_pairing_mode_rp rp;
+ struct hci_request rq;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.mode = mode;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_WRITE_SIMPLE_PAIRING_MODE;
+ rq.cparam = &cp;
+ rq.clen = WRITE_SIMPLE_PAIRING_MODE_CP_SIZE;
+ rq.rparam = &rp;
+ rq.rlen = WRITE_SIMPLE_PAIRING_MODE_RP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
+
+int hci_read_local_oob_data(int dd, uint8_t *hash, uint8_t *randomizer, int to)
+{
+ read_local_oob_data_rp rp;
+ struct hci_request rq;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_READ_LOCAL_OOB_DATA;
+ rq.rparam = &rp;
+ rq.rlen = READ_LOCAL_OOB_DATA_RP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ memcpy(hash, rp.hash, 16);
+ memcpy(randomizer, rp.randomizer, 16);
+ return 0;
+}
+
+int hci_read_inq_response_tx_power_level(int dd, int8_t *level, int to)
+{
+ read_inq_response_tx_power_level_rp rp;
+ struct hci_request rq;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_READ_INQ_RESPONSE_TX_POWER_LEVEL;
+ rq.rparam = &rp;
+ rq.rlen = READ_INQ_RESPONSE_TX_POWER_LEVEL_RP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ *level = rp.level;
+ return 0;
+}
+
+int hci_read_inquiry_transmit_power_level(int dd, int8_t *level, int to)
+{
+ return hci_read_inq_response_tx_power_level(dd, level, to);
+}
+
+int hci_write_inquiry_transmit_power_level(int dd, int8_t level, int to)
+{
+ write_inquiry_transmit_power_level_cp cp;
+ write_inquiry_transmit_power_level_rp rp;
+ struct hci_request rq;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.level = level;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_WRITE_INQUIRY_TRANSMIT_POWER_LEVEL;
+ rq.cparam = &cp;
+ rq.clen = WRITE_INQUIRY_TRANSMIT_POWER_LEVEL_CP_SIZE;
+ rq.rparam = &rp;
+ rq.rlen = WRITE_INQUIRY_TRANSMIT_POWER_LEVEL_RP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
+
+int hci_read_transmit_power_level(int dd, uint16_t handle, uint8_t type,
+ int8_t *level, int to)
+{
+ read_transmit_power_level_cp cp;
+ read_transmit_power_level_rp rp;
+ struct hci_request rq;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.handle = handle;
+ cp.type = type;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_READ_TRANSMIT_POWER_LEVEL;
+ rq.cparam = &cp;
+ rq.clen = READ_TRANSMIT_POWER_LEVEL_CP_SIZE;
+ rq.rparam = &rp;
+ rq.rlen = READ_TRANSMIT_POWER_LEVEL_RP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ *level = rp.level;
+ return 0;
+}
+
+int hci_read_link_policy(int dd, uint16_t handle, uint16_t *policy, int to)
+{
+ read_link_policy_rp rp;
+ struct hci_request rq;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_LINK_POLICY;
+ rq.ocf = OCF_READ_LINK_POLICY;
+ rq.cparam = &handle;
+ rq.clen = 2;
+ rq.rparam = &rp;
+ rq.rlen = READ_LINK_POLICY_RP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ *policy = rp.policy;
+ return 0;
+}
+
+int hci_write_link_policy(int dd, uint16_t handle, uint16_t policy, int to)
+{
+ write_link_policy_cp cp;
+ write_link_policy_rp rp;
+ struct hci_request rq;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.handle = handle;
+ cp.policy = policy;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_LINK_POLICY;
+ rq.ocf = OCF_WRITE_LINK_POLICY;
+ rq.cparam = &cp;
+ rq.clen = WRITE_LINK_POLICY_CP_SIZE;
+ rq.rparam = &rp;
+ rq.rlen = WRITE_LINK_POLICY_RP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
+
+int hci_read_link_supervision_timeout(int dd, uint16_t handle,
+ uint16_t *timeout, int to)
+{
+ read_link_supervision_timeout_rp rp;
+ struct hci_request rq;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_READ_LINK_SUPERVISION_TIMEOUT;
+ rq.cparam = &handle;
+ rq.clen = 2;
+ rq.rparam = &rp;
+ rq.rlen = READ_LINK_SUPERVISION_TIMEOUT_RP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ *timeout = rp.timeout;
+ return 0;
+}
+
+int hci_write_link_supervision_timeout(int dd, uint16_t handle,
+ uint16_t timeout, int to)
+{
+ write_link_supervision_timeout_cp cp;
+ write_link_supervision_timeout_rp rp;
+ struct hci_request rq;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.handle = handle;
+ cp.timeout = timeout;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_WRITE_LINK_SUPERVISION_TIMEOUT;
+ rq.cparam = &cp;
+ rq.clen = WRITE_LINK_SUPERVISION_TIMEOUT_CP_SIZE;
+ rq.rparam = &rp;
+ rq.rlen = WRITE_LINK_SUPERVISION_TIMEOUT_RP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
+
+int hci_set_afh_classification(int dd, uint8_t *map, int to)
+{
+ set_afh_classification_cp cp;
+ set_afh_classification_rp rp;
+ struct hci_request rq;
+
+ memset(&cp, 0, sizeof(cp));
+ memcpy(cp.map, map, 10);
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_SET_AFH_CLASSIFICATION;
+ rq.cparam = &cp;
+ rq.clen = SET_AFH_CLASSIFICATION_CP_SIZE;
+ rq.rparam = &rp;
+ rq.rlen = SET_AFH_CLASSIFICATION_RP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
+
+int hci_read_link_quality(int dd, uint16_t handle, uint8_t *link_quality,
+ int to)
+{
+ read_link_quality_rp rp;
+ struct hci_request rq;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_STATUS_PARAM;
+ rq.ocf = OCF_READ_LINK_QUALITY;
+ rq.cparam = &handle;
+ rq.clen = 2;
+ rq.rparam = &rp;
+ rq.rlen = READ_LINK_QUALITY_RP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ *link_quality = rp.link_quality;
+ return 0;
+}
+
+int hci_read_rssi(int dd, uint16_t handle, int8_t *rssi, int to)
+{
+ read_rssi_rp rp;
+ struct hci_request rq;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_STATUS_PARAM;
+ rq.ocf = OCF_READ_RSSI;
+ rq.cparam = &handle;
+ rq.clen = 2;
+ rq.rparam = &rp;
+ rq.rlen = READ_RSSI_RP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ *rssi = rp.rssi;
+ return 0;
+}
+
+int hci_read_afh_map(int dd, uint16_t handle, uint8_t *mode, uint8_t *map,
+ int to)
+{
+ read_afh_map_rp rp;
+ struct hci_request rq;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_STATUS_PARAM;
+ rq.ocf = OCF_READ_AFH_MAP;
+ rq.cparam = &handle;
+ rq.clen = 2;
+ rq.rparam = &rp;
+ rq.rlen = READ_AFH_MAP_RP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ *mode = rp.mode;
+ memcpy(map, rp.map, 10);
+ return 0;
+}
+
+int hci_read_clock(int dd, uint16_t handle, uint8_t which, uint32_t *clock,
+ uint16_t *accuracy, int to)
+{
+ read_clock_cp cp;
+ read_clock_rp rp;
+ struct hci_request rq;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.handle = handle;
+ cp.which_clock = which;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_STATUS_PARAM;
+ rq.ocf = OCF_READ_CLOCK;
+ rq.cparam = &cp;
+ rq.clen = READ_CLOCK_CP_SIZE;
+ rq.rparam = &rp;
+ rq.rlen = READ_CLOCK_RP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ *clock = rp.clock;
+ *accuracy = rp.accuracy;
+ return 0;
+}
+
+int hci_le_set_scan_enable(int dd, uint8_t enable, uint8_t filter_dup, int to)
+{
+ struct hci_request rq;
+ le_set_scan_enable_cp scan_cp;
+ uint8_t status;
+
+ memset(&scan_cp, 0, sizeof(scan_cp));
+ scan_cp.enable = enable;
+ scan_cp.filter_dup = filter_dup;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_LE_CTL;
+ rq.ocf = OCF_LE_SET_SCAN_ENABLE;
+ rq.cparam = &scan_cp;
+ rq.clen = LE_SET_SCAN_ENABLE_CP_SIZE;
+ rq.rparam = &status;
+ rq.rlen = 1;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (status) {
+ errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
+
+int hci_le_set_scan_parameters(int dd, uint8_t type,
+ uint16_t interval, uint16_t window,
+ uint8_t own_type, uint8_t filter, int to)
+{
+ struct hci_request rq;
+ le_set_scan_parameters_cp param_cp;
+ uint8_t status;
+
+ memset(&param_cp, 0, sizeof(param_cp));
+ param_cp.type = type;
+ param_cp.interval = interval;
+ param_cp.window = window;
+ param_cp.own_bdaddr_type = own_type;
+ param_cp.filter = filter;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_LE_CTL;
+ rq.ocf = OCF_LE_SET_SCAN_PARAMETERS;
+ rq.cparam = &param_cp;
+ rq.clen = LE_SET_SCAN_PARAMETERS_CP_SIZE;
+ rq.rparam = &status;
+ rq.rlen = 1;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (status) {
+ errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
+
+int hci_le_set_advertise_enable(int dd, uint8_t enable, int to)
+{
+ struct hci_request rq;
+ le_set_advertise_enable_cp adv_cp;
+ uint8_t status;
+
+ memset(&adv_cp, 0, sizeof(adv_cp));
+ adv_cp.enable = enable;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_LE_CTL;
+ rq.ocf = OCF_LE_SET_ADVERTISE_ENABLE;
+ rq.cparam = &adv_cp;
+ rq.clen = LE_SET_ADVERTISE_ENABLE_CP_SIZE;
+ rq.rparam = &status;
+ rq.rlen = 1;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (status) {
+ errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
+
+int hci_le_create_conn(int dd, uint16_t interval, uint16_t window,
+ uint8_t initiator_filter, uint8_t peer_bdaddr_type,
+ bdaddr_t peer_bdaddr, uint8_t own_bdaddr_type,
+ uint16_t min_interval, uint16_t max_interval,
+ uint16_t latency, uint16_t supervision_timeout,
+ uint16_t min_ce_length, uint16_t max_ce_length,
+ uint16_t *handle, int to)
+{
+ struct hci_request rq;
+ le_create_connection_cp create_conn_cp;
+ evt_le_connection_complete conn_complete_rp;
+
+ memset(&create_conn_cp, 0, sizeof(create_conn_cp));
+ create_conn_cp.interval = interval;
+ create_conn_cp.window = window;
+ create_conn_cp.initiator_filter = initiator_filter;
+ create_conn_cp.peer_bdaddr_type = peer_bdaddr_type;
+ create_conn_cp.peer_bdaddr = peer_bdaddr;
+ create_conn_cp.own_bdaddr_type = own_bdaddr_type;
+ create_conn_cp.min_interval = min_interval;
+ create_conn_cp.max_interval = max_interval;
+ create_conn_cp.latency = latency;
+ create_conn_cp.supervision_timeout = supervision_timeout;
+ create_conn_cp.min_ce_length = min_ce_length;
+ create_conn_cp.max_ce_length = max_ce_length;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_LE_CTL;
+ rq.ocf = OCF_LE_CREATE_CONN;
+ rq.event = EVT_LE_CONN_COMPLETE;
+ rq.cparam = &create_conn_cp;
+ rq.clen = LE_CREATE_CONN_CP_SIZE;
+ rq.rparam = &conn_complete_rp;
+ rq.rlen = EVT_CONN_COMPLETE_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (conn_complete_rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ if (handle)
+ *handle = conn_complete_rp.handle;
+
+ return 0;
+}
+
+int hci_le_conn_update(int dd, uint16_t handle, uint16_t min_interval,
+ uint16_t max_interval, uint16_t latency,
+ uint16_t supervision_timeout, int to)
+{
+ evt_le_connection_update_complete evt;
+ le_connection_update_cp cp;
+ struct hci_request rq;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.handle = handle;
+ cp.min_interval = min_interval;
+ cp.max_interval = max_interval;
+ cp.latency = latency;
+ cp.supervision_timeout = supervision_timeout;
+ cp.min_ce_length = htobs(0x0001);
+ cp.max_ce_length = htobs(0x0001);
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_LE_CTL;
+ rq.ocf = OCF_LE_CONN_UPDATE;
+ rq.cparam = &cp;
+ rq.clen = LE_CONN_UPDATE_CP_SIZE;
+ rq.event = EVT_LE_CONN_UPDATE_COMPLETE;
+ rq.rparam = &evt;
+ rq.rlen = sizeof(evt);
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (evt.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/lib/hci.h b/lib/hci.h
new file mode 100644
index 0000000..d2d4f77
--- /dev/null
+++ b/lib/hci.h
@@ -0,0 +1,2367 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2000-2001 Qualcomm Incorporated
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 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
+ *
+ */
+
+#ifndef __HCI_H
+#define __HCI_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/socket.h>
+
+#define HCI_MAX_DEV 16
+
+#define HCI_MAX_ACL_SIZE 1024
+#define HCI_MAX_SCO_SIZE 255
+#define HCI_MAX_EVENT_SIZE 260
+#define HCI_MAX_FRAME_SIZE (HCI_MAX_ACL_SIZE + 4)
+
+/* HCI dev events */
+#define HCI_DEV_REG 1
+#define HCI_DEV_UNREG 2
+#define HCI_DEV_UP 3
+#define HCI_DEV_DOWN 4
+#define HCI_DEV_SUSPEND 5
+#define HCI_DEV_RESUME 6
+
+/* HCI bus types */
+#define HCI_VIRTUAL 0
+#define HCI_USB 1
+#define HCI_PCCARD 2
+#define HCI_UART 3
+#define HCI_RS232 4
+#define HCI_PCI 5
+#define HCI_SDIO 6
+
+/* HCI controller types */
+#define HCI_BREDR 0x00
+#define HCI_AMP 0x01
+
+/* HCI device flags */
+enum {
+ HCI_UP,
+ HCI_INIT,
+ HCI_RUNNING,
+
+ HCI_PSCAN,
+ HCI_ISCAN,
+ HCI_AUTH,
+ HCI_ENCRYPT,
+ HCI_INQUIRY,
+
+ HCI_RAW,
+};
+
+/* LE address type */
+enum {
+ LE_PUBLIC_ADDRESS = 0x00,
+ LE_RANDOM_ADDRESS = 0x01
+};
+
+/* HCI ioctl defines */
+#define HCIDEVUP _IOW('H', 201, int)
+#define HCIDEVDOWN _IOW('H', 202, int)
+#define HCIDEVRESET _IOW('H', 203, int)
+#define HCIDEVRESTAT _IOW('H', 204, int)
+
+#define HCIGETDEVLIST _IOR('H', 210, int)
+#define HCIGETDEVINFO _IOR('H', 211, int)
+#define HCIGETCONNLIST _IOR('H', 212, int)
+#define HCIGETCONNINFO _IOR('H', 213, int)
+#define HCIGETAUTHINFO _IOR('H', 215, int)
+
+#define HCISETRAW _IOW('H', 220, int)
+#define HCISETSCAN _IOW('H', 221, int)
+#define HCISETAUTH _IOW('H', 222, int)
+#define HCISETENCRYPT _IOW('H', 223, int)
+#define HCISETPTYPE _IOW('H', 224, int)
+#define HCISETLINKPOL _IOW('H', 225, int)
+#define HCISETLINKMODE _IOW('H', 226, int)
+#define HCISETACLMTU _IOW('H', 227, int)
+#define HCISETSCOMTU _IOW('H', 228, int)
+
+#define HCIBLOCKADDR _IOW('H', 230, int)
+#define HCIUNBLOCKADDR _IOW('H', 231, int)
+
+#define HCIINQUIRY _IOR('H', 240, int)
+
+#ifndef __NO_HCI_DEFS
+
+/* HCI Packet types */
+#define HCI_COMMAND_PKT 0x01
+#define HCI_ACLDATA_PKT 0x02
+#define HCI_SCODATA_PKT 0x03
+#define HCI_EVENT_PKT 0x04
+#define HCI_VENDOR_PKT 0xff
+
+/* HCI Packet types */
+#define HCI_2DH1 0x0002
+#define HCI_3DH1 0x0004
+#define HCI_DM1 0x0008
+#define HCI_DH1 0x0010
+#define HCI_2DH3 0x0100
+#define HCI_3DH3 0x0200
+#define HCI_DM3 0x0400
+#define HCI_DH3 0x0800
+#define HCI_2DH5 0x1000
+#define HCI_3DH5 0x2000
+#define HCI_DM5 0x4000
+#define HCI_DH5 0x8000
+
+#define HCI_HV1 0x0020
+#define HCI_HV2 0x0040
+#define HCI_HV3 0x0080
+
+#define HCI_EV3 0x0008
+#define HCI_EV4 0x0010
+#define HCI_EV5 0x0020
+#define HCI_2EV3 0x0040
+#define HCI_3EV3 0x0080
+#define HCI_2EV5 0x0100
+#define HCI_3EV5 0x0200
+
+#define SCO_PTYPE_MASK (HCI_HV1 | HCI_HV2 | HCI_HV3)
+#define ACL_PTYPE_MASK (HCI_DM1 | HCI_DH1 | HCI_DM3 | HCI_DH3 | HCI_DM5 | HCI_DH5)
+
+/* HCI Error codes */
+#define HCI_UNKNOWN_COMMAND 0x01
+#define HCI_NO_CONNECTION 0x02
+#define HCI_HARDWARE_FAILURE 0x03
+#define HCI_PAGE_TIMEOUT 0x04
+#define HCI_AUTHENTICATION_FAILURE 0x05
+#define HCI_PIN_OR_KEY_MISSING 0x06
+#define HCI_MEMORY_FULL 0x07
+#define HCI_CONNECTION_TIMEOUT 0x08
+#define HCI_MAX_NUMBER_OF_CONNECTIONS 0x09
+#define HCI_MAX_NUMBER_OF_SCO_CONNECTIONS 0x0a
+#define HCI_ACL_CONNECTION_EXISTS 0x0b
+#define HCI_COMMAND_DISALLOWED 0x0c
+#define HCI_REJECTED_LIMITED_RESOURCES 0x0d
+#define HCI_REJECTED_SECURITY 0x0e
+#define HCI_REJECTED_PERSONAL 0x0f
+#define HCI_HOST_TIMEOUT 0x10
+#define HCI_UNSUPPORTED_FEATURE 0x11
+#define HCI_INVALID_PARAMETERS 0x12
+#define HCI_OE_USER_ENDED_CONNECTION 0x13
+#define HCI_OE_LOW_RESOURCES 0x14
+#define HCI_OE_POWER_OFF 0x15
+#define HCI_CONNECTION_TERMINATED 0x16
+#define HCI_REPEATED_ATTEMPTS 0x17
+#define HCI_PAIRING_NOT_ALLOWED 0x18
+#define HCI_UNKNOWN_LMP_PDU 0x19
+#define HCI_UNSUPPORTED_REMOTE_FEATURE 0x1a
+#define HCI_SCO_OFFSET_REJECTED 0x1b
+#define HCI_SCO_INTERVAL_REJECTED 0x1c
+#define HCI_AIR_MODE_REJECTED 0x1d
+#define HCI_INVALID_LMP_PARAMETERS 0x1e
+#define HCI_UNSPECIFIED_ERROR 0x1f
+#define HCI_UNSUPPORTED_LMP_PARAMETER_VALUE 0x20
+#define HCI_ROLE_CHANGE_NOT_ALLOWED 0x21
+#define HCI_LMP_RESPONSE_TIMEOUT 0x22
+#define HCI_LMP_ERROR_TRANSACTION_COLLISION 0x23
+#define HCI_LMP_PDU_NOT_ALLOWED 0x24
+#define HCI_ENCRYPTION_MODE_NOT_ACCEPTED 0x25
+#define HCI_UNIT_LINK_KEY_USED 0x26
+#define HCI_QOS_NOT_SUPPORTED 0x27
+#define HCI_INSTANT_PASSED 0x28
+#define HCI_PAIRING_NOT_SUPPORTED 0x29
+#define HCI_TRANSACTION_COLLISION 0x2a
+#define HCI_QOS_UNACCEPTABLE_PARAMETER 0x2c
+#define HCI_QOS_REJECTED 0x2d
+#define HCI_CLASSIFICATION_NOT_SUPPORTED 0x2e
+#define HCI_INSUFFICIENT_SECURITY 0x2f
+#define HCI_PARAMETER_OUT_OF_RANGE 0x30
+#define HCI_ROLE_SWITCH_PENDING 0x32
+#define HCI_SLOT_VIOLATION 0x34
+#define HCI_ROLE_SWITCH_FAILED 0x35
+#define HCI_EIR_TOO_LARGE 0x36
+#define HCI_SIMPLE_PAIRING_NOT_SUPPORTED 0x37
+#define HCI_HOST_BUSY_PAIRING 0x38
+
+/* ACL flags */
+#define ACL_START_NO_FLUSH 0x00
+#define ACL_CONT 0x01
+#define ACL_START 0x02
+#define ACL_ACTIVE_BCAST 0x04
+#define ACL_PICO_BCAST 0x08
+
+/* Baseband links */
+#define SCO_LINK 0x00
+#define ACL_LINK 0x01
+#define ESCO_LINK 0x02
+
+/* LMP features */
+#define LMP_3SLOT 0x01
+#define LMP_5SLOT 0x02
+#define LMP_ENCRYPT 0x04
+#define LMP_SOFFSET 0x08
+#define LMP_TACCURACY 0x10
+#define LMP_RSWITCH 0x20
+#define LMP_HOLD 0x40
+#define LMP_SNIFF 0x80
+
+#define LMP_PARK 0x01
+#define LMP_RSSI 0x02
+#define LMP_QUALITY 0x04
+#define LMP_SCO 0x08
+#define LMP_HV2 0x10
+#define LMP_HV3 0x20
+#define LMP_ULAW 0x40
+#define LMP_ALAW 0x80
+
+#define LMP_CVSD 0x01
+#define LMP_PSCHEME 0x02
+#define LMP_PCONTROL 0x04
+#define LMP_TRSP_SCO 0x08
+#define LMP_BCAST_ENC 0x80
+
+#define LMP_EDR_ACL_2M 0x02
+#define LMP_EDR_ACL_3M 0x04
+#define LMP_ENH_ISCAN 0x08
+#define LMP_ILACE_ISCAN 0x10
+#define LMP_ILACE_PSCAN 0x20
+#define LMP_RSSI_INQ 0x40
+#define LMP_ESCO 0x80
+
+#define LMP_EV4 0x01
+#define LMP_EV5 0x02
+#define LMP_AFH_CAP_SLV 0x08
+#define LMP_AFH_CLS_SLV 0x10
+#define LMP_NO_BREDR 0x20
+#define LMP_LE 0x40
+#define LMP_EDR_3SLOT 0x80
+
+#define LMP_EDR_5SLOT 0x01
+#define LMP_SNIFF_SUBR 0x02
+#define LMP_PAUSE_ENC 0x04
+#define LMP_AFH_CAP_MST 0x08
+#define LMP_AFH_CLS_MST 0x10
+#define LMP_EDR_ESCO_2M 0x20
+#define LMP_EDR_ESCO_3M 0x40
+#define LMP_EDR_3S_ESCO 0x80
+
+#define LMP_EXT_INQ 0x01
+#define LMP_LE_BREDR 0x02
+#define LMP_SIMPLE_PAIR 0x08
+#define LMP_ENCAPS_PDU 0x10
+#define LMP_ERR_DAT_REP 0x20
+#define LMP_NFLUSH_PKTS 0x40
+
+#define LMP_LSTO 0x01
+#define LMP_INQ_TX_PWR 0x02
+#define LMP_EPC 0x04
+#define LMP_EXT_FEAT 0x80
+
+/* Extended LMP features */
+#define LMP_HOST_LE 0x02
+
+/* Link policies */
+#define HCI_LP_RSWITCH 0x0001
+#define HCI_LP_HOLD 0x0002
+#define HCI_LP_SNIFF 0x0004
+#define HCI_LP_PARK 0x0008
+
+/* Link mode */
+#define HCI_LM_ACCEPT 0x8000
+#define HCI_LM_MASTER 0x0001
+#define HCI_LM_AUTH 0x0002
+#define HCI_LM_ENCRYPT 0x0004
+#define HCI_LM_TRUSTED 0x0008
+#define HCI_LM_RELIABLE 0x0010
+#define HCI_LM_SECURE 0x0020
+
+/* ----- HCI Commands ----- */
+
+/* Link Control */
+#define OGF_LINK_CTL 0x01
+
+#define OCF_INQUIRY 0x0001
+typedef struct {
+ uint8_t lap[3];
+ uint8_t length; /* 1.28s units */
+ uint8_t num_rsp;
+} __attribute__ ((packed)) inquiry_cp;
+#define INQUIRY_CP_SIZE 5
+
+typedef struct {
+ uint8_t status;
+ bdaddr_t bdaddr;
+} __attribute__ ((packed)) status_bdaddr_rp;
+#define STATUS_BDADDR_RP_SIZE 7
+
+#define OCF_INQUIRY_CANCEL 0x0002
+
+#define OCF_PERIODIC_INQUIRY 0x0003
+typedef struct {
+ uint16_t max_period; /* 1.28s units */
+ uint16_t min_period; /* 1.28s units */
+ uint8_t lap[3];
+ uint8_t length; /* 1.28s units */
+ uint8_t num_rsp;
+} __attribute__ ((packed)) periodic_inquiry_cp;
+#define PERIODIC_INQUIRY_CP_SIZE 9
+
+#define OCF_EXIT_PERIODIC_INQUIRY 0x0004
+
+#define OCF_CREATE_CONN 0x0005
+typedef struct {
+ bdaddr_t bdaddr;
+ uint16_t pkt_type;
+ uint8_t pscan_rep_mode;
+ uint8_t pscan_mode;
+ uint16_t clock_offset;
+ uint8_t role_switch;
+} __attribute__ ((packed)) create_conn_cp;
+#define CREATE_CONN_CP_SIZE 13
+
+#define OCF_DISCONNECT 0x0006
+typedef struct {
+ uint16_t handle;
+ uint8_t reason;
+} __attribute__ ((packed)) disconnect_cp;
+#define DISCONNECT_CP_SIZE 3
+
+#define OCF_ADD_SCO 0x0007
+typedef struct {
+ uint16_t handle;
+ uint16_t pkt_type;
+} __attribute__ ((packed)) add_sco_cp;
+#define ADD_SCO_CP_SIZE 4
+
+#define OCF_CREATE_CONN_CANCEL 0x0008
+typedef struct {
+ bdaddr_t bdaddr;
+} __attribute__ ((packed)) create_conn_cancel_cp;
+#define CREATE_CONN_CANCEL_CP_SIZE 6
+
+#define OCF_ACCEPT_CONN_REQ 0x0009
+typedef struct {
+ bdaddr_t bdaddr;
+ uint8_t role;
+} __attribute__ ((packed)) accept_conn_req_cp;
+#define ACCEPT_CONN_REQ_CP_SIZE 7
+
+#define OCF_REJECT_CONN_REQ 0x000A
+typedef struct {
+ bdaddr_t bdaddr;
+ uint8_t reason;
+} __attribute__ ((packed)) reject_conn_req_cp;
+#define REJECT_CONN_REQ_CP_SIZE 7
+
+#define OCF_LINK_KEY_REPLY 0x000B
+typedef struct {
+ bdaddr_t bdaddr;
+ uint8_t link_key[16];
+} __attribute__ ((packed)) link_key_reply_cp;
+#define LINK_KEY_REPLY_CP_SIZE 22
+
+#define OCF_LINK_KEY_NEG_REPLY 0x000C
+
+#define OCF_PIN_CODE_REPLY 0x000D
+typedef struct {
+ bdaddr_t bdaddr;
+ uint8_t pin_len;
+ uint8_t pin_code[16];
+} __attribute__ ((packed)) pin_code_reply_cp;
+#define PIN_CODE_REPLY_CP_SIZE 23
+
+#define OCF_PIN_CODE_NEG_REPLY 0x000E
+
+#define OCF_SET_CONN_PTYPE 0x000F
+typedef struct {
+ uint16_t handle;
+ uint16_t pkt_type;
+} __attribute__ ((packed)) set_conn_ptype_cp;
+#define SET_CONN_PTYPE_CP_SIZE 4
+
+#define OCF_AUTH_REQUESTED 0x0011
+typedef struct {
+ uint16_t handle;
+} __attribute__ ((packed)) auth_requested_cp;
+#define AUTH_REQUESTED_CP_SIZE 2
+
+#define OCF_SET_CONN_ENCRYPT 0x0013
+typedef struct {
+ uint16_t handle;
+ uint8_t encrypt;
+} __attribute__ ((packed)) set_conn_encrypt_cp;
+#define SET_CONN_ENCRYPT_CP_SIZE 3
+
+#define OCF_CHANGE_CONN_LINK_KEY 0x0015
+typedef struct {
+ uint16_t handle;
+} __attribute__ ((packed)) change_conn_link_key_cp;
+#define CHANGE_CONN_LINK_KEY_CP_SIZE 2
+
+#define OCF_MASTER_LINK_KEY 0x0017
+typedef struct {
+ uint8_t key_flag;
+} __attribute__ ((packed)) master_link_key_cp;
+#define MASTER_LINK_KEY_CP_SIZE 1
+
+#define OCF_REMOTE_NAME_REQ 0x0019
+typedef struct {
+ bdaddr_t bdaddr;
+ uint8_t pscan_rep_mode;
+ uint8_t pscan_mode;
+ uint16_t clock_offset;
+} __attribute__ ((packed)) remote_name_req_cp;
+#define REMOTE_NAME_REQ_CP_SIZE 10
+
+#define OCF_REMOTE_NAME_REQ_CANCEL 0x001A
+typedef struct {
+ bdaddr_t bdaddr;
+} __attribute__ ((packed)) remote_name_req_cancel_cp;
+#define REMOTE_NAME_REQ_CANCEL_CP_SIZE 6
+
+#define OCF_READ_REMOTE_FEATURES 0x001B
+typedef struct {
+ uint16_t handle;
+} __attribute__ ((packed)) read_remote_features_cp;
+#define READ_REMOTE_FEATURES_CP_SIZE 2
+
+#define OCF_READ_REMOTE_EXT_FEATURES 0x001C
+typedef struct {
+ uint16_t handle;
+ uint8_t page_num;
+} __attribute__ ((packed)) read_remote_ext_features_cp;
+#define READ_REMOTE_EXT_FEATURES_CP_SIZE 3
+
+#define OCF_READ_REMOTE_VERSION 0x001D
+typedef struct {
+ uint16_t handle;
+} __attribute__ ((packed)) read_remote_version_cp;
+#define READ_REMOTE_VERSION_CP_SIZE 2
+
+#define OCF_READ_CLOCK_OFFSET 0x001F
+typedef struct {
+ uint16_t handle;
+} __attribute__ ((packed)) read_clock_offset_cp;
+#define READ_CLOCK_OFFSET_CP_SIZE 2
+
+#define OCF_READ_LMP_HANDLE 0x0020
+
+#define OCF_SETUP_SYNC_CONN 0x0028
+typedef struct {
+ uint16_t handle;
+ uint32_t tx_bandwith;
+ uint32_t rx_bandwith;
+ uint16_t max_latency;
+ uint16_t voice_setting;
+ uint8_t retrans_effort;
+ uint16_t pkt_type;
+} __attribute__ ((packed)) setup_sync_conn_cp;
+#define SETUP_SYNC_CONN_CP_SIZE 17
+
+#define OCF_ACCEPT_SYNC_CONN_REQ 0x0029
+typedef struct {
+ bdaddr_t bdaddr;
+ uint32_t tx_bandwith;
+ uint32_t rx_bandwith;
+ uint16_t max_latency;
+ uint16_t voice_setting;
+ uint8_t retrans_effort;
+ uint16_t pkt_type;
+} __attribute__ ((packed)) accept_sync_conn_req_cp;
+#define ACCEPT_SYNC_CONN_REQ_CP_SIZE 21
+
+#define OCF_REJECT_SYNC_CONN_REQ 0x002A
+typedef struct {
+ bdaddr_t bdaddr;
+ uint8_t reason;
+} __attribute__ ((packed)) reject_sync_conn_req_cp;
+#define REJECT_SYNC_CONN_REQ_CP_SIZE 7
+
+#define OCF_IO_CAPABILITY_REPLY 0x002B
+typedef struct {
+ bdaddr_t bdaddr;
+ uint8_t capability;
+ uint8_t oob_data;
+ uint8_t authentication;
+} __attribute__ ((packed)) io_capability_reply_cp;
+#define IO_CAPABILITY_REPLY_CP_SIZE 9
+
+#define OCF_USER_CONFIRM_REPLY 0x002C
+typedef struct {
+ bdaddr_t bdaddr;
+} __attribute__ ((packed)) user_confirm_reply_cp;
+#define USER_CONFIRM_REPLY_CP_SIZE 6
+
+#define OCF_USER_CONFIRM_NEG_REPLY 0x002D
+
+#define OCF_USER_PASSKEY_REPLY 0x002E
+typedef struct {
+ bdaddr_t bdaddr;
+ uint32_t passkey;
+} __attribute__ ((packed)) user_passkey_reply_cp;
+#define USER_PASSKEY_REPLY_CP_SIZE 10
+
+#define OCF_USER_PASSKEY_NEG_REPLY 0x002F
+
+#define OCF_REMOTE_OOB_DATA_REPLY 0x0030
+typedef struct {
+ bdaddr_t bdaddr;
+ uint8_t hash[16];
+ uint8_t randomizer[16];
+} __attribute__ ((packed)) remote_oob_data_reply_cp;
+#define REMOTE_OOB_DATA_REPLY_CP_SIZE 38
+
+#define OCF_REMOTE_OOB_DATA_NEG_REPLY 0x0033
+
+#define OCF_IO_CAPABILITY_NEG_REPLY 0x0034
+typedef struct {
+ bdaddr_t bdaddr;
+ uint8_t reason;
+} __attribute__ ((packed)) io_capability_neg_reply_cp;
+#define IO_CAPABILITY_NEG_REPLY_CP_SIZE 7
+
+#define OCF_CREATE_PHYSICAL_LINK 0x0035
+typedef struct {
+ uint8_t handle;
+ uint8_t key_length;
+ uint8_t key_type;
+ uint8_t key[32];
+} __attribute__ ((packed)) create_physical_link_cp;
+#define CREATE_PHYSICAL_LINK_CP_SIZE 35
+
+#define OCF_ACCEPT_PHYSICAL_LINK 0x0036
+
+#define OCF_DISCONNECT_PHYSICAL_LINK 0x0037
+typedef struct {
+ uint8_t handle;
+ uint8_t reason;
+} __attribute__ ((packed)) disconnect_physical_link_cp;
+#define DISCONNECT_PHYSICAL_LINK_CP_SIZE 2
+
+#define OCF_CREATE_LOGICAL_LINK 0x0038
+typedef struct {
+ uint8_t handle;
+ uint8_t tx_flow[16];
+ uint8_t rx_flow[16];
+} __attribute__ ((packed)) create_logical_link_cp;
+#define CREATE_LOGICAL_LINK_CP_SIZE 33
+
+#define OCF_ACCEPT_LOGICAL_LINK 0x0039
+
+#define OCF_DISCONNECT_LOGICAL_LINK 0x003A
+typedef struct {
+ uint16_t handle;
+} __attribute__ ((packed)) disconnect_logical_link_cp;
+#define DISCONNECT_LOGICAL_LINK_CP_SIZE 2
+
+#define OCF_LOGICAL_LINK_CANCEL 0x003B
+typedef struct {
+ uint8_t handle;
+ uint8_t tx_flow_id;
+} __attribute__ ((packed)) cancel_logical_link_cp;
+#define LOGICAL_LINK_CANCEL_CP_SIZE 2
+typedef struct {
+ uint8_t status;
+ uint8_t handle;
+ uint8_t tx_flow_id;
+} __attribute__ ((packed)) cancel_logical_link_rp;
+#define LOGICAL_LINK_CANCEL_RP_SIZE 3
+
+#define OCF_FLOW_SPEC_MODIFY 0x003C
+
+/* Link Policy */
+#define OGF_LINK_POLICY 0x02
+
+#define OCF_HOLD_MODE 0x0001
+typedef struct {
+ uint16_t handle;
+ uint16_t max_interval;
+ uint16_t min_interval;
+} __attribute__ ((packed)) hold_mode_cp;
+#define HOLD_MODE_CP_SIZE 6
+
+#define OCF_SNIFF_MODE 0x0003
+typedef struct {
+ uint16_t handle;
+ uint16_t max_interval;
+ uint16_t min_interval;
+ uint16_t attempt;
+ uint16_t timeout;
+} __attribute__ ((packed)) sniff_mode_cp;
+#define SNIFF_MODE_CP_SIZE 10
+
+#define OCF_EXIT_SNIFF_MODE 0x0004
+typedef struct {
+ uint16_t handle;
+} __attribute__ ((packed)) exit_sniff_mode_cp;
+#define EXIT_SNIFF_MODE_CP_SIZE 2
+
+#define OCF_PARK_MODE 0x0005
+typedef struct {
+ uint16_t handle;
+ uint16_t max_interval;
+ uint16_t min_interval;
+} __attribute__ ((packed)) park_mode_cp;
+#define PARK_MODE_CP_SIZE 6
+
+#define OCF_EXIT_PARK_MODE 0x0006
+typedef struct {
+ uint16_t handle;
+} __attribute__ ((packed)) exit_park_mode_cp;
+#define EXIT_PARK_MODE_CP_SIZE 2
+
+#define OCF_QOS_SETUP 0x0007
+typedef struct {
+ uint8_t service_type; /* 1 = best effort */
+ uint32_t token_rate; /* Byte per seconds */
+ uint32_t peak_bandwidth; /* Byte per seconds */
+ uint32_t latency; /* Microseconds */
+ uint32_t delay_variation; /* Microseconds */
+} __attribute__ ((packed)) hci_qos;
+#define HCI_QOS_CP_SIZE 17
+typedef struct {
+ uint16_t handle;
+ uint8_t flags; /* Reserved */
+ hci_qos qos;
+} __attribute__ ((packed)) qos_setup_cp;
+#define QOS_SETUP_CP_SIZE (3 + HCI_QOS_CP_SIZE)
+
+#define OCF_ROLE_DISCOVERY 0x0009
+typedef struct {
+ uint16_t handle;
+} __attribute__ ((packed)) role_discovery_cp;
+#define ROLE_DISCOVERY_CP_SIZE 2
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t role;
+} __attribute__ ((packed)) role_discovery_rp;
+#define ROLE_DISCOVERY_RP_SIZE 4
+
+#define OCF_SWITCH_ROLE 0x000B
+typedef struct {
+ bdaddr_t bdaddr;
+ uint8_t role;
+} __attribute__ ((packed)) switch_role_cp;
+#define SWITCH_ROLE_CP_SIZE 7
+
+#define OCF_READ_LINK_POLICY 0x000C
+typedef struct {
+ uint16_t handle;
+} __attribute__ ((packed)) read_link_policy_cp;
+#define READ_LINK_POLICY_CP_SIZE 2
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint16_t policy;
+} __attribute__ ((packed)) read_link_policy_rp;
+#define READ_LINK_POLICY_RP_SIZE 5
+
+#define OCF_WRITE_LINK_POLICY 0x000D
+typedef struct {
+ uint16_t handle;
+ uint16_t policy;
+} __attribute__ ((packed)) write_link_policy_cp;
+#define WRITE_LINK_POLICY_CP_SIZE 4
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+} __attribute__ ((packed)) write_link_policy_rp;
+#define WRITE_LINK_POLICY_RP_SIZE 3
+
+#define OCF_READ_DEFAULT_LINK_POLICY 0x000E
+
+#define OCF_WRITE_DEFAULT_LINK_POLICY 0x000F
+
+#define OCF_FLOW_SPECIFICATION 0x0010
+
+#define OCF_SNIFF_SUBRATING 0x0011
+typedef struct {
+ uint16_t handle;
+ uint16_t max_latency;
+ uint16_t min_remote_timeout;
+ uint16_t min_local_timeout;
+} __attribute__ ((packed)) sniff_subrating_cp;
+#define SNIFF_SUBRATING_CP_SIZE 8
+
+/* Host Controller and Baseband */
+#define OGF_HOST_CTL 0x03
+
+#define OCF_SET_EVENT_MASK 0x0001
+typedef struct {
+ uint8_t mask[8];
+} __attribute__ ((packed)) set_event_mask_cp;
+#define SET_EVENT_MASK_CP_SIZE 8
+
+#define OCF_RESET 0x0003
+
+#define OCF_SET_EVENT_FLT 0x0005
+typedef struct {
+ uint8_t flt_type;
+ uint8_t cond_type;
+ uint8_t condition[0];
+} __attribute__ ((packed)) set_event_flt_cp;
+#define SET_EVENT_FLT_CP_SIZE 2
+
+/* Filter types */
+#define FLT_CLEAR_ALL 0x00
+#define FLT_INQ_RESULT 0x01
+#define FLT_CONN_SETUP 0x02
+/* INQ_RESULT Condition types */
+#define INQ_RESULT_RETURN_ALL 0x00
+#define INQ_RESULT_RETURN_CLASS 0x01
+#define INQ_RESULT_RETURN_BDADDR 0x02
+/* CONN_SETUP Condition types */
+#define CONN_SETUP_ALLOW_ALL 0x00
+#define CONN_SETUP_ALLOW_CLASS 0x01
+#define CONN_SETUP_ALLOW_BDADDR 0x02
+/* CONN_SETUP Conditions */
+#define CONN_SETUP_AUTO_OFF 0x01
+#define CONN_SETUP_AUTO_ON 0x02
+
+#define OCF_FLUSH 0x0008
+
+#define OCF_READ_PIN_TYPE 0x0009
+typedef struct {
+ uint8_t status;
+ uint8_t pin_type;
+} __attribute__ ((packed)) read_pin_type_rp;
+#define READ_PIN_TYPE_RP_SIZE 2
+
+#define OCF_WRITE_PIN_TYPE 0x000A
+typedef struct {
+ uint8_t pin_type;
+} __attribute__ ((packed)) write_pin_type_cp;
+#define WRITE_PIN_TYPE_CP_SIZE 1
+
+#define OCF_CREATE_NEW_UNIT_KEY 0x000B
+
+#define OCF_READ_STORED_LINK_KEY 0x000D
+typedef struct {
+ bdaddr_t bdaddr;
+ uint8_t read_all;
+} __attribute__ ((packed)) read_stored_link_key_cp;
+#define READ_STORED_LINK_KEY_CP_SIZE 7
+typedef struct {
+ uint8_t status;
+ uint16_t max_keys;
+ uint16_t num_keys;
+} __attribute__ ((packed)) read_stored_link_key_rp;
+#define READ_STORED_LINK_KEY_RP_SIZE 5
+
+#define OCF_WRITE_STORED_LINK_KEY 0x0011
+typedef struct {
+ uint8_t num_keys;
+ /* variable length part */
+} __attribute__ ((packed)) write_stored_link_key_cp;
+#define WRITE_STORED_LINK_KEY_CP_SIZE 1
+typedef struct {
+ uint8_t status;
+ uint8_t num_keys;
+} __attribute__ ((packed)) write_stored_link_key_rp;
+#define READ_WRITE_LINK_KEY_RP_SIZE 2
+
+#define OCF_DELETE_STORED_LINK_KEY 0x0012
+typedef struct {
+ bdaddr_t bdaddr;
+ uint8_t delete_all;
+} __attribute__ ((packed)) delete_stored_link_key_cp;
+#define DELETE_STORED_LINK_KEY_CP_SIZE 7
+typedef struct {
+ uint8_t status;
+ uint16_t num_keys;
+} __attribute__ ((packed)) delete_stored_link_key_rp;
+#define DELETE_STORED_LINK_KEY_RP_SIZE 3
+
+#define OCF_CHANGE_LOCAL_NAME 0x0013
+typedef struct {
+ uint8_t name[248];
+} __attribute__ ((packed)) change_local_name_cp;
+#define CHANGE_LOCAL_NAME_CP_SIZE 248
+
+#define OCF_READ_LOCAL_NAME 0x0014
+typedef struct {
+ uint8_t status;
+ uint8_t name[248];
+} __attribute__ ((packed)) read_local_name_rp;
+#define READ_LOCAL_NAME_RP_SIZE 249
+
+#define OCF_READ_CONN_ACCEPT_TIMEOUT 0x0015
+typedef struct {
+ uint8_t status;
+ uint16_t timeout;
+} __attribute__ ((packed)) read_conn_accept_timeout_rp;
+#define READ_CONN_ACCEPT_TIMEOUT_RP_SIZE 3
+
+#define OCF_WRITE_CONN_ACCEPT_TIMEOUT 0x0016
+typedef struct {
+ uint16_t timeout;
+} __attribute__ ((packed)) write_conn_accept_timeout_cp;
+#define WRITE_CONN_ACCEPT_TIMEOUT_CP_SIZE 2
+
+#define OCF_READ_PAGE_TIMEOUT 0x0017
+typedef struct {
+ uint8_t status;
+ uint16_t timeout;
+} __attribute__ ((packed)) read_page_timeout_rp;
+#define READ_PAGE_TIMEOUT_RP_SIZE 3
+
+#define OCF_WRITE_PAGE_TIMEOUT 0x0018
+typedef struct {
+ uint16_t timeout;
+} __attribute__ ((packed)) write_page_timeout_cp;
+#define WRITE_PAGE_TIMEOUT_CP_SIZE 2
+
+#define OCF_READ_SCAN_ENABLE 0x0019
+typedef struct {
+ uint8_t status;
+ uint8_t enable;
+} __attribute__ ((packed)) read_scan_enable_rp;
+#define READ_SCAN_ENABLE_RP_SIZE 2
+
+#define OCF_WRITE_SCAN_ENABLE 0x001A
+ #define SCAN_DISABLED 0x00
+ #define SCAN_INQUIRY 0x01
+ #define SCAN_PAGE 0x02
+
+#define OCF_READ_PAGE_ACTIVITY 0x001B
+typedef struct {
+ uint8_t status;
+ uint16_t interval;
+ uint16_t window;
+} __attribute__ ((packed)) read_page_activity_rp;
+#define READ_PAGE_ACTIVITY_RP_SIZE 5
+
+#define OCF_WRITE_PAGE_ACTIVITY 0x001C
+typedef struct {
+ uint16_t interval;
+ uint16_t window;
+} __attribute__ ((packed)) write_page_activity_cp;
+#define WRITE_PAGE_ACTIVITY_CP_SIZE 4
+
+#define OCF_READ_INQ_ACTIVITY 0x001D
+typedef struct {
+ uint8_t status;
+ uint16_t interval;
+ uint16_t window;
+} __attribute__ ((packed)) read_inq_activity_rp;
+#define READ_INQ_ACTIVITY_RP_SIZE 5
+
+#define OCF_WRITE_INQ_ACTIVITY 0x001E
+typedef struct {
+ uint16_t interval;
+ uint16_t window;
+} __attribute__ ((packed)) write_inq_activity_cp;
+#define WRITE_INQ_ACTIVITY_CP_SIZE 4
+
+#define OCF_READ_AUTH_ENABLE 0x001F
+
+#define OCF_WRITE_AUTH_ENABLE 0x0020
+ #define AUTH_DISABLED 0x00
+ #define AUTH_ENABLED 0x01
+
+#define OCF_READ_ENCRYPT_MODE 0x0021
+
+#define OCF_WRITE_ENCRYPT_MODE 0x0022
+ #define ENCRYPT_DISABLED 0x00
+ #define ENCRYPT_P2P 0x01
+ #define ENCRYPT_BOTH 0x02
+
+#define OCF_READ_CLASS_OF_DEV 0x0023
+typedef struct {
+ uint8_t status;
+ uint8_t dev_class[3];
+} __attribute__ ((packed)) read_class_of_dev_rp;
+#define READ_CLASS_OF_DEV_RP_SIZE 4
+
+#define OCF_WRITE_CLASS_OF_DEV 0x0024
+typedef struct {
+ uint8_t dev_class[3];
+} __attribute__ ((packed)) write_class_of_dev_cp;
+#define WRITE_CLASS_OF_DEV_CP_SIZE 3
+
+#define OCF_READ_VOICE_SETTING 0x0025
+typedef struct {
+ uint8_t status;
+ uint16_t voice_setting;
+} __attribute__ ((packed)) read_voice_setting_rp;
+#define READ_VOICE_SETTING_RP_SIZE 3
+
+#define OCF_WRITE_VOICE_SETTING 0x0026
+typedef struct {
+ uint16_t voice_setting;
+} __attribute__ ((packed)) write_voice_setting_cp;
+#define WRITE_VOICE_SETTING_CP_SIZE 2
+
+#define OCF_READ_AUTOMATIC_FLUSH_TIMEOUT 0x0027
+
+#define OCF_WRITE_AUTOMATIC_FLUSH_TIMEOUT 0x0028
+
+#define OCF_READ_NUM_BROADCAST_RETRANS 0x0029
+
+#define OCF_WRITE_NUM_BROADCAST_RETRANS 0x002A
+
+#define OCF_READ_HOLD_MODE_ACTIVITY 0x002B
+
+#define OCF_WRITE_HOLD_MODE_ACTIVITY 0x002C
+
+#define OCF_READ_TRANSMIT_POWER_LEVEL 0x002D
+typedef struct {
+ uint16_t handle;
+ uint8_t type;
+} __attribute__ ((packed)) read_transmit_power_level_cp;
+#define READ_TRANSMIT_POWER_LEVEL_CP_SIZE 3
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ int8_t level;
+} __attribute__ ((packed)) read_transmit_power_level_rp;
+#define READ_TRANSMIT_POWER_LEVEL_RP_SIZE 4
+
+#define OCF_READ_SYNC_FLOW_ENABLE 0x002E
+
+#define OCF_WRITE_SYNC_FLOW_ENABLE 0x002F
+
+#define OCF_SET_CONTROLLER_TO_HOST_FC 0x0031
+
+#define OCF_HOST_BUFFER_SIZE 0x0033
+typedef struct {
+ uint16_t acl_mtu;
+ uint8_t sco_mtu;
+ uint16_t acl_max_pkt;
+ uint16_t sco_max_pkt;
+} __attribute__ ((packed)) host_buffer_size_cp;
+#define HOST_BUFFER_SIZE_CP_SIZE 7
+
+#define OCF_HOST_NUM_COMP_PKTS 0x0035
+typedef struct {
+ uint8_t num_hndl;
+ /* variable length part */
+} __attribute__ ((packed)) host_num_comp_pkts_cp;
+#define HOST_NUM_COMP_PKTS_CP_SIZE 1
+
+#define OCF_READ_LINK_SUPERVISION_TIMEOUT 0x0036
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint16_t timeout;
+} __attribute__ ((packed)) read_link_supervision_timeout_rp;
+#define READ_LINK_SUPERVISION_TIMEOUT_RP_SIZE 5
+
+#define OCF_WRITE_LINK_SUPERVISION_TIMEOUT 0x0037
+typedef struct {
+ uint16_t handle;
+ uint16_t timeout;
+} __attribute__ ((packed)) write_link_supervision_timeout_cp;
+#define WRITE_LINK_SUPERVISION_TIMEOUT_CP_SIZE 4
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+} __attribute__ ((packed)) write_link_supervision_timeout_rp;
+#define WRITE_LINK_SUPERVISION_TIMEOUT_RP_SIZE 3
+
+#define OCF_READ_NUM_SUPPORTED_IAC 0x0038
+
+#define MAX_IAC_LAP 0x40
+#define OCF_READ_CURRENT_IAC_LAP 0x0039
+typedef struct {
+ uint8_t status;
+ uint8_t num_current_iac;
+ uint8_t lap[MAX_IAC_LAP][3];
+} __attribute__ ((packed)) read_current_iac_lap_rp;
+#define READ_CURRENT_IAC_LAP_RP_SIZE 2+3*MAX_IAC_LAP
+
+#define OCF_WRITE_CURRENT_IAC_LAP 0x003A
+typedef struct {
+ uint8_t num_current_iac;
+ uint8_t lap[MAX_IAC_LAP][3];
+} __attribute__ ((packed)) write_current_iac_lap_cp;
+#define WRITE_CURRENT_IAC_LAP_CP_SIZE 1+3*MAX_IAC_LAP
+
+#define OCF_READ_PAGE_SCAN_PERIOD_MODE 0x003B
+
+#define OCF_WRITE_PAGE_SCAN_PERIOD_MODE 0x003C
+
+#define OCF_READ_PAGE_SCAN_MODE 0x003D
+
+#define OCF_WRITE_PAGE_SCAN_MODE 0x003E
+
+#define OCF_SET_AFH_CLASSIFICATION 0x003F
+typedef struct {
+ uint8_t map[10];
+} __attribute__ ((packed)) set_afh_classification_cp;
+#define SET_AFH_CLASSIFICATION_CP_SIZE 10
+typedef struct {
+ uint8_t status;
+} __attribute__ ((packed)) set_afh_classification_rp;
+#define SET_AFH_CLASSIFICATION_RP_SIZE 1
+
+#define OCF_READ_INQUIRY_SCAN_TYPE 0x0042
+typedef struct {
+ uint8_t status;
+ uint8_t type;
+} __attribute__ ((packed)) read_inquiry_scan_type_rp;
+#define READ_INQUIRY_SCAN_TYPE_RP_SIZE 2
+
+#define OCF_WRITE_INQUIRY_SCAN_TYPE 0x0043
+typedef struct {
+ uint8_t type;
+} __attribute__ ((packed)) write_inquiry_scan_type_cp;
+#define WRITE_INQUIRY_SCAN_TYPE_CP_SIZE 1
+typedef struct {
+ uint8_t status;
+} __attribute__ ((packed)) write_inquiry_scan_type_rp;
+#define WRITE_INQUIRY_SCAN_TYPE_RP_SIZE 1
+
+#define OCF_READ_INQUIRY_MODE 0x0044
+typedef struct {
+ uint8_t status;
+ uint8_t mode;
+} __attribute__ ((packed)) read_inquiry_mode_rp;
+#define READ_INQUIRY_MODE_RP_SIZE 2
+
+#define OCF_WRITE_INQUIRY_MODE 0x0045
+typedef struct {
+ uint8_t mode;
+} __attribute__ ((packed)) write_inquiry_mode_cp;
+#define WRITE_INQUIRY_MODE_CP_SIZE 1
+typedef struct {
+ uint8_t status;
+} __attribute__ ((packed)) write_inquiry_mode_rp;
+#define WRITE_INQUIRY_MODE_RP_SIZE 1
+
+#define OCF_READ_PAGE_SCAN_TYPE 0x0046
+
+#define OCF_WRITE_PAGE_SCAN_TYPE 0x0047
+ #define PAGE_SCAN_TYPE_STANDARD 0x00
+ #define PAGE_SCAN_TYPE_INTERLACED 0x01
+
+#define OCF_READ_AFH_MODE 0x0048
+typedef struct {
+ uint8_t status;
+ uint8_t mode;
+} __attribute__ ((packed)) read_afh_mode_rp;
+#define READ_AFH_MODE_RP_SIZE 2
+
+#define OCF_WRITE_AFH_MODE 0x0049
+typedef struct {
+ uint8_t mode;
+} __attribute__ ((packed)) write_afh_mode_cp;
+#define WRITE_AFH_MODE_CP_SIZE 1
+typedef struct {
+ uint8_t status;
+} __attribute__ ((packed)) write_afh_mode_rp;
+#define WRITE_AFH_MODE_RP_SIZE 1
+
+#define OCF_READ_EXT_INQUIRY_RESPONSE 0x0051
+typedef struct {
+ uint8_t status;
+ uint8_t fec;
+ uint8_t data[240];
+} __attribute__ ((packed)) read_ext_inquiry_response_rp;
+#define READ_EXT_INQUIRY_RESPONSE_RP_SIZE 242
+
+#define OCF_WRITE_EXT_INQUIRY_RESPONSE 0x0052
+typedef struct {
+ uint8_t fec;
+ uint8_t data[240];
+} __attribute__ ((packed)) write_ext_inquiry_response_cp;
+#define WRITE_EXT_INQUIRY_RESPONSE_CP_SIZE 241
+typedef struct {
+ uint8_t status;
+} __attribute__ ((packed)) write_ext_inquiry_response_rp;
+#define WRITE_EXT_INQUIRY_RESPONSE_RP_SIZE 1
+
+#define OCF_REFRESH_ENCRYPTION_KEY 0x0053
+typedef struct {
+ uint16_t handle;
+} __attribute__ ((packed)) refresh_encryption_key_cp;
+#define REFRESH_ENCRYPTION_KEY_CP_SIZE 2
+typedef struct {
+ uint8_t status;
+} __attribute__ ((packed)) refresh_encryption_key_rp;
+#define REFRESH_ENCRYPTION_KEY_RP_SIZE 1
+
+#define OCF_READ_SIMPLE_PAIRING_MODE 0x0055
+typedef struct {
+ uint8_t status;
+ uint8_t mode;
+} __attribute__ ((packed)) read_simple_pairing_mode_rp;
+#define READ_SIMPLE_PAIRING_MODE_RP_SIZE 2
+
+#define OCF_WRITE_SIMPLE_PAIRING_MODE 0x0056
+typedef struct {
+ uint8_t mode;
+} __attribute__ ((packed)) write_simple_pairing_mode_cp;
+#define WRITE_SIMPLE_PAIRING_MODE_CP_SIZE 1
+typedef struct {
+ uint8_t status;
+} __attribute__ ((packed)) write_simple_pairing_mode_rp;
+#define WRITE_SIMPLE_PAIRING_MODE_RP_SIZE 1
+
+#define OCF_READ_LOCAL_OOB_DATA 0x0057
+typedef struct {
+ uint8_t status;
+ uint8_t hash[16];
+ uint8_t randomizer[16];
+} __attribute__ ((packed)) read_local_oob_data_rp;
+#define READ_LOCAL_OOB_DATA_RP_SIZE 33
+
+#define OCF_READ_INQ_RESPONSE_TX_POWER_LEVEL 0x0058
+typedef struct {
+ uint8_t status;
+ int8_t level;
+} __attribute__ ((packed)) read_inq_response_tx_power_level_rp;
+#define READ_INQ_RESPONSE_TX_POWER_LEVEL_RP_SIZE 2
+
+#define OCF_READ_INQUIRY_TRANSMIT_POWER_LEVEL 0x0058
+typedef struct {
+ uint8_t status;
+ int8_t level;
+} __attribute__ ((packed)) read_inquiry_transmit_power_level_rp;
+#define READ_INQUIRY_TRANSMIT_POWER_LEVEL_RP_SIZE 2
+
+#define OCF_WRITE_INQUIRY_TRANSMIT_POWER_LEVEL 0x0059
+typedef struct {
+ int8_t level;
+} __attribute__ ((packed)) write_inquiry_transmit_power_level_cp;
+#define WRITE_INQUIRY_TRANSMIT_POWER_LEVEL_CP_SIZE 1
+typedef struct {
+ uint8_t status;
+} __attribute__ ((packed)) write_inquiry_transmit_power_level_rp;
+#define WRITE_INQUIRY_TRANSMIT_POWER_LEVEL_RP_SIZE 1
+
+#define OCF_READ_DEFAULT_ERROR_DATA_REPORTING 0x005A
+typedef struct {
+ uint8_t status;
+ uint8_t reporting;
+} __attribute__ ((packed)) read_default_error_data_reporting_rp;
+#define READ_DEFAULT_ERROR_DATA_REPORTING_RP_SIZE 2
+
+#define OCF_WRITE_DEFAULT_ERROR_DATA_REPORTING 0x005B
+typedef struct {
+ uint8_t reporting;
+} __attribute__ ((packed)) write_default_error_data_reporting_cp;
+#define WRITE_DEFAULT_ERROR_DATA_REPORTING_CP_SIZE 1
+typedef struct {
+ uint8_t status;
+} __attribute__ ((packed)) write_default_error_data_reporting_rp;
+#define WRITE_DEFAULT_ERROR_DATA_REPORTING_RP_SIZE 1
+
+#define OCF_ENHANCED_FLUSH 0x005F
+typedef struct {
+ uint16_t handle;
+ uint8_t type;
+} __attribute__ ((packed)) enhanced_flush_cp;
+#define ENHANCED_FLUSH_CP_SIZE 3
+
+#define OCF_SEND_KEYPRESS_NOTIFY 0x0060
+typedef struct {
+ bdaddr_t bdaddr;
+ uint8_t type;
+} __attribute__ ((packed)) send_keypress_notify_cp;
+#define SEND_KEYPRESS_NOTIFY_CP_SIZE 7
+typedef struct {
+ uint8_t status;
+} __attribute__ ((packed)) send_keypress_notify_rp;
+#define SEND_KEYPRESS_NOTIFY_RP_SIZE 1
+
+#define OCF_READ_LOGICAL_LINK_ACCEPT_TIMEOUT 0x0061
+typedef struct {
+ uint8_t status;
+ uint16_t timeout;
+} __attribute__ ((packed)) read_log_link_accept_timeout_rp;
+#define READ_LOGICAL_LINK_ACCEPT_TIMEOUT_RP_SIZE 3
+
+#define OCF_WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT 0x0062
+typedef struct {
+ uint16_t timeout;
+} __attribute__ ((packed)) write_log_link_accept_timeout_cp;
+#define WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT_CP_SIZE 2
+
+#define OCF_SET_EVENT_MASK_PAGE_2 0x0063
+
+#define OCF_READ_LOCATION_DATA 0x0064
+
+#define OCF_WRITE_LOCATION_DATA 0x0065
+
+#define OCF_READ_FLOW_CONTROL_MODE 0x0066
+
+#define OCF_WRITE_FLOW_CONTROL_MODE 0x0067
+
+#define OCF_READ_ENHANCED_TRANSMIT_POWER_LEVEL 0x0068
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ int8_t level_gfsk;
+ int8_t level_dqpsk;
+ int8_t level_8dpsk;
+} __attribute__ ((packed)) read_enhanced_transmit_power_level_rp;
+#define READ_ENHANCED_TRANSMIT_POWER_LEVEL_RP_SIZE 6
+
+#define OCF_READ_BEST_EFFORT_FLUSH_TIMEOUT 0x0069
+typedef struct {
+ uint8_t status;
+ uint32_t timeout;
+} __attribute__ ((packed)) read_best_effort_flush_timeout_rp;
+#define READ_BEST_EFFORT_FLUSH_TIMEOUT_RP_SIZE 5
+
+#define OCF_WRITE_BEST_EFFORT_FLUSH_TIMEOUT 0x006A
+typedef struct {
+ uint16_t handle;
+ uint32_t timeout;
+} __attribute__ ((packed)) write_best_effort_flush_timeout_cp;
+#define WRITE_BEST_EFFORT_FLUSH_TIMEOUT_CP_SIZE 6
+typedef struct {
+ uint8_t status;
+} __attribute__ ((packed)) write_best_effort_flush_timeout_rp;
+#define WRITE_BEST_EFFORT_FLUSH_TIMEOUT_RP_SIZE 1
+
+#define OCF_WRITE_LE_HOST_SUPPORTED 0x006D
+typedef struct {
+ uint8_t le;
+ uint8_t simul;
+} __attribute__ ((packed)) write_le_host_supported_cp;
+#define WRITE_LE_HOST_SUPPORTED_CP_SIZE 2
+
+/* Informational Parameters */
+#define OGF_INFO_PARAM 0x04
+
+#define OCF_READ_LOCAL_VERSION 0x0001
+typedef struct {
+ uint8_t status;
+ uint8_t hci_ver;
+ uint16_t hci_rev;
+ uint8_t lmp_ver;
+ uint16_t manufacturer;
+ uint16_t lmp_subver;
+} __attribute__ ((packed)) read_local_version_rp;
+#define READ_LOCAL_VERSION_RP_SIZE 9
+
+#define OCF_READ_LOCAL_COMMANDS 0x0002
+typedef struct {
+ uint8_t status;
+ uint8_t commands[64];
+} __attribute__ ((packed)) read_local_commands_rp;
+#define READ_LOCAL_COMMANDS_RP_SIZE 65
+
+#define OCF_READ_LOCAL_FEATURES 0x0003
+typedef struct {
+ uint8_t status;
+ uint8_t features[8];
+} __attribute__ ((packed)) read_local_features_rp;
+#define READ_LOCAL_FEATURES_RP_SIZE 9
+
+#define OCF_READ_LOCAL_EXT_FEATURES 0x0004
+typedef struct {
+ uint8_t page_num;
+} __attribute__ ((packed)) read_local_ext_features_cp;
+#define READ_LOCAL_EXT_FEATURES_CP_SIZE 1
+typedef struct {
+ uint8_t status;
+ uint8_t page_num;
+ uint8_t max_page_num;
+ uint8_t features[8];
+} __attribute__ ((packed)) read_local_ext_features_rp;
+#define READ_LOCAL_EXT_FEATURES_RP_SIZE 11
+
+#define OCF_READ_BUFFER_SIZE 0x0005
+typedef struct {
+ uint8_t status;
+ uint16_t acl_mtu;
+ uint8_t sco_mtu;
+ uint16_t acl_max_pkt;
+ uint16_t sco_max_pkt;
+} __attribute__ ((packed)) read_buffer_size_rp;
+#define READ_BUFFER_SIZE_RP_SIZE 8
+
+#define OCF_READ_COUNTRY_CODE 0x0007
+
+#define OCF_READ_BD_ADDR 0x0009
+typedef struct {
+ uint8_t status;
+ bdaddr_t bdaddr;
+} __attribute__ ((packed)) read_bd_addr_rp;
+#define READ_BD_ADDR_RP_SIZE 7
+
+/* Status params */
+#define OGF_STATUS_PARAM 0x05
+
+#define OCF_READ_FAILED_CONTACT_COUNTER 0x0001
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t counter;
+} __attribute__ ((packed)) read_failed_contact_counter_rp;
+#define READ_FAILED_CONTACT_COUNTER_RP_SIZE 4
+
+#define OCF_RESET_FAILED_CONTACT_COUNTER 0x0002
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+} __attribute__ ((packed)) reset_failed_contact_counter_rp;
+#define RESET_FAILED_CONTACT_COUNTER_RP_SIZE 4
+
+#define OCF_READ_LINK_QUALITY 0x0003
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t link_quality;
+} __attribute__ ((packed)) read_link_quality_rp;
+#define READ_LINK_QUALITY_RP_SIZE 4
+
+#define OCF_READ_RSSI 0x0005
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ int8_t rssi;
+} __attribute__ ((packed)) read_rssi_rp;
+#define READ_RSSI_RP_SIZE 4
+
+#define OCF_READ_AFH_MAP 0x0006
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t mode;
+ uint8_t map[10];
+} __attribute__ ((packed)) read_afh_map_rp;
+#define READ_AFH_MAP_RP_SIZE 14
+
+#define OCF_READ_CLOCK 0x0007
+typedef struct {
+ uint16_t handle;
+ uint8_t which_clock;
+} __attribute__ ((packed)) read_clock_cp;
+#define READ_CLOCK_CP_SIZE 3
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint32_t clock;
+ uint16_t accuracy;
+} __attribute__ ((packed)) read_clock_rp;
+#define READ_CLOCK_RP_SIZE 9
+
+#define OCF_READ_LOCAL_AMP_INFO 0x0009
+typedef struct {
+ uint8_t status;
+ uint8_t amp_status;
+ uint32_t total_bandwidth;
+ uint32_t max_guaranteed_bandwidth;
+ uint32_t min_latency;
+ uint32_t max_pdu_size;
+ uint8_t controller_type;
+ uint16_t pal_caps;
+ uint16_t max_amp_assoc_length;
+ uint32_t max_flush_timeout;
+ uint32_t best_effort_flush_timeout;
+} __attribute__ ((packed)) read_local_amp_info_rp;
+#define READ_LOCAL_AMP_INFO_RP_SIZE 31
+
+#define OCF_READ_LOCAL_AMP_ASSOC 0x000A
+typedef struct {
+ uint8_t handle;
+ uint16_t length_so_far;
+ uint16_t assoc_length;
+} __attribute__ ((packed)) read_local_amp_assoc_cp;
+#define READ_LOCAL_AMP_ASSOC_CP_SIZE 5
+typedef struct {
+ uint8_t status;
+ uint8_t handle;
+ uint16_t length;
+ uint8_t fragment[248];
+} __attribute__ ((packed)) read_local_amp_assoc_rp;
+#define READ_LOCAL_AMP_ASSOC_RP_SIZE 252
+
+#define OCF_WRITE_REMOTE_AMP_ASSOC 0x000B
+typedef struct {
+ uint8_t handle;
+ uint16_t length_so_far;
+ uint16_t assoc_length;
+ uint8_t fragment[248];
+} __attribute__ ((packed)) write_remote_amp_assoc_cp;
+#define WRITE_REMOTE_AMP_ASSOC_CP_SIZE 253
+typedef struct {
+ uint8_t status;
+ uint8_t handle;
+} __attribute__ ((packed)) write_remote_amp_assoc_rp;
+#define WRITE_REMOTE_AMP_ASSOC_RP_SIZE 2
+
+/* Testing commands */
+#define OGF_TESTING_CMD 0x3e
+
+#define OCF_READ_LOOPBACK_MODE 0x0001
+
+#define OCF_WRITE_LOOPBACK_MODE 0x0002
+
+#define OCF_ENABLE_DEVICE_UNDER_TEST_MODE 0x0003
+
+#define OCF_WRITE_SIMPLE_PAIRING_DEBUG_MODE 0x0004
+typedef struct {
+ uint8_t mode;
+} __attribute__ ((packed)) write_simple_pairing_debug_mode_cp;
+#define WRITE_SIMPLE_PAIRING_DEBUG_MODE_CP_SIZE 1
+typedef struct {
+ uint8_t status;
+} __attribute__ ((packed)) write_simple_pairing_debug_mode_rp;
+#define WRITE_SIMPLE_PAIRING_DEBUG_MODE_RP_SIZE 1
+
+/* LE commands */
+#define OGF_LE_CTL 0x08
+
+#define OCF_LE_SET_EVENT_MASK 0x0001
+typedef struct {
+ uint8_t mask[8];
+} __attribute__ ((packed)) le_set_event_mask_cp;
+#define LE_SET_EVENT_MASK_CP_SIZE 8
+
+#define OCF_LE_READ_BUFFER_SIZE 0x0002
+typedef struct {
+ uint8_t status;
+ uint16_t pkt_len;
+ uint8_t max_pkt;
+} __attribute__ ((packed)) le_read_buffer_size_rp;
+#define LE_READ_BUFFER_SIZE_RP_SIZE 4
+
+#define OCF_LE_READ_LOCAL_SUPPORTED_FEATURES 0x0003
+typedef struct {
+ uint8_t status;
+ uint8_t features[8];
+} __attribute__ ((packed)) le_read_local_supported_features_rp;
+#define LE_READ_LOCAL_SUPPORTED_FEATURES_RP_SIZE 9
+
+#define OCF_LE_SET_RANDOM_ADDRESS 0x0005
+typedef struct {
+ bdaddr_t bdaddr;
+} __attribute__ ((packed)) le_set_random_address_cp;
+#define LE_SET_RANDOM_ADDRESS_CP_SIZE 6
+
+#define OCF_LE_SET_ADVERTISING_PARAMETERS 0x0006
+typedef struct {
+ uint16_t min_interval;
+ uint16_t max_interval;
+ uint8_t advtype;
+ uint8_t own_bdaddr_type;
+ uint8_t direct_bdaddr_type;
+ bdaddr_t direct_bdaddr;
+ uint8_t chan_map;
+ uint8_t filter;
+} __attribute__ ((packed)) le_set_advertising_parameters_cp;
+#define LE_SET_ADVERTISING_PARAMETERS_CP_SIZE 15
+
+#define OCF_LE_READ_ADVERTISING_CHANNEL_TX_POWER 0x0007
+typedef struct {
+ uint8_t status;
+ uint8_t level;
+} __attribute__ ((packed)) le_read_advertising_channel_tx_power_rp;
+#define LE_READ_ADVERTISING_CHANNEL_TX_POWER_RP_SIZE 2
+
+#define OCF_LE_SET_ADVERTISING_DATA 0x0008
+typedef struct {
+ uint8_t length;
+ uint8_t data[31];
+} __attribute__ ((packed)) le_set_advertising_data_cp;
+#define LE_SET_ADVERTISING_DATA_CP_SIZE 32
+
+#define OCF_LE_SET_SCAN_RESPONSE_DATA 0x0009
+typedef struct {
+ uint8_t length;
+ uint8_t data[31];
+} __attribute__ ((packed)) le_set_scan_response_data_cp;
+#define LE_SET_SCAN_RESPONSE_DATA_CP_SIZE 32
+
+#define OCF_LE_SET_ADVERTISE_ENABLE 0x000A
+typedef struct {
+ uint8_t enable;
+} __attribute__ ((packed)) le_set_advertise_enable_cp;
+#define LE_SET_ADVERTISE_ENABLE_CP_SIZE 1
+
+#define OCF_LE_SET_SCAN_PARAMETERS 0x000B
+typedef struct {
+ uint8_t type;
+ uint16_t interval;
+ uint16_t window;
+ uint8_t own_bdaddr_type;
+ uint8_t filter;
+} __attribute__ ((packed)) le_set_scan_parameters_cp;
+#define LE_SET_SCAN_PARAMETERS_CP_SIZE 7
+
+#define OCF_LE_SET_SCAN_ENABLE 0x000C
+typedef struct {
+ uint8_t enable;
+ uint8_t filter_dup;
+} __attribute__ ((packed)) le_set_scan_enable_cp;
+#define LE_SET_SCAN_ENABLE_CP_SIZE 2
+
+#define OCF_LE_CREATE_CONN 0x000D
+typedef struct {
+ uint16_t interval;
+ uint16_t window;
+ uint8_t initiator_filter;
+ uint8_t peer_bdaddr_type;
+ bdaddr_t peer_bdaddr;
+ uint8_t own_bdaddr_type;
+ uint16_t min_interval;
+ uint16_t max_interval;
+ uint16_t latency;
+ uint16_t supervision_timeout;
+ uint16_t min_ce_length;
+ uint16_t max_ce_length;
+} __attribute__ ((packed)) le_create_connection_cp;
+#define LE_CREATE_CONN_CP_SIZE 25
+
+#define OCF_LE_CREATE_CONN_CANCEL 0x000E
+
+#define OCF_LE_READ_WHITE_LIST_SIZE 0x000F
+typedef struct {
+ uint8_t status;
+ uint8_t size;
+} __attribute__ ((packed)) le_read_white_list_size_rp;
+#define LE_READ_WHITE_LIST_SIZE_RP_SIZE 2
+
+#define OCF_LE_CLEAR_WHITE_LIST 0x0010
+
+#define OCF_LE_ADD_DEVICE_TO_WHITE_LIST 0x0011
+typedef struct {
+ uint8_t bdaddr_type;
+ bdaddr_t bdaddr;
+} __attribute__ ((packed)) le_add_device_to_white_list_cp;
+#define LE_ADD_DEVICE_TO_WHITE_LIST_CP_SIZE 7
+
+#define OCF_LE_REMOVE_DEVICE_FROM_WHITE_LIST 0x0012
+typedef struct {
+ uint8_t bdaddr_type;
+ bdaddr_t bdaddr;
+} __attribute__ ((packed)) le_remove_device_from_white_list_cp;
+#define LE_REMOVE_DEVICE_FROM_WHITE_LIST_CP_SIZE 7
+
+#define OCF_LE_CONN_UPDATE 0x0013
+typedef struct {
+ uint16_t handle;
+ uint16_t min_interval;
+ uint16_t max_interval;
+ uint16_t latency;
+ uint16_t supervision_timeout;
+ uint16_t min_ce_length;
+ uint16_t max_ce_length;
+} __attribute__ ((packed)) le_connection_update_cp;
+#define LE_CONN_UPDATE_CP_SIZE 14
+
+#define OCF_LE_SET_HOST_CHANNEL_CLASSIFICATION 0x0014
+typedef struct {
+ uint8_t map[5];
+} __attribute__ ((packed)) le_set_host_channel_classification_cp;
+#define LE_SET_HOST_CHANNEL_CLASSIFICATION_CP_SIZE 5
+
+#define OCF_LE_READ_CHANNEL_MAP 0x0015
+typedef struct {
+ uint16_t handle;
+} __attribute__ ((packed)) le_read_channel_map_cp;
+#define LE_READ_CHANNEL_MAP_CP_SIZE 2
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t map[5];
+} __attribute__ ((packed)) le_read_channel_map_rp;
+#define LE_READ_CHANNEL_MAP_RP_SIZE 8
+
+#define OCF_LE_READ_REMOTE_USED_FEATURES 0x0016
+typedef struct {
+ uint16_t handle;
+} __attribute__ ((packed)) le_read_remote_used_features_cp;
+#define LE_READ_REMOTE_USED_FEATURES_CP_SIZE 2
+
+#define OCF_LE_ENCRYPT 0x0017
+typedef struct {
+ uint8_t key[16];
+ uint8_t plaintext[16];
+} __attribute__ ((packed)) le_encrypt_cp;
+#define LE_ENCRYPT_CP_SIZE 32
+typedef struct {
+ uint8_t status;
+ uint8_t data[16];
+} __attribute__ ((packed)) le_encrypt_rp;
+#define LE_ENCRYPT_RP_SIZE 17
+
+#define OCF_LE_RAND 0x0018
+typedef struct {
+ uint8_t status;
+ uint64_t random;
+} __attribute__ ((packed)) le_rand_rp;
+#define LE_RAND_RP_SIZE 9
+
+#define OCF_LE_START_ENCRYPTION 0x0019
+typedef struct {
+ uint16_t handle;
+ uint64_t random;
+ uint16_t diversifier;
+ uint8_t key[16];
+} __attribute__ ((packed)) le_start_encryption_cp;
+#define LE_START_ENCRYPTION_CP_SIZE 28
+
+#define OCF_LE_LTK_REPLY 0x001A
+typedef struct {
+ uint16_t handle;
+ uint8_t key[16];
+} __attribute__ ((packed)) le_ltk_reply_cp;
+#define LE_LTK_REPLY_CP_SIZE 18
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+} __attribute__ ((packed)) le_ltk_reply_rp;
+#define LE_LTK_REPLY_RP_SIZE 3
+
+#define OCF_LE_LTK_NEG_REPLY 0x001B
+typedef struct {
+ uint16_t handle;
+} __attribute__ ((packed)) le_ltk_neg_reply_cp;
+#define LE_LTK_NEG_REPLY_CP_SIZE 2
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+} __attribute__ ((packed)) le_ltk_neg_reply_rp;
+#define LE_LTK_NEG_REPLY_RP_SIZE 3
+
+#define OCF_LE_READ_SUPPORTED_STATES 0x001C
+typedef struct {
+ uint8_t status;
+ uint64_t states;
+} __attribute__ ((packed)) le_read_supported_states_rp;
+#define LE_READ_SUPPORTED_STATES_RP_SIZE 9
+
+#define OCF_LE_RECEIVER_TEST 0x001D
+typedef struct {
+ uint8_t frequency;
+} __attribute__ ((packed)) le_receiver_test_cp;
+#define LE_RECEIVER_TEST_CP_SIZE 1
+
+#define OCF_LE_TRANSMITTER_TEST 0x001E
+typedef struct {
+ uint8_t frequency;
+ uint8_t length;
+ uint8_t payload;
+} __attribute__ ((packed)) le_transmitter_test_cp;
+#define LE_TRANSMITTER_TEST_CP_SIZE 3
+
+#define OCF_LE_TEST_END 0x001F
+typedef struct {
+ uint8_t status;
+ uint16_t num_pkts;
+} __attribute__ ((packed)) le_test_end_rp;
+#define LE_TEST_END_RP_SIZE 3
+
+/* Vendor specific commands */
+#define OGF_VENDOR_CMD 0x3f
+
+/* ---- HCI Events ---- */
+
+#define EVT_INQUIRY_COMPLETE 0x01
+
+#define EVT_INQUIRY_RESULT 0x02
+typedef struct {
+ bdaddr_t bdaddr;
+ uint8_t pscan_rep_mode;
+ uint8_t pscan_period_mode;
+ uint8_t pscan_mode;
+ uint8_t dev_class[3];
+ uint16_t clock_offset;
+} __attribute__ ((packed)) inquiry_info;
+#define INQUIRY_INFO_SIZE 14
+
+#define EVT_CONN_COMPLETE 0x03
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ bdaddr_t bdaddr;
+ uint8_t link_type;
+ uint8_t encr_mode;
+} __attribute__ ((packed)) evt_conn_complete;
+#define EVT_CONN_COMPLETE_SIZE 13
+
+#define EVT_CONN_REQUEST 0x04
+typedef struct {
+ bdaddr_t bdaddr;
+ uint8_t dev_class[3];
+ uint8_t link_type;
+} __attribute__ ((packed)) evt_conn_request;
+#define EVT_CONN_REQUEST_SIZE 10
+
+#define EVT_DISCONN_COMPLETE 0x05
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t reason;
+} __attribute__ ((packed)) evt_disconn_complete;
+#define EVT_DISCONN_COMPLETE_SIZE 4
+
+#define EVT_AUTH_COMPLETE 0x06
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+} __attribute__ ((packed)) evt_auth_complete;
+#define EVT_AUTH_COMPLETE_SIZE 3
+
+#define EVT_REMOTE_NAME_REQ_COMPLETE 0x07
+typedef struct {
+ uint8_t status;
+ bdaddr_t bdaddr;
+ uint8_t name[248];
+} __attribute__ ((packed)) evt_remote_name_req_complete;
+#define EVT_REMOTE_NAME_REQ_COMPLETE_SIZE 255
+
+#define EVT_ENCRYPT_CHANGE 0x08
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t encrypt;
+} __attribute__ ((packed)) evt_encrypt_change;
+#define EVT_ENCRYPT_CHANGE_SIZE 5
+
+#define EVT_CHANGE_CONN_LINK_KEY_COMPLETE 0x09
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+} __attribute__ ((packed)) evt_change_conn_link_key_complete;
+#define EVT_CHANGE_CONN_LINK_KEY_COMPLETE_SIZE 3
+
+#define EVT_MASTER_LINK_KEY_COMPLETE 0x0A
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t key_flag;
+} __attribute__ ((packed)) evt_master_link_key_complete;
+#define EVT_MASTER_LINK_KEY_COMPLETE_SIZE 4
+
+#define EVT_READ_REMOTE_FEATURES_COMPLETE 0x0B
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t features[8];
+} __attribute__ ((packed)) evt_read_remote_features_complete;
+#define EVT_READ_REMOTE_FEATURES_COMPLETE_SIZE 11
+
+#define EVT_READ_REMOTE_VERSION_COMPLETE 0x0C
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t lmp_ver;
+ uint16_t manufacturer;
+ uint16_t lmp_subver;
+} __attribute__ ((packed)) evt_read_remote_version_complete;
+#define EVT_READ_REMOTE_VERSION_COMPLETE_SIZE 8
+
+#define EVT_QOS_SETUP_COMPLETE 0x0D
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t flags; /* Reserved */
+ hci_qos qos;
+} __attribute__ ((packed)) evt_qos_setup_complete;
+#define EVT_QOS_SETUP_COMPLETE_SIZE (4 + HCI_QOS_CP_SIZE)
+
+#define EVT_CMD_COMPLETE 0x0E
+typedef struct {
+ uint8_t ncmd;
+ uint16_t opcode;
+} __attribute__ ((packed)) evt_cmd_complete;
+#define EVT_CMD_COMPLETE_SIZE 3
+
+#define EVT_CMD_STATUS 0x0F
+typedef struct {
+ uint8_t status;
+ uint8_t ncmd;
+ uint16_t opcode;
+} __attribute__ ((packed)) evt_cmd_status;
+#define EVT_CMD_STATUS_SIZE 4
+
+#define EVT_HARDWARE_ERROR 0x10
+typedef struct {
+ uint8_t code;
+} __attribute__ ((packed)) evt_hardware_error;
+#define EVT_HARDWARE_ERROR_SIZE 1
+
+#define EVT_FLUSH_OCCURRED 0x11
+typedef struct {
+ uint16_t handle;
+} __attribute__ ((packed)) evt_flush_occured;
+#define EVT_FLUSH_OCCURRED_SIZE 2
+
+#define EVT_ROLE_CHANGE 0x12
+typedef struct {
+ uint8_t status;
+ bdaddr_t bdaddr;
+ uint8_t role;
+} __attribute__ ((packed)) evt_role_change;
+#define EVT_ROLE_CHANGE_SIZE 8
+
+#define EVT_NUM_COMP_PKTS 0x13
+typedef struct {
+ uint8_t num_hndl;
+ /* variable length part */
+} __attribute__ ((packed)) evt_num_comp_pkts;
+#define EVT_NUM_COMP_PKTS_SIZE 1
+
+#define EVT_MODE_CHANGE 0x14
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t mode;
+ uint16_t interval;
+} __attribute__ ((packed)) evt_mode_change;
+#define EVT_MODE_CHANGE_SIZE 6
+
+#define EVT_RETURN_LINK_KEYS 0x15
+typedef struct {
+ uint8_t num_keys;
+ /* variable length part */
+} __attribute__ ((packed)) evt_return_link_keys;
+#define EVT_RETURN_LINK_KEYS_SIZE 1
+
+#define EVT_PIN_CODE_REQ 0x16
+typedef struct {
+ bdaddr_t bdaddr;
+} __attribute__ ((packed)) evt_pin_code_req;
+#define EVT_PIN_CODE_REQ_SIZE 6
+
+#define EVT_LINK_KEY_REQ 0x17
+typedef struct {
+ bdaddr_t bdaddr;
+} __attribute__ ((packed)) evt_link_key_req;
+#define EVT_LINK_KEY_REQ_SIZE 6
+
+#define EVT_LINK_KEY_NOTIFY 0x18
+typedef struct {
+ bdaddr_t bdaddr;
+ uint8_t link_key[16];
+ uint8_t key_type;
+} __attribute__ ((packed)) evt_link_key_notify;
+#define EVT_LINK_KEY_NOTIFY_SIZE 23
+
+#define EVT_LOOPBACK_COMMAND 0x19
+
+#define EVT_DATA_BUFFER_OVERFLOW 0x1A
+typedef struct {
+ uint8_t link_type;
+} __attribute__ ((packed)) evt_data_buffer_overflow;
+#define EVT_DATA_BUFFER_OVERFLOW_SIZE 1
+
+#define EVT_MAX_SLOTS_CHANGE 0x1B
+typedef struct {
+ uint16_t handle;
+ uint8_t max_slots;
+} __attribute__ ((packed)) evt_max_slots_change;
+#define EVT_MAX_SLOTS_CHANGE_SIZE 3
+
+#define EVT_READ_CLOCK_OFFSET_COMPLETE 0x1C
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint16_t clock_offset;
+} __attribute__ ((packed)) evt_read_clock_offset_complete;
+#define EVT_READ_CLOCK_OFFSET_COMPLETE_SIZE 5
+
+#define EVT_CONN_PTYPE_CHANGED 0x1D
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint16_t ptype;
+} __attribute__ ((packed)) evt_conn_ptype_changed;
+#define EVT_CONN_PTYPE_CHANGED_SIZE 5
+
+#define EVT_QOS_VIOLATION 0x1E
+typedef struct {
+ uint16_t handle;
+} __attribute__ ((packed)) evt_qos_violation;
+#define EVT_QOS_VIOLATION_SIZE 2
+
+#define EVT_PSCAN_REP_MODE_CHANGE 0x20
+typedef struct {
+ bdaddr_t bdaddr;
+ uint8_t pscan_rep_mode;
+} __attribute__ ((packed)) evt_pscan_rep_mode_change;
+#define EVT_PSCAN_REP_MODE_CHANGE_SIZE 7
+
+#define EVT_FLOW_SPEC_COMPLETE 0x21
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t flags;
+ uint8_t direction;
+ hci_qos qos;
+} __attribute__ ((packed)) evt_flow_spec_complete;
+#define EVT_FLOW_SPEC_COMPLETE_SIZE (5 + HCI_QOS_CP_SIZE)
+
+#define EVT_INQUIRY_RESULT_WITH_RSSI 0x22
+typedef struct {
+ bdaddr_t bdaddr;
+ uint8_t pscan_rep_mode;
+ uint8_t pscan_period_mode;
+ uint8_t dev_class[3];
+ uint16_t clock_offset;
+ int8_t rssi;
+} __attribute__ ((packed)) inquiry_info_with_rssi;
+#define INQUIRY_INFO_WITH_RSSI_SIZE 14
+typedef struct {
+ bdaddr_t bdaddr;
+ uint8_t pscan_rep_mode;
+ uint8_t pscan_period_mode;
+ uint8_t pscan_mode;
+ uint8_t dev_class[3];
+ uint16_t clock_offset;
+ int8_t rssi;
+} __attribute__ ((packed)) inquiry_info_with_rssi_and_pscan_mode;
+#define INQUIRY_INFO_WITH_RSSI_AND_PSCAN_MODE_SIZE 15
+
+#define EVT_READ_REMOTE_EXT_FEATURES_COMPLETE 0x23
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t page_num;
+ uint8_t max_page_num;
+ uint8_t features[8];
+} __attribute__ ((packed)) evt_read_remote_ext_features_complete;
+#define EVT_READ_REMOTE_EXT_FEATURES_COMPLETE_SIZE 13
+
+#define EVT_SYNC_CONN_COMPLETE 0x2C
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ bdaddr_t bdaddr;
+ uint8_t link_type;
+ uint8_t trans_interval;
+ uint8_t retrans_window;
+ uint16_t rx_pkt_len;
+ uint16_t tx_pkt_len;
+ uint8_t air_mode;
+} __attribute__ ((packed)) evt_sync_conn_complete;
+#define EVT_SYNC_CONN_COMPLETE_SIZE 17
+
+#define EVT_SYNC_CONN_CHANGED 0x2D
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t trans_interval;
+ uint8_t retrans_window;
+ uint16_t rx_pkt_len;
+ uint16_t tx_pkt_len;
+} __attribute__ ((packed)) evt_sync_conn_changed;
+#define EVT_SYNC_CONN_CHANGED_SIZE 9
+
+#define EVT_SNIFF_SUBRATING 0x2E
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint16_t max_tx_latency;
+ uint16_t max_rx_latency;
+ uint16_t min_remote_timeout;
+ uint16_t min_local_timeout;
+} __attribute__ ((packed)) evt_sniff_subrating;
+#define EVT_SNIFF_SUBRATING_SIZE 11
+
+#define EVT_EXTENDED_INQUIRY_RESULT 0x2F
+typedef struct {
+ bdaddr_t bdaddr;
+ uint8_t pscan_rep_mode;
+ uint8_t pscan_period_mode;
+ uint8_t dev_class[3];
+ uint16_t clock_offset;
+ int8_t rssi;
+ uint8_t data[240];
+} __attribute__ ((packed)) extended_inquiry_info;
+#define EXTENDED_INQUIRY_INFO_SIZE 254
+
+#define EVT_ENCRYPTION_KEY_REFRESH_COMPLETE 0x30
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+} __attribute__ ((packed)) evt_encryption_key_refresh_complete;
+#define EVT_ENCRYPTION_KEY_REFRESH_COMPLETE_SIZE 3
+
+#define EVT_IO_CAPABILITY_REQUEST 0x31
+typedef struct {
+ bdaddr_t bdaddr;
+} __attribute__ ((packed)) evt_io_capability_request;
+#define EVT_IO_CAPABILITY_REQUEST_SIZE 6
+
+#define EVT_IO_CAPABILITY_RESPONSE 0x32
+typedef struct {
+ bdaddr_t bdaddr;
+ uint8_t capability;
+ uint8_t oob_data;
+ uint8_t authentication;
+} __attribute__ ((packed)) evt_io_capability_response;
+#define EVT_IO_CAPABILITY_RESPONSE_SIZE 9
+
+#define EVT_USER_CONFIRM_REQUEST 0x33
+typedef struct {
+ bdaddr_t bdaddr;
+ uint32_t passkey;
+} __attribute__ ((packed)) evt_user_confirm_request;
+#define EVT_USER_CONFIRM_REQUEST_SIZE 10
+
+#define EVT_USER_PASSKEY_REQUEST 0x34
+typedef struct {
+ bdaddr_t bdaddr;
+} __attribute__ ((packed)) evt_user_passkey_request;
+#define EVT_USER_PASSKEY_REQUEST_SIZE 6
+
+#define EVT_REMOTE_OOB_DATA_REQUEST 0x35
+typedef struct {
+ bdaddr_t bdaddr;
+} __attribute__ ((packed)) evt_remote_oob_data_request;
+#define EVT_REMOTE_OOB_DATA_REQUEST_SIZE 6
+
+#define EVT_SIMPLE_PAIRING_COMPLETE 0x36
+typedef struct {
+ uint8_t status;
+ bdaddr_t bdaddr;
+} __attribute__ ((packed)) evt_simple_pairing_complete;
+#define EVT_SIMPLE_PAIRING_COMPLETE_SIZE 7
+
+#define EVT_LINK_SUPERVISION_TIMEOUT_CHANGED 0x38
+typedef struct {
+ uint16_t handle;
+ uint16_t timeout;
+} __attribute__ ((packed)) evt_link_supervision_timeout_changed;
+#define EVT_LINK_SUPERVISION_TIMEOUT_CHANGED_SIZE 4
+
+#define EVT_ENHANCED_FLUSH_COMPLETE 0x39
+typedef struct {
+ uint16_t handle;
+} __attribute__ ((packed)) evt_enhanced_flush_complete;
+#define EVT_ENHANCED_FLUSH_COMPLETE_SIZE 2
+
+#define EVT_USER_PASSKEY_NOTIFY 0x3B
+typedef struct {
+ bdaddr_t bdaddr;
+ uint32_t passkey;
+} __attribute__ ((packed)) evt_user_passkey_notify;
+#define EVT_USER_PASSKEY_NOTIFY_SIZE 10
+
+#define EVT_KEYPRESS_NOTIFY 0x3C
+typedef struct {
+ bdaddr_t bdaddr;
+ uint8_t type;
+} __attribute__ ((packed)) evt_keypress_notify;
+#define EVT_KEYPRESS_NOTIFY_SIZE 7
+
+#define EVT_REMOTE_HOST_FEATURES_NOTIFY 0x3D
+typedef struct {
+ bdaddr_t bdaddr;
+ uint8_t features[8];
+} __attribute__ ((packed)) evt_remote_host_features_notify;
+#define EVT_REMOTE_HOST_FEATURES_NOTIFY_SIZE 14
+
+#define EVT_LE_META_EVENT 0x3E
+typedef struct {
+ uint8_t subevent;
+ uint8_t data[0];
+} __attribute__ ((packed)) evt_le_meta_event;
+#define EVT_LE_META_EVENT_SIZE 1
+
+#define EVT_LE_CONN_COMPLETE 0x01
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t role;
+ uint8_t peer_bdaddr_type;
+ bdaddr_t peer_bdaddr;
+ uint16_t interval;
+ uint16_t latency;
+ uint16_t supervision_timeout;
+ uint8_t master_clock_accuracy;
+} __attribute__ ((packed)) evt_le_connection_complete;
+#define EVT_LE_CONN_COMPLETE_SIZE 18
+
+#define EVT_LE_ADVERTISING_REPORT 0x02
+typedef struct {
+ uint8_t evt_type;
+ uint8_t bdaddr_type;
+ bdaddr_t bdaddr;
+ uint8_t length;
+ uint8_t data[0];
+} __attribute__ ((packed)) le_advertising_info;
+#define LE_ADVERTISING_INFO_SIZE 9
+
+#define EVT_LE_CONN_UPDATE_COMPLETE 0x03
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint16_t interval;
+ uint16_t latency;
+ uint16_t supervision_timeout;
+} __attribute__ ((packed)) evt_le_connection_update_complete;
+#define EVT_LE_CONN_UPDATE_COMPLETE_SIZE 9
+
+#define EVT_LE_READ_REMOTE_USED_FEATURES_COMPLETE 0x04
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t features[8];
+} __attribute__ ((packed)) evt_le_read_remote_used_features_complete;
+#define EVT_LE_READ_REMOTE_USED_FEATURES_COMPLETE_SIZE 11
+
+#define EVT_LE_LTK_REQUEST 0x05
+typedef struct {
+ uint16_t handle;
+ uint64_t random;
+ uint16_t diversifier;
+} __attribute__ ((packed)) evt_le_long_term_key_request;
+#define EVT_LE_LTK_REQUEST_SIZE 12
+
+#define EVT_PHYSICAL_LINK_COMPLETE 0x40
+typedef struct {
+ uint8_t status;
+ uint8_t handle;
+} __attribute__ ((packed)) evt_physical_link_complete;
+#define EVT_PHYSICAL_LINK_COMPLETE_SIZE 2
+
+#define EVT_CHANNEL_SELECTED 0x41
+
+#define EVT_DISCONNECT_PHYSICAL_LINK_COMPLETE 0x42
+typedef struct {
+ uint8_t status;
+ uint8_t handle;
+ uint8_t reason;
+} __attribute__ ((packed)) evt_disconn_physical_link_complete;
+#define EVT_DISCONNECT_PHYSICAL_LINK_COMPLETE_SIZE 3
+
+#define EVT_PHYSICAL_LINK_LOSS_EARLY_WARNING 0x43
+typedef struct {
+ uint8_t handle;
+ uint8_t reason;
+} __attribute__ ((packed)) evt_physical_link_loss_warning;
+#define EVT_PHYSICAL_LINK_LOSS_WARNING_SIZE 2
+
+#define EVT_PHYSICAL_LINK_RECOVERY 0x44
+typedef struct {
+ uint8_t handle;
+} __attribute__ ((packed)) evt_physical_link_recovery;
+#define EVT_PHYSICAL_LINK_RECOVERY_SIZE 1
+
+#define EVT_LOGICAL_LINK_COMPLETE 0x45
+typedef struct {
+ uint8_t status;
+ uint16_t log_handle;
+ uint8_t handle;
+ uint8_t tx_flow_id;
+} __attribute__ ((packed)) evt_logical_link_complete;
+#define EVT_LOGICAL_LINK_COMPLETE_SIZE 5
+
+#define EVT_DISCONNECT_LOGICAL_LINK_COMPLETE 0x46
+
+#define EVT_FLOW_SPEC_MODIFY_COMPLETE 0x47
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+} __attribute__ ((packed)) evt_flow_spec_modify_complete;
+#define EVT_FLOW_SPEC_MODIFY_COMPLETE_SIZE 3
+
+#define EVT_NUMBER_COMPLETED_BLOCKS 0x48
+
+#define EVT_AMP_STATUS_CHANGE 0x4D
+typedef struct {
+ uint8_t status;
+ uint8_t amp_status;
+} __attribute__ ((packed)) evt_amp_status_change;
+#define EVT_AMP_STATUS_CHANGE_SIZE 2
+
+#define EVT_TESTING 0xFE
+
+#define EVT_VENDOR 0xFF
+
+/* Internal events generated by BlueZ stack */
+#define EVT_STACK_INTERNAL 0xFD
+typedef struct {
+ uint16_t type;
+ uint8_t data[0];
+} __attribute__ ((packed)) evt_stack_internal;
+#define EVT_STACK_INTERNAL_SIZE 2
+
+#define EVT_SI_DEVICE 0x01
+typedef struct {
+ uint16_t event;
+ uint16_t dev_id;
+} __attribute__ ((packed)) evt_si_device;
+#define EVT_SI_DEVICE_SIZE 4
+
+/* -------- HCI Packet structures -------- */
+#define HCI_TYPE_LEN 1
+
+typedef struct {
+ uint16_t opcode; /* OCF & OGF */
+ uint8_t plen;
+} __attribute__ ((packed)) hci_command_hdr;
+#define HCI_COMMAND_HDR_SIZE 3
+
+typedef struct {
+ uint8_t evt;
+ uint8_t plen;
+} __attribute__ ((packed)) hci_event_hdr;
+#define HCI_EVENT_HDR_SIZE 2
+
+typedef struct {
+ uint16_t handle; /* Handle & Flags(PB, BC) */
+ uint16_t dlen;
+} __attribute__ ((packed)) hci_acl_hdr;
+#define HCI_ACL_HDR_SIZE 4
+
+typedef struct {
+ uint16_t handle;
+ uint8_t dlen;
+} __attribute__ ((packed)) hci_sco_hdr;
+#define HCI_SCO_HDR_SIZE 3
+
+typedef struct {
+ uint16_t device;
+ uint16_t type;
+ uint16_t plen;
+} __attribute__ ((packed)) hci_msg_hdr;
+#define HCI_MSG_HDR_SIZE 6
+
+/* Command opcode pack/unpack */
+#define cmd_opcode_pack(ogf, ocf) (uint16_t)((ocf & 0x03ff)|(ogf << 10))
+#define cmd_opcode_ogf(op) (op >> 10)
+#define cmd_opcode_ocf(op) (op & 0x03ff)
+
+/* ACL handle and flags pack/unpack */
+#define acl_handle_pack(h, f) (uint16_t)((h & 0x0fff)|(f << 12))
+#define acl_handle(h) (h & 0x0fff)
+#define acl_flags(h) (h >> 12)
+
+#endif /* _NO_HCI_DEFS */
+
+/* HCI Socket options */
+#define HCI_DATA_DIR 1
+#define HCI_FILTER 2
+#define HCI_TIME_STAMP 3
+
+/* HCI CMSG flags */
+#define HCI_CMSG_DIR 0x0001
+#define HCI_CMSG_TSTAMP 0x0002
+
+struct sockaddr_hci {
+ sa_family_t hci_family;
+ unsigned short hci_dev;
+ unsigned short hci_channel;
+};
+#define HCI_DEV_NONE 0xffff
+
+#define HCI_CHANNEL_RAW 0
+#define HCI_CHANNEL_CONTROL 1
+
+struct hci_filter {
+ uint32_t type_mask;
+ uint32_t event_mask[2];
+ uint16_t opcode;
+};
+
+#define HCI_FLT_TYPE_BITS 31
+#define HCI_FLT_EVENT_BITS 63
+#define HCI_FLT_OGF_BITS 63
+#define HCI_FLT_OCF_BITS 127
+
+/* Ioctl requests structures */
+struct hci_dev_stats {
+ uint32_t err_rx;
+ uint32_t err_tx;
+ uint32_t cmd_tx;
+ uint32_t evt_rx;
+ uint32_t acl_tx;
+ uint32_t acl_rx;
+ uint32_t sco_tx;
+ uint32_t sco_rx;
+ uint32_t byte_rx;
+ uint32_t byte_tx;
+};
+
+struct hci_dev_info {
+ uint16_t dev_id;
+ char name[8];
+
+ bdaddr_t bdaddr;
+
+ uint32_t flags;
+ uint8_t type;
+
+ uint8_t features[8];
+
+ uint32_t pkt_type;
+ uint32_t link_policy;
+ uint32_t link_mode;
+
+ uint16_t acl_mtu;
+ uint16_t acl_pkts;
+ uint16_t sco_mtu;
+ uint16_t sco_pkts;
+
+ struct hci_dev_stats stat;
+};
+
+struct hci_conn_info {
+ uint16_t handle;
+ bdaddr_t bdaddr;
+ uint8_t type;
+ uint8_t out;
+ uint16_t state;
+ uint32_t link_mode;
+};
+
+struct hci_dev_req {
+ uint16_t dev_id;
+ uint32_t dev_opt;
+};
+
+struct hci_dev_list_req {
+ uint16_t dev_num;
+ struct hci_dev_req dev_req[0]; /* hci_dev_req structures */
+};
+
+struct hci_conn_list_req {
+ uint16_t dev_id;
+ uint16_t conn_num;
+ struct hci_conn_info conn_info[0];
+};
+
+struct hci_conn_info_req {
+ bdaddr_t bdaddr;
+ uint8_t type;
+ struct hci_conn_info conn_info[0];
+};
+
+struct hci_auth_info_req {
+ bdaddr_t bdaddr;
+ uint8_t type;
+};
+
+struct hci_inquiry_req {
+ uint16_t dev_id;
+ uint16_t flags;
+ uint8_t lap[3];
+ uint8_t length;
+ uint8_t num_rsp;
+};
+#define IREQ_CACHE_FLUSH 0x0001
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __HCI_H */
diff --git a/lib/hci_lib.h b/lib/hci_lib.h
new file mode 100644
index 0000000..725eb05
--- /dev/null
+++ b/lib/hci_lib.h
@@ -0,0 +1,233 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2000-2001 Qualcomm Incorporated
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 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
+ *
+ */
+
+#ifndef __HCI_LIB_H
+#define __HCI_LIB_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct hci_request {
+ uint16_t ogf;
+ uint16_t ocf;
+ int event;
+ void *cparam;
+ int clen;
+ void *rparam;
+ int rlen;
+};
+
+struct hci_version {
+ uint16_t manufacturer;
+ uint8_t hci_ver;
+ uint16_t hci_rev;
+ uint8_t lmp_ver;
+ uint16_t lmp_subver;
+};
+
+int hci_open_dev(int dev_id);
+int hci_close_dev(int dd);
+int hci_send_cmd(int dd, uint16_t ogf, uint16_t ocf, uint8_t plen, void *param);
+int hci_send_req(int dd, struct hci_request *req, int timeout);
+
+int hci_create_connection(int dd, const bdaddr_t *bdaddr, uint16_t ptype, uint16_t clkoffset, uint8_t rswitch, uint16_t *handle, int to);
+int hci_disconnect(int dd, uint16_t handle, uint8_t reason, int to);
+
+int hci_inquiry(int dev_id, int len, int num_rsp, const uint8_t *lap, inquiry_info **ii, long flags);
+int hci_devinfo(int dev_id, struct hci_dev_info *di);
+int hci_devba(int dev_id, bdaddr_t *bdaddr);
+int hci_devid(const char *str);
+
+int hci_read_local_name(int dd, int len, char *name, int to);
+int hci_write_local_name(int dd, const char *name, int to);
+int hci_read_remote_name(int dd, const bdaddr_t *bdaddr, int len, char *name, int to);
+int hci_read_remote_name_with_clock_offset(int dd, const bdaddr_t *bdaddr, uint8_t pscan_rep_mode, uint16_t clkoffset, int len, char *name, int to);
+int hci_read_remote_name_cancel(int dd, const bdaddr_t *bdaddr, int to);
+int hci_read_remote_version(int dd, uint16_t handle, struct hci_version *ver, int to);
+int hci_read_remote_features(int dd, uint16_t handle, uint8_t *features, int to);
+int hci_read_remote_ext_features(int dd, uint16_t handle, uint8_t page, uint8_t *max_page, uint8_t *features, int to);
+int hci_read_clock_offset(int dd, uint16_t handle, uint16_t *clkoffset, int to);
+int hci_read_local_version(int dd, struct hci_version *ver, int to);
+int hci_read_local_commands(int dd, uint8_t *commands, int to);
+int hci_read_local_features(int dd, uint8_t *features, int to);
+int hci_read_local_ext_features(int dd, uint8_t page, uint8_t *max_page, uint8_t *features, int to);
+int hci_read_bd_addr(int dd, bdaddr_t *bdaddr, int to);
+int hci_read_class_of_dev(int dd, uint8_t *cls, int to);
+int hci_write_class_of_dev(int dd, uint32_t cls, int to);
+int hci_read_voice_setting(int dd, uint16_t *vs, int to);
+int hci_write_voice_setting(int dd, uint16_t vs, int to);
+int hci_read_current_iac_lap(int dd, uint8_t *num_iac, uint8_t *lap, int to);
+int hci_write_current_iac_lap(int dd, uint8_t num_iac, uint8_t *lap, int to);
+int hci_read_stored_link_key(int dd, bdaddr_t *bdaddr, uint8_t all, int to);
+int hci_write_stored_link_key(int dd, bdaddr_t *bdaddr, uint8_t *key, int to);
+int hci_delete_stored_link_key(int dd, bdaddr_t *bdaddr, uint8_t all, int to);
+int hci_authenticate_link(int dd, uint16_t handle, int to);
+int hci_encrypt_link(int dd, uint16_t handle, uint8_t encrypt, int to);
+int hci_change_link_key(int dd, uint16_t handle, int to);
+int hci_switch_role(int dd, bdaddr_t *bdaddr, uint8_t role, int to);
+int hci_park_mode(int dd, uint16_t handle, uint16_t max_interval, uint16_t min_interval, int to);
+int hci_exit_park_mode(int dd, uint16_t handle, int to);
+int hci_read_inquiry_scan_type(int dd, uint8_t *type, int to);
+int hci_write_inquiry_scan_type(int dd, uint8_t type, int to);
+int hci_read_inquiry_mode(int dd, uint8_t *mode, int to);
+int hci_write_inquiry_mode(int dd, uint8_t mode, int to);
+int hci_read_afh_mode(int dd, uint8_t *mode, int to);
+int hci_write_afh_mode(int dd, uint8_t mode, int to);
+int hci_read_ext_inquiry_response(int dd, uint8_t *fec, uint8_t *data, int to);
+int hci_write_ext_inquiry_response(int dd, uint8_t fec, uint8_t *data, int to);
+int hci_read_simple_pairing_mode(int dd, uint8_t *mode, int to);
+int hci_write_simple_pairing_mode(int dd, uint8_t mode, int to);
+int hci_read_local_oob_data(int dd, uint8_t *hash, uint8_t *randomizer, int to);
+int hci_read_inq_response_tx_power_level(int dd, int8_t *level, int to);
+int hci_read_inquiry_transmit_power_level(int dd, int8_t *level, int to);
+int hci_write_inquiry_transmit_power_level(int dd, int8_t level, int to);
+int hci_read_transmit_power_level(int dd, uint16_t handle, uint8_t type, int8_t *level, int to);
+int hci_read_link_policy(int dd, uint16_t handle, uint16_t *policy, int to);
+int hci_write_link_policy(int dd, uint16_t handle, uint16_t policy, int to);
+int hci_read_link_supervision_timeout(int dd, uint16_t handle, uint16_t *timeout, int to);
+int hci_write_link_supervision_timeout(int dd, uint16_t handle, uint16_t timeout, int to);
+int hci_set_afh_classification(int dd, uint8_t *map, int to);
+int hci_read_link_quality(int dd, uint16_t handle, uint8_t *link_quality, int to);
+int hci_read_rssi(int dd, uint16_t handle, int8_t *rssi, int to);
+int hci_read_afh_map(int dd, uint16_t handle, uint8_t *mode, uint8_t *map, int to);
+int hci_read_clock(int dd, uint16_t handle, uint8_t which, uint32_t *clock, uint16_t *accuracy, int to);
+
+int hci_le_set_scan_enable(int dev_id, uint8_t enable, uint8_t filter_dup, int to);
+int hci_le_set_scan_parameters(int dev_id, uint8_t type, uint16_t interval,
+ uint16_t window, uint8_t own_type,
+ uint8_t filter, int to);
+int hci_le_set_advertise_enable(int dev_id, uint8_t enable, int to);
+int hci_le_create_conn(int dd, uint16_t interval, uint16_t window,
+ uint8_t initiator_filter, uint8_t peer_bdaddr_type,
+ bdaddr_t peer_bdaddr, uint8_t own_bdaddr_type,
+ uint16_t min_interval, uint16_t max_interval,
+ uint16_t latency, uint16_t supervision_timeout,
+ uint16_t min_ce_length, uint16_t max_ce_length,
+ uint16_t *handle, int to);
+
+int hci_le_conn_update(int dd, uint16_t handle, uint16_t min_interval,
+ uint16_t max_interval, uint16_t latency,
+ uint16_t supervision_timeout, int to);
+int hci_le_add_white_list(int dd, const bdaddr_t *bdaddr, uint8_t type, int to);
+int hci_le_rm_white_list(int dd, const bdaddr_t *bdaddr, uint8_t type, int to);
+int hci_le_read_white_list_size(int dd, uint8_t *size, int to);
+int hci_le_clear_white_list(int dd, int to);
+int hci_for_each_dev(int flag, int(*func)(int dd, int dev_id, long arg), long arg);
+int hci_get_route(bdaddr_t *bdaddr);
+
+char *hci_bustostr(int bus);
+char *hci_typetostr(int type);
+char *hci_dtypetostr(int type);
+char *hci_dflagstostr(uint32_t flags);
+char *hci_ptypetostr(unsigned int ptype);
+int hci_strtoptype(char *str, unsigned int *val);
+char *hci_scoptypetostr(unsigned int ptype);
+int hci_strtoscoptype(char *str, unsigned int *val);
+char *hci_lptostr(unsigned int ptype);
+int hci_strtolp(char *str, unsigned int *val);
+char *hci_lmtostr(unsigned int ptype);
+int hci_strtolm(char *str, unsigned int *val);
+
+char *hci_cmdtostr(unsigned int cmd);
+char *hci_commandstostr(uint8_t *commands, char *pref, int width);
+
+char *hci_vertostr(unsigned int ver);
+int hci_strtover(char *str, unsigned int *ver);
+char *lmp_vertostr(unsigned int ver);
+int lmp_strtover(char *str, unsigned int *ver);
+
+char *lmp_featurestostr(uint8_t *features, char *pref, int width);
+
+static inline void hci_set_bit(int nr, void *addr)
+{
+ *((uint32_t *) addr + (nr >> 5)) |= (1 << (nr & 31));
+}
+
+static inline void hci_clear_bit(int nr, void *addr)
+{
+ *((uint32_t *) addr + (nr >> 5)) &= ~(1 << (nr & 31));
+}
+
+static inline int hci_test_bit(int nr, void *addr)
+{
+ return *((uint32_t *) addr + (nr >> 5)) & (1 << (nr & 31));
+}
+
+/* HCI filter tools */
+static inline void hci_filter_clear(struct hci_filter *f)
+{
+ memset(f, 0, sizeof(*f));
+}
+static inline void hci_filter_set_ptype(int t, struct hci_filter *f)
+{
+ hci_set_bit((t == HCI_VENDOR_PKT) ? 0 : (t & HCI_FLT_TYPE_BITS), &f->type_mask);
+}
+static inline void hci_filter_clear_ptype(int t, struct hci_filter *f)
+{
+ hci_clear_bit((t == HCI_VENDOR_PKT) ? 0 : (t & HCI_FLT_TYPE_BITS), &f->type_mask);
+}
+static inline int hci_filter_test_ptype(int t, struct hci_filter *f)
+{
+ return hci_test_bit((t == HCI_VENDOR_PKT) ? 0 : (t & HCI_FLT_TYPE_BITS), &f->type_mask);
+}
+static inline void hci_filter_all_ptypes(struct hci_filter *f)
+{
+ memset((void *) &f->type_mask, 0xff, sizeof(f->type_mask));
+}
+static inline void hci_filter_set_event(int e, struct hci_filter *f)
+{
+ hci_set_bit((e & HCI_FLT_EVENT_BITS), &f->event_mask);
+}
+static inline void hci_filter_clear_event(int e, struct hci_filter *f)
+{
+ hci_clear_bit((e & HCI_FLT_EVENT_BITS), &f->event_mask);
+}
+static inline int hci_filter_test_event(int e, struct hci_filter *f)
+{
+ return hci_test_bit((e & HCI_FLT_EVENT_BITS), &f->event_mask);
+}
+static inline void hci_filter_all_events(struct hci_filter *f)
+{
+ memset((void *) f->event_mask, 0xff, sizeof(f->event_mask));
+}
+static inline void hci_filter_set_opcode(int opcode, struct hci_filter *f)
+{
+ f->opcode = opcode;
+}
+static inline void hci_filter_clear_opcode(struct hci_filter *f)
+{
+ f->opcode = 0;
+}
+static inline int hci_filter_test_opcode(int opcode, struct hci_filter *f)
+{
+ return (f->opcode == opcode);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __HCI_LIB_H */
diff --git a/lib/hidp.h b/lib/hidp.h
new file mode 100644
index 0000000..c5e6a78
--- /dev/null
+++ b/lib/hidp.h
@@ -0,0 +1,85 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2003-2010 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
+ *
+ */
+
+#ifndef __HIDP_H
+#define __HIDP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* HIDP defaults */
+#define HIDP_MINIMUM_MTU 48
+#define HIDP_DEFAULT_MTU 48
+
+/* HIDP ioctl defines */
+#define HIDPCONNADD _IOW('H', 200, int)
+#define HIDPCONNDEL _IOW('H', 201, int)
+#define HIDPGETCONNLIST _IOR('H', 210, int)
+#define HIDPGETCONNINFO _IOR('H', 211, int)
+
+#define HIDP_VIRTUAL_CABLE_UNPLUG 0
+#define HIDP_BOOT_PROTOCOL_MODE 1
+#define HIDP_BLUETOOTH_VENDOR_ID 9
+
+struct hidp_connadd_req {
+ int ctrl_sock; /* Connected control socket */
+ int intr_sock; /* Connected interrupt socket */
+ uint16_t parser; /* Parser version */
+ uint16_t rd_size; /* Report descriptor size */
+ uint8_t *rd_data; /* Report descriptor data */
+ uint8_t country;
+ uint8_t subclass;
+ uint16_t vendor;
+ uint16_t product;
+ uint16_t version;
+ uint32_t flags;
+ uint32_t idle_to;
+ char name[128]; /* Device name */
+};
+
+struct hidp_conndel_req {
+ bdaddr_t bdaddr;
+ uint32_t flags;
+};
+
+struct hidp_conninfo {
+ bdaddr_t bdaddr;
+ uint32_t flags;
+ uint16_t state;
+ uint16_t vendor;
+ uint16_t product;
+ uint16_t version;
+ char name[128];
+};
+
+struct hidp_connlist_req {
+ uint32_t cnum;
+ struct hidp_conninfo *ci;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __HIDP_H */
diff --git a/lib/l2cap.h b/lib/l2cap.h
new file mode 100644
index 0000000..e59cfdd
--- /dev/null
+++ b/lib/l2cap.h
@@ -0,0 +1,208 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2000-2001 Qualcomm Incorporated
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 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
+ *
+ */
+
+#ifndef __L2CAP_H
+#define __L2CAP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/socket.h>
+
+/* L2CAP defaults */
+#define L2CAP_DEFAULT_MTU 672
+#define L2CAP_DEFAULT_FLUSH_TO 0xFFFF
+
+/* L2CAP socket address */
+struct sockaddr_l2 {
+ sa_family_t l2_family;
+ unsigned short l2_psm;
+ bdaddr_t l2_bdaddr;
+ unsigned short l2_cid;
+};
+
+/* L2CAP socket options */
+#define L2CAP_OPTIONS 0x01
+struct l2cap_options {
+ uint16_t omtu;
+ uint16_t imtu;
+ uint16_t flush_to;
+ uint8_t mode;
+ uint8_t fcs;
+ uint8_t max_tx;
+ uint16_t txwin_size;
+};
+
+#define L2CAP_CONNINFO 0x02
+struct l2cap_conninfo {
+ uint16_t hci_handle;
+ uint8_t dev_class[3];
+};
+
+#define L2CAP_LM 0x03
+#define L2CAP_LM_MASTER 0x0001
+#define L2CAP_LM_AUTH 0x0002
+#define L2CAP_LM_ENCRYPT 0x0004
+#define L2CAP_LM_TRUSTED 0x0008
+#define L2CAP_LM_RELIABLE 0x0010
+#define L2CAP_LM_SECURE 0x0020
+
+/* L2CAP command codes */
+#define L2CAP_COMMAND_REJ 0x01
+#define L2CAP_CONN_REQ 0x02
+#define L2CAP_CONN_RSP 0x03
+#define L2CAP_CONF_REQ 0x04
+#define L2CAP_CONF_RSP 0x05
+#define L2CAP_DISCONN_REQ 0x06
+#define L2CAP_DISCONN_RSP 0x07
+#define L2CAP_ECHO_REQ 0x08
+#define L2CAP_ECHO_RSP 0x09
+#define L2CAP_INFO_REQ 0x0a
+#define L2CAP_INFO_RSP 0x0b
+
+/* L2CAP structures */
+typedef struct {
+ uint16_t len;
+ uint16_t cid;
+} __attribute__ ((packed)) l2cap_hdr;
+#define L2CAP_HDR_SIZE 4
+
+typedef struct {
+ uint8_t code;
+ uint8_t ident;
+ uint16_t len;
+} __attribute__ ((packed)) l2cap_cmd_hdr;
+#define L2CAP_CMD_HDR_SIZE 4
+
+typedef struct {
+ uint16_t reason;
+} __attribute__ ((packed)) l2cap_cmd_rej;
+#define L2CAP_CMD_REJ_SIZE 2
+
+typedef struct {
+ uint16_t psm;
+ uint16_t scid;
+} __attribute__ ((packed)) l2cap_conn_req;
+#define L2CAP_CONN_REQ_SIZE 4
+
+typedef struct {
+ uint16_t dcid;
+ uint16_t scid;
+ uint16_t result;
+ uint16_t status;
+} __attribute__ ((packed)) l2cap_conn_rsp;
+#define L2CAP_CONN_RSP_SIZE 8
+
+/* connect result */
+#define L2CAP_CR_SUCCESS 0x0000
+#define L2CAP_CR_PEND 0x0001
+#define L2CAP_CR_BAD_PSM 0x0002
+#define L2CAP_CR_SEC_BLOCK 0x0003
+#define L2CAP_CR_NO_MEM 0x0004
+
+/* connect status */
+#define L2CAP_CS_NO_INFO 0x0000
+#define L2CAP_CS_AUTHEN_PEND 0x0001
+#define L2CAP_CS_AUTHOR_PEND 0x0002
+
+typedef struct {
+ uint16_t dcid;
+ uint16_t flags;
+ uint8_t data[0];
+} __attribute__ ((packed)) l2cap_conf_req;
+#define L2CAP_CONF_REQ_SIZE 4
+
+typedef struct {
+ uint16_t scid;
+ uint16_t flags;
+ uint16_t result;
+ uint8_t data[0];
+} __attribute__ ((packed)) l2cap_conf_rsp;
+#define L2CAP_CONF_RSP_SIZE 6
+
+#define L2CAP_CONF_SUCCESS 0x0000
+#define L2CAP_CONF_UNACCEPT 0x0001
+#define L2CAP_CONF_REJECT 0x0002
+#define L2CAP_CONF_UNKNOWN 0x0003
+
+typedef struct {
+ uint8_t type;
+ uint8_t len;
+ uint8_t val[0];
+} __attribute__ ((packed)) l2cap_conf_opt;
+#define L2CAP_CONF_OPT_SIZE 2
+
+#define L2CAP_CONF_MTU 0x01
+#define L2CAP_CONF_FLUSH_TO 0x02
+#define L2CAP_CONF_QOS 0x03
+#define L2CAP_CONF_RFC 0x04
+#define L2CAP_CONF_FCS 0x05
+
+#define L2CAP_CONF_MAX_SIZE 22
+
+#define L2CAP_MODE_BASIC 0x00
+#define L2CAP_MODE_RETRANS 0x01
+#define L2CAP_MODE_FLOWCTL 0x02
+#define L2CAP_MODE_ERTM 0x03
+#define L2CAP_MODE_STREAMING 0x04
+
+typedef struct {
+ uint16_t dcid;
+ uint16_t scid;
+} __attribute__ ((packed)) l2cap_disconn_req;
+#define L2CAP_DISCONN_REQ_SIZE 4
+
+typedef struct {
+ uint16_t dcid;
+ uint16_t scid;
+} __attribute__ ((packed)) l2cap_disconn_rsp;
+#define L2CAP_DISCONN_RSP_SIZE 4
+
+typedef struct {
+ uint16_t type;
+} __attribute__ ((packed)) l2cap_info_req;
+#define L2CAP_INFO_REQ_SIZE 2
+
+typedef struct {
+ uint16_t type;
+ uint16_t result;
+ uint8_t data[0];
+} __attribute__ ((packed)) l2cap_info_rsp;
+#define L2CAP_INFO_RSP_SIZE 4
+
+/* info type */
+#define L2CAP_IT_CL_MTU 0x0001
+#define L2CAP_IT_FEAT_MASK 0x0002
+
+/* info result */
+#define L2CAP_IR_SUCCESS 0x0000
+#define L2CAP_IR_NOTSUPP 0x0001
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __L2CAP_H */
diff --git a/lib/mgmt.h b/lib/mgmt.h
new file mode 100644
index 0000000..3d29b24
--- /dev/null
+++ b/lib/mgmt.h
@@ -0,0 +1,271 @@
+/*
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2010 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
+ *
+ */
+
+#ifndef __packed
+#define __packed __attribute__((packed))
+#endif
+
+#define MGMT_INDEX_NONE 0xFFFF
+
+struct mgmt_hdr {
+ uint16_t opcode;
+ uint16_t index;
+ uint16_t len;
+} __packed;
+#define MGMT_HDR_SIZE 6
+
+#define MGMT_OP_READ_VERSION 0x0001
+struct mgmt_rp_read_version {
+ uint8_t version;
+ uint16_t revision;
+} __packed;
+
+#define MGMT_OP_READ_FEATURES 0x0002
+struct mgmt_rp_read_features {
+ uint8_t features[8];
+} __packed;
+
+#define MGMT_OP_READ_INDEX_LIST 0x0003
+struct mgmt_rp_read_index_list {
+ uint16_t num_controllers;
+ uint16_t index[0];
+} __packed;
+
+#define MGMT_OP_READ_INFO 0x0004
+struct mgmt_rp_read_info {
+ uint8_t type;
+ uint8_t powered;
+ uint8_t connectable;
+ uint8_t discoverable;
+ uint8_t pairable;
+ uint8_t sec_mode;
+ bdaddr_t bdaddr;
+ uint8_t dev_class[3];
+ uint8_t features[8];
+ uint16_t manufacturer;
+ uint8_t hci_ver;
+ uint16_t hci_rev;
+ uint8_t name[249];
+} __packed;
+
+struct mgmt_mode {
+ uint8_t val;
+} __packed;
+
+#define MGMT_OP_SET_POWERED 0x0005
+
+#define MGMT_OP_SET_DISCOVERABLE 0x0006
+
+#define MGMT_OP_SET_CONNECTABLE 0x0007
+
+#define MGMT_OP_SET_PAIRABLE 0x0008
+
+#define MGMT_OP_ADD_UUID 0x0009
+struct mgmt_cp_add_uuid {
+ uint8_t uuid[16];
+ uint8_t svc_hint;
+} __packed;
+
+#define MGMT_OP_REMOVE_UUID 0x000A
+struct mgmt_cp_remove_uuid {
+ uint8_t uuid[16];
+} __packed;
+
+#define MGMT_OP_SET_DEV_CLASS 0x000B
+struct mgmt_cp_set_dev_class {
+ uint8_t major;
+ uint8_t minor;
+} __packed;
+
+#define MGMT_OP_SET_SERVICE_CACHE 0x000C
+struct mgmt_cp_set_service_cache {
+ uint8_t enable;
+} __packed;
+
+struct mgmt_key_info {
+ bdaddr_t bdaddr;
+ uint8_t type;
+ uint8_t val[16];
+ uint8_t pin_len;
+} __packed;
+
+#define MGMT_OP_LOAD_KEYS 0x000D
+struct mgmt_cp_load_keys {
+ uint8_t debug_keys;
+ uint16_t key_count;
+ struct mgmt_key_info keys[0];
+} __packed;
+
+#define MGMT_OP_REMOVE_KEY 0x000E
+struct mgmt_cp_remove_key {
+ bdaddr_t bdaddr;
+ uint8_t disconnect;
+} __packed;
+
+#define MGMT_OP_DISCONNECT 0x000F
+struct mgmt_cp_disconnect {
+ bdaddr_t bdaddr;
+} __packed;
+struct mgmt_rp_disconnect {
+ bdaddr_t bdaddr;
+} __packed;
+
+#define MGMT_OP_GET_CONNECTIONS 0x0010
+struct mgmt_rp_get_connections {
+ uint16_t conn_count;
+ bdaddr_t conn[0];
+} __packed;
+
+#define MGMT_OP_PIN_CODE_REPLY 0x0011
+struct mgmt_cp_pin_code_reply {
+ bdaddr_t bdaddr;
+ uint8_t pin_len;
+ uint8_t pin_code[16];
+} __packed;
+
+#define MGMT_OP_PIN_CODE_NEG_REPLY 0x0012
+struct mgmt_cp_pin_code_neg_reply {
+ bdaddr_t bdaddr;
+} __packed;
+
+#define MGMT_OP_SET_IO_CAPABILITY 0x0013
+struct mgmt_cp_set_io_capability {
+ uint8_t io_capability;
+} __packed;
+
+#define MGMT_OP_PAIR_DEVICE 0x0014
+struct mgmt_cp_pair_device {
+ bdaddr_t bdaddr;
+ uint8_t io_cap;
+} __packed;
+struct mgmt_rp_pair_device {
+ bdaddr_t bdaddr;
+ uint8_t status;
+} __packed;
+
+#define MGMT_OP_USER_CONFIRM_REPLY 0x0015
+struct mgmt_cp_user_confirm_reply {
+ bdaddr_t bdaddr;
+} __packed;
+struct mgmt_rp_user_confirm_reply {
+ bdaddr_t bdaddr;
+ uint8_t status;
+} __packed;
+
+#define MGMT_OP_USER_CONFIRM_NEG_REPLY 0x0016
+
+#define MGMT_OP_SET_LOCAL_NAME 0x0017
+struct mgmt_cp_set_local_name {
+ uint8_t name[249];
+} __packed;
+
+#define MGMT_OP_READ_LOCAL_OOB_DATA 0x0018
+struct mgmt_rp_read_local_oob_data {
+ uint8_t hash[16];
+ uint8_t randomizer[16];
+} __packed;
+
+#define MGMT_OP_ADD_REMOTE_OOB_DATA 0x0019
+struct mgmt_cp_add_remote_oob_data {
+ bdaddr_t bdaddr;
+ uint8_t hash[16];
+ uint8_t randomizer[16];
+} __packed;
+
+#define MGMT_OP_REMOVE_REMOTE_OOB_DATA 0x001A
+struct mgmt_cp_remove_remote_oob_data {
+ bdaddr_t bdaddr;
+} __packed;
+
+#define MGMT_EV_CMD_COMPLETE 0x0001
+struct mgmt_ev_cmd_complete {
+ uint16_t opcode;
+ uint8_t data[0];
+} __packed;
+
+#define MGMT_EV_CMD_STATUS 0x0002
+struct mgmt_ev_cmd_status {
+ uint8_t status;
+ uint16_t opcode;
+} __packed;
+
+#define MGMT_EV_CONTROLLER_ERROR 0x0003
+struct mgmt_ev_controller_error {
+ uint8_t error_code;
+} __packed;
+
+#define MGMT_EV_INDEX_ADDED 0x0004
+
+#define MGMT_EV_INDEX_REMOVED 0x0005
+
+#define MGMT_EV_POWERED 0x0006
+
+#define MGMT_EV_DISCOVERABLE 0x0007
+
+#define MGMT_EV_CONNECTABLE 0x0008
+
+#define MGMT_EV_PAIRABLE 0x0009
+
+#define MGMT_EV_NEW_KEY 0x000A
+struct mgmt_ev_new_key {
+ struct mgmt_key_info key;
+ uint8_t old_key_type;
+} __packed;
+
+#define MGMT_EV_DEVICE_CONNECTED 0x000B
+struct mgmt_ev_device_connected {
+ bdaddr_t bdaddr;
+} __packed;
+
+#define MGMT_EV_DEVICE_DISCONNECTED 0x000C
+struct mgmt_ev_device_disconnected {
+ bdaddr_t bdaddr;
+} __packed;
+
+#define MGMT_EV_CONNECT_FAILED 0x000D
+struct mgmt_ev_connect_failed {
+ bdaddr_t bdaddr;
+ uint8_t status;
+} __packed;
+
+#define MGMT_EV_PIN_CODE_REQUEST 0x000E
+struct mgmt_ev_pin_code_request {
+ bdaddr_t bdaddr;
+} __packed;
+
+#define MGMT_EV_USER_CONFIRM_REQUEST 0x000F
+struct mgmt_ev_user_confirm_request {
+ bdaddr_t bdaddr;
+ uint32_t value;
+} __packed;
+
+#define MGMT_EV_AUTH_FAILED 0x0010
+struct mgmt_ev_auth_failed {
+ bdaddr_t bdaddr;
+ uint8_t status;
+} __packed;
+
+#define MGMT_EV_LOCAL_NAME_CHANGED 0x0011
+struct mgmt_ev_local_name_changed {
+ uint8_t name[249];
+} __packed;
diff --git a/lib/rfcomm.h b/lib/rfcomm.h
new file mode 100644
index 0000000..ad6c0e1
--- /dev/null
+++ b/lib/rfcomm.h
@@ -0,0 +1,99 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 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
+ *
+ */
+
+#ifndef __RFCOMM_H
+#define __RFCOMM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/socket.h>
+
+/* RFCOMM defaults */
+#define RFCOMM_DEFAULT_MTU 127
+
+#define RFCOMM_PSM 3
+
+/* RFCOMM socket address */
+struct sockaddr_rc {
+ sa_family_t rc_family;
+ bdaddr_t rc_bdaddr;
+ uint8_t rc_channel;
+};
+
+/* RFCOMM socket options */
+#define RFCOMM_CONNINFO 0x02
+struct rfcomm_conninfo {
+ uint16_t hci_handle;
+ uint8_t dev_class[3];
+};
+
+#define RFCOMM_LM 0x03
+#define RFCOMM_LM_MASTER 0x0001
+#define RFCOMM_LM_AUTH 0x0002
+#define RFCOMM_LM_ENCRYPT 0x0004
+#define RFCOMM_LM_TRUSTED 0x0008
+#define RFCOMM_LM_RELIABLE 0x0010
+#define RFCOMM_LM_SECURE 0x0020
+
+/* RFCOMM TTY support */
+#define RFCOMM_MAX_DEV 256
+
+#define RFCOMMCREATEDEV _IOW('R', 200, int)
+#define RFCOMMRELEASEDEV _IOW('R', 201, int)
+#define RFCOMMGETDEVLIST _IOR('R', 210, int)
+#define RFCOMMGETDEVINFO _IOR('R', 211, int)
+
+struct rfcomm_dev_req {
+ int16_t dev_id;
+ uint32_t flags;
+ bdaddr_t src;
+ bdaddr_t dst;
+ uint8_t channel;
+};
+#define RFCOMM_REUSE_DLC 0
+#define RFCOMM_RELEASE_ONHUP 1
+#define RFCOMM_HANGUP_NOW 2
+#define RFCOMM_TTY_ATTACHED 3
+
+struct rfcomm_dev_info {
+ int16_t id;
+ uint32_t flags;
+ uint16_t state;
+ bdaddr_t src;
+ bdaddr_t dst;
+ uint8_t channel;
+};
+
+struct rfcomm_dev_list_req {
+ uint16_t dev_num;
+ struct rfcomm_dev_info dev_info[0];
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __RFCOMM_H */
diff --git a/lib/sco.h b/lib/sco.h
new file mode 100644
index 0000000..75336a5
--- /dev/null
+++ b/lib/sco.h
@@ -0,0 +1,62 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 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
+ *
+ */
+
+#ifndef __SCO_H
+#define __SCO_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* SCO defaults */
+#define SCO_DEFAULT_MTU 500
+#define SCO_DEFAULT_FLUSH_TO 0xFFFF
+
+#define SCO_CONN_TIMEOUT (HZ * 40)
+#define SCO_DISCONN_TIMEOUT (HZ * 2)
+#define SCO_CONN_IDLE_TIMEOUT (HZ * 60)
+
+/* SCO socket address */
+struct sockaddr_sco {
+ sa_family_t sco_family;
+ bdaddr_t sco_bdaddr;
+};
+
+/* set/get sockopt defines */
+#define SCO_OPTIONS 0x01
+struct sco_options {
+ uint16_t mtu;
+};
+
+#define SCO_CONNINFO 0x02
+struct sco_conninfo {
+ uint16_t hci_handle;
+ uint8_t dev_class[3];
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SCO_H */
diff --git a/lib/sdp.c b/lib/sdp.c
new file mode 100644
index 0000000..d24d1e2
--- /dev/null
+++ b/lib/sdp.c
@@ -0,0 +1,4791 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2001-2002 Nokia Corporation
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2002-2003 Stephen Crane <steve.crane@rococosoft.com>
+ *
+ *
+ * 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 <limits.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+
+#include "bluetooth.h"
+#include "hci.h"
+#include "hci_lib.h"
+#include "l2cap.h"
+#include "sdp.h"
+#include "sdp_lib.h"
+
+#define SDPINF(fmt, arg...) syslog(LOG_INFO, fmt "\n", ## arg)
+#define SDPERR(fmt, arg...) syslog(LOG_ERR, "%s: " fmt "\n", __func__ , ## arg)
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#ifdef SDP_DEBUG
+#define SDPDBG(fmt, arg...) syslog(LOG_DEBUG, "%s: " fmt "\n", __func__ , ## arg)
+#else
+#define SDPDBG(fmt...)
+#endif
+
+#define BASE_UUID "00000000-0000-1000-8000-00805F9B34FB"
+
+static uint128_t bluetooth_base_uuid = {
+ .data = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB }
+};
+
+#define SDP_MAX_ATTR_LEN 65535
+
+static sdp_data_t *sdp_copy_seq(sdp_data_t *data);
+static int sdp_attr_add_new_with_length(sdp_record_t *rec,
+ uint16_t attr, uint8_t dtd, const void *value, uint32_t len);
+static int sdp_gen_buffer(sdp_buf_t *buf, sdp_data_t *d);
+
+/* Message structure. */
+struct tupla {
+ int index;
+ char *str;
+};
+
+static struct tupla Protocol[] = {
+ { SDP_UUID, "SDP" },
+ { UDP_UUID, "UDP" },
+ { RFCOMM_UUID, "RFCOMM" },
+ { TCP_UUID, "TCP" },
+ { TCS_BIN_UUID, "TCS-BIN" },
+ { TCS_AT_UUID, "TCS-AT" },
+ { OBEX_UUID, "OBEX" },
+ { IP_UUID, "IP" },
+ { FTP_UUID, "FTP" },
+ { HTTP_UUID, "HTTP" },
+ { WSP_UUID, "WSP" },
+ { BNEP_UUID, "BNEP" },
+ { UPNP_UUID, "UPNP" },
+ { HIDP_UUID, "HIDP" },
+ { HCRP_CTRL_UUID, "HCRP-Ctrl" },
+ { HCRP_DATA_UUID, "HCRP-Data" },
+ { HCRP_NOTE_UUID, "HCRP-Notify" },
+ { AVCTP_UUID, "AVCTP" },
+ { AVDTP_UUID, "AVDTP" },
+ { CMTP_UUID, "CMTP" },
+ { UDI_UUID, "UDI" },
+ { MCAP_CTRL_UUID, "MCAP-Ctrl" },
+ { MCAP_DATA_UUID, "MCAP-Data" },
+ { L2CAP_UUID, "L2CAP" },
+ { ATT_UUID, "ATT" },
+ { 0 }
+};
+
+static struct tupla ServiceClass[] = {
+ { SDP_SERVER_SVCLASS_ID, "SDP Server" },
+ { BROWSE_GRP_DESC_SVCLASS_ID, "Browse Group Descriptor" },
+ { PUBLIC_BROWSE_GROUP, "Public Browse Group" },
+ { SERIAL_PORT_SVCLASS_ID, "Serial Port" },
+ { LAN_ACCESS_SVCLASS_ID, "LAN Access Using PPP" },
+ { DIALUP_NET_SVCLASS_ID, "Dialup Networking" },
+ { IRMC_SYNC_SVCLASS_ID, "IrMC Sync" },
+ { OBEX_OBJPUSH_SVCLASS_ID, "OBEX Object Push" },
+ { OBEX_FILETRANS_SVCLASS_ID, "OBEX File Transfer" },
+ { IRMC_SYNC_CMD_SVCLASS_ID, "IrMC Sync Command" },
+ { HEADSET_SVCLASS_ID, "Headset" },
+ { CORDLESS_TELEPHONY_SVCLASS_ID, "Cordless Telephony" },
+ { AUDIO_SOURCE_SVCLASS_ID, "Audio Source" },
+ { AUDIO_SINK_SVCLASS_ID, "Audio Sink" },
+ { AV_REMOTE_TARGET_SVCLASS_ID, "AV Remote Target" },
+ { ADVANCED_AUDIO_SVCLASS_ID, "Advanced Audio" },
+ { AV_REMOTE_SVCLASS_ID, "AV Remote" },
+ { VIDEO_CONF_SVCLASS_ID, "Video Conferencing" },
+ { INTERCOM_SVCLASS_ID, "Intercom" },
+ { FAX_SVCLASS_ID, "Fax" },
+ { HEADSET_AGW_SVCLASS_ID, "Headset Audio Gateway" },
+ { WAP_SVCLASS_ID, "WAP" },
+ { WAP_CLIENT_SVCLASS_ID, "WAP Client" },
+ { PANU_SVCLASS_ID, "PAN User" },
+ { NAP_SVCLASS_ID, "Network Access Point" },
+ { GN_SVCLASS_ID, "PAN Group Network" },
+ { DIRECT_PRINTING_SVCLASS_ID, "Direct Printing" },
+ { REFERENCE_PRINTING_SVCLASS_ID, "Reference Printing" },
+ { IMAGING_SVCLASS_ID, "Imaging" },
+ { IMAGING_RESPONDER_SVCLASS_ID, "Imaging Responder" },
+ { IMAGING_ARCHIVE_SVCLASS_ID, "Imaging Automatic Archive" },
+ { IMAGING_REFOBJS_SVCLASS_ID, "Imaging Referenced Objects" },
+ { HANDSFREE_SVCLASS_ID, "Handsfree" },
+ { HANDSFREE_AGW_SVCLASS_ID, "Handsfree Audio Gateway" },
+ { DIRECT_PRT_REFOBJS_SVCLASS_ID, "Direct Printing Ref. Objects" },
+ { REFLECTED_UI_SVCLASS_ID, "Reflected UI" },
+ { BASIC_PRINTING_SVCLASS_ID, "Basic Printing" },
+ { PRINTING_STATUS_SVCLASS_ID, "Printing Status" },
+ { HID_SVCLASS_ID, "Human Interface Device" },
+ { HCR_SVCLASS_ID, "Hardcopy Cable Replacement" },
+ { HCR_PRINT_SVCLASS_ID, "HCR Print" },
+ { HCR_SCAN_SVCLASS_ID, "HCR Scan" },
+ { CIP_SVCLASS_ID, "Common ISDN Access" },
+ { VIDEO_CONF_GW_SVCLASS_ID, "Video Conferencing Gateway" },
+ { UDI_MT_SVCLASS_ID, "UDI MT" },
+ { UDI_TA_SVCLASS_ID, "UDI TA" },
+ { AV_SVCLASS_ID, "Audio/Video" },
+ { SAP_SVCLASS_ID, "SIM Access" },
+ { PBAP_PCE_SVCLASS_ID, "Phonebook Access - PCE" },
+ { PBAP_PSE_SVCLASS_ID, "Phonebook Access - PSE" },
+ { PBAP_SVCLASS_ID, "Phonebook Access" },
+ { PNP_INFO_SVCLASS_ID, "PnP Information" },
+ { GENERIC_NETWORKING_SVCLASS_ID, "Generic Networking" },
+ { GENERIC_FILETRANS_SVCLASS_ID, "Generic File Transfer" },
+ { GENERIC_AUDIO_SVCLASS_ID, "Generic Audio" },
+ { GENERIC_TELEPHONY_SVCLASS_ID, "Generic Telephony" },
+ { UPNP_SVCLASS_ID, "UPnP" },
+ { UPNP_IP_SVCLASS_ID, "UPnP IP" },
+ { UPNP_PAN_SVCLASS_ID, "UPnP PAN" },
+ { UPNP_LAP_SVCLASS_ID, "UPnP LAP" },
+ { UPNP_L2CAP_SVCLASS_ID, "UPnP L2CAP" },
+ { VIDEO_SOURCE_SVCLASS_ID, "Video Source" },
+ { VIDEO_SINK_SVCLASS_ID, "Video Sink" },
+ { VIDEO_DISTRIBUTION_SVCLASS_ID, "Video Distribution" },
+ { HDP_SVCLASS_ID, "HDP" },
+ { HDP_SOURCE_SVCLASS_ID, "HDP Source" },
+ { HDP_SINK_SVCLASS_ID, "HDP Sink" },
+ { APPLE_AGENT_SVCLASS_ID, "Apple Agent" },
+ { GENERIC_ATTRIB_SVCLASS_ID, "Generic Attribute" },
+ { 0 }
+};
+
+#define Profile ServiceClass
+
+static char *string_lookup(struct tupla *pt0, int index)
+{
+ struct tupla *pt;
+
+ for (pt = pt0; pt->index; pt++)
+ if (pt->index == index)
+ return pt->str;
+
+ return "";
+}
+
+static char *string_lookup_uuid(struct tupla *pt0, const uuid_t* uuid)
+{
+ uuid_t tmp_uuid;
+
+ memcpy(&tmp_uuid, uuid, sizeof(tmp_uuid));
+
+ if (sdp_uuid128_to_uuid(&tmp_uuid)) {
+ switch (tmp_uuid.type) {
+ case SDP_UUID16:
+ return string_lookup(pt0, tmp_uuid.value.uuid16);
+ case SDP_UUID32:
+ return string_lookup(pt0, tmp_uuid.value.uuid32);
+ }
+ }
+
+ return "";
+}
+
+/*
+ * Prints into a string the Protocol UUID
+ * coping a maximum of n characters.
+ */
+static int uuid2str(struct tupla *message, const uuid_t *uuid, char *str, size_t n)
+{
+ char *str2;
+
+ if (!uuid) {
+ snprintf(str, n, "NULL");
+ return -2;
+ }
+
+ switch (uuid->type) {
+ case SDP_UUID16:
+ str2 = string_lookup(message, uuid->value.uuid16);
+ snprintf(str, n, "%s", str2);
+ break;
+ case SDP_UUID32:
+ str2 = string_lookup(message, uuid->value.uuid32);
+ snprintf(str, n, "%s", str2);
+ break;
+ case SDP_UUID128:
+ str2 = string_lookup_uuid(message, uuid);
+ snprintf(str, n, "%s", str2);
+ break;
+ default:
+ snprintf(str, n, "Type of UUID (%x) unknown.", uuid->type);
+ return -1;
+ }
+
+ return 0;
+}
+
+int sdp_proto_uuid2strn(const uuid_t *uuid, char *str, size_t n)
+{
+ return uuid2str(Protocol, uuid, str, n);
+}
+
+int sdp_svclass_uuid2strn(const uuid_t *uuid, char *str, size_t n)
+{
+ return uuid2str(ServiceClass, uuid, str, n);
+}
+
+int sdp_profile_uuid2strn(const uuid_t *uuid, char *str, size_t n)
+{
+ return uuid2str(Profile, uuid, str, n);
+}
+
+/*
+ * convert the UUID to string, copying a maximum of n characters.
+ */
+int sdp_uuid2strn(const uuid_t *uuid, char *str, size_t n)
+{
+ if (!uuid) {
+ snprintf(str, n, "NULL");
+ return -2;
+ }
+ switch (uuid->type) {
+ case SDP_UUID16:
+ snprintf(str, n, "%.4x", uuid->value.uuid16);
+ break;
+ case SDP_UUID32:
+ snprintf(str, n, "%.8x", uuid->value.uuid32);
+ break;
+ case SDP_UUID128:{
+ unsigned int data0;
+ unsigned short data1;
+ unsigned short data2;
+ unsigned short data3;
+ unsigned int data4;
+ unsigned short data5;
+
+ memcpy(&data0, &uuid->value.uuid128.data[0], 4);
+ memcpy(&data1, &uuid->value.uuid128.data[4], 2);
+ memcpy(&data2, &uuid->value.uuid128.data[6], 2);
+ memcpy(&data3, &uuid->value.uuid128.data[8], 2);
+ memcpy(&data4, &uuid->value.uuid128.data[10], 4);
+ memcpy(&data5, &uuid->value.uuid128.data[14], 2);
+
+ snprintf(str, n, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x",
+ ntohl(data0), ntohs(data1),
+ ntohs(data2), ntohs(data3),
+ ntohl(data4), ntohs(data5));
+ }
+ break;
+ default:
+ snprintf(str, n, "Type of UUID (%x) unknown.", uuid->type);
+ return -1; /* Enum type of UUID not set */
+ }
+ return 0;
+}
+
+#ifdef SDP_DEBUG
+/*
+ * Function prints the UUID in hex as per defined syntax -
+ *
+ * 4bytes-2bytes-2bytes-2bytes-6bytes
+ *
+ * There is some ugly code, including hardcoding, but
+ * that is just the way it is converting 16 and 32 bit
+ * UUIDs to 128 bit as defined in the SDP doc
+ */
+void sdp_uuid_print(const uuid_t *uuid)
+{
+ if (uuid == NULL) {
+ SDPERR("Null passed to print UUID\n");
+ return;
+ }
+ if (uuid->type == SDP_UUID16) {
+ SDPDBG(" uint16_t : 0x%.4x\n", uuid->value.uuid16);
+ } else if (uuid->type == SDP_UUID32) {
+ SDPDBG(" uint32_t : 0x%.8x\n", uuid->value.uuid32);
+ } else if (uuid->type == SDP_UUID128) {
+ unsigned int data0;
+ unsigned short data1;
+ unsigned short data2;
+ unsigned short data3;
+ unsigned int data4;
+ unsigned short data5;
+
+ memcpy(&data0, &uuid->value.uuid128.data[0], 4);
+ memcpy(&data1, &uuid->value.uuid128.data[4], 2);
+ memcpy(&data2, &uuid->value.uuid128.data[6], 2);
+ memcpy(&data3, &uuid->value.uuid128.data[8], 2);
+ memcpy(&data4, &uuid->value.uuid128.data[10], 4);
+ memcpy(&data5, &uuid->value.uuid128.data[14], 2);
+
+ SDPDBG(" uint128_t : 0x%.8x-", ntohl(data0));
+ SDPDBG("%.4x-", ntohs(data1));
+ SDPDBG("%.4x-", ntohs(data2));
+ SDPDBG("%.4x-", ntohs(data3));
+ SDPDBG("%.8x", ntohl(data4));
+ SDPDBG("%.4x\n", ntohs(data5));
+ } else
+ SDPERR("Enum type of UUID not set\n");
+}
+#endif
+
+sdp_data_t *sdp_data_alloc_with_length(uint8_t dtd, const void *value,
+ uint32_t length)
+{
+ sdp_data_t *seq;
+ sdp_data_t *d = malloc(sizeof(sdp_data_t));
+
+ if (!d)
+ return NULL;
+
+ memset(d, 0, sizeof(sdp_data_t));
+ d->dtd = dtd;
+ d->unitSize = sizeof(uint8_t);
+
+ switch (dtd) {
+ case SDP_DATA_NIL:
+ break;
+ case SDP_UINT8:
+ d->val.uint8 = *(uint8_t *) value;
+ d->unitSize += sizeof(uint8_t);
+ break;
+ case SDP_INT8:
+ case SDP_BOOL:
+ d->val.int8 = *(int8_t *) value;
+ d->unitSize += sizeof(int8_t);
+ break;
+ case SDP_UINT16:
+ d->val.uint16 = bt_get_unaligned((uint16_t *) value);
+ d->unitSize += sizeof(uint16_t);
+ break;
+ case SDP_INT16:
+ d->val.int16 = bt_get_unaligned((int16_t *) value);
+ d->unitSize += sizeof(int16_t);
+ break;
+ case SDP_UINT32:
+ d->val.uint32 = bt_get_unaligned((uint32_t *) value);
+ d->unitSize += sizeof(uint32_t);
+ break;
+ case SDP_INT32:
+ d->val.int32 = bt_get_unaligned((int32_t *) value);
+ d->unitSize += sizeof(int32_t);
+ break;
+ case SDP_INT64:
+ d->val.int64 = bt_get_unaligned((int64_t *) value);
+ d->unitSize += sizeof(int64_t);
+ break;
+ case SDP_UINT64:
+ d->val.uint64 = bt_get_unaligned((uint64_t *) value);
+ d->unitSize += sizeof(uint64_t);
+ break;
+ case SDP_UINT128:
+ memcpy(&d->val.uint128.data, value, sizeof(uint128_t));
+ d->unitSize += sizeof(uint128_t);
+ break;
+ case SDP_INT128:
+ memcpy(&d->val.int128.data, value, sizeof(uint128_t));
+ d->unitSize += sizeof(uint128_t);
+ break;
+ case SDP_UUID16:
+ sdp_uuid16_create(&d->val.uuid, bt_get_unaligned((uint16_t *) value));
+ d->unitSize += sizeof(uint16_t);
+ break;
+ case SDP_UUID32:
+ sdp_uuid32_create(&d->val.uuid, bt_get_unaligned((uint32_t *) value));
+ d->unitSize += sizeof(uint32_t);
+ break;
+ case SDP_UUID128:
+ sdp_uuid128_create(&d->val.uuid, value);
+ d->unitSize += sizeof(uint128_t);
+ break;
+ case SDP_URL_STR8:
+ case SDP_URL_STR16:
+ case SDP_TEXT_STR8:
+ case SDP_TEXT_STR16:
+ if (!value) {
+ free(d);
+ return NULL;
+ }
+
+ d->unitSize += length;
+ if (length <= USHRT_MAX) {
+ d->val.str = malloc(length);
+ if (!d->val.str) {
+ free(d);
+ return NULL;
+ }
+
+ memcpy(d->val.str, value, length);
+ } else {
+ SDPERR("Strings of size > USHRT_MAX not supported\n");
+ free(d);
+ d = NULL;
+ }
+ break;
+ case SDP_URL_STR32:
+ case SDP_TEXT_STR32:
+ SDPERR("Strings of size > USHRT_MAX not supported\n");
+ break;
+ case SDP_ALT8:
+ case SDP_ALT16:
+ case SDP_ALT32:
+ case SDP_SEQ8:
+ case SDP_SEQ16:
+ case SDP_SEQ32:
+ if (dtd == SDP_ALT8 || dtd == SDP_SEQ8)
+ d->unitSize += sizeof(uint8_t);
+ else if (dtd == SDP_ALT16 || dtd == SDP_SEQ16)
+ d->unitSize += sizeof(uint16_t);
+ else if (dtd == SDP_ALT32 || dtd == SDP_SEQ32)
+ d->unitSize += sizeof(uint32_t);
+ seq = (sdp_data_t *)value;
+ d->val.dataseq = seq;
+ for (; seq; seq = seq->next)
+ d->unitSize += seq->unitSize;
+ break;
+ default:
+ free(d);
+ d = NULL;
+ }
+
+ return d;
+}
+
+sdp_data_t *sdp_data_alloc(uint8_t dtd, const void *value)
+{
+ uint32_t length;
+
+ switch (dtd) {
+ case SDP_URL_STR8:
+ case SDP_URL_STR16:
+ case SDP_TEXT_STR8:
+ case SDP_TEXT_STR16:
+ if (!value)
+ return NULL;
+
+ length = strlen((char *) value);
+ break;
+ default:
+ length = 0;
+ break;
+ }
+
+ return sdp_data_alloc_with_length(dtd, value, length);
+}
+
+sdp_data_t *sdp_seq_append(sdp_data_t *seq, sdp_data_t *d)
+{
+ if (seq) {
+ sdp_data_t *p;
+ for (p = seq; p->next; p = p->next);
+ p->next = d;
+ } else
+ seq = d;
+ d->next = NULL;
+ return seq;
+}
+
+sdp_data_t *sdp_seq_alloc_with_length(void **dtds, void **values, int *length,
+ int len)
+{
+ sdp_data_t *curr = NULL, *seq = NULL;
+ int i;
+
+ for (i = 0; i < len; i++) {
+ sdp_data_t *data;
+ int8_t dtd = *(uint8_t *) dtds[i];
+
+ if (dtd >= SDP_SEQ8 && dtd <= SDP_ALT32)
+ data = (sdp_data_t *) values[i];
+ else
+ data = sdp_data_alloc_with_length(dtd, values[i], length[i]);
+
+ if (!data)
+ return NULL;
+
+ if (curr)
+ curr->next = data;
+ else
+ seq = data;
+
+ curr = data;
+ }
+
+ return sdp_data_alloc_with_length(SDP_SEQ8, seq, length[i]);
+}
+
+sdp_data_t *sdp_seq_alloc(void **dtds, void **values, int len)
+{
+ sdp_data_t *curr = NULL, *seq = NULL;
+ int i;
+
+ for (i = 0; i < len; i++) {
+ sdp_data_t *data;
+ uint8_t dtd = *(uint8_t *) dtds[i];
+
+ if (dtd >= SDP_SEQ8 && dtd <= SDP_ALT32)
+ data = (sdp_data_t *) values[i];
+ else
+ data = sdp_data_alloc(dtd, values[i]);
+
+ if (!data)
+ return NULL;
+
+ if (curr)
+ curr->next = data;
+ else
+ seq = data;
+
+ curr = data;
+ }
+
+ return sdp_data_alloc(SDP_SEQ8, seq);
+}
+
+static void extract_svclass_uuid(sdp_data_t *data, uuid_t *uuid)
+{
+ sdp_data_t *d;
+
+ if (!data || data->dtd < SDP_SEQ8 || data->dtd > SDP_SEQ32)
+ return;
+
+ d = data->val.dataseq;
+ if (!d)
+ return;
+
+ if (d->dtd < SDP_UUID16 || d->dtd > SDP_UUID128)
+ return;
+
+ *uuid = d->val.uuid;
+}
+
+int sdp_attr_add(sdp_record_t *rec, uint16_t attr, sdp_data_t *d)
+{
+ sdp_data_t *p = sdp_data_get(rec, attr);
+
+ if (p)
+ return -1;
+
+ d->attrId = attr;
+ rec->attrlist = sdp_list_insert_sorted(rec->attrlist, d, sdp_attrid_comp_func);
+
+ if (attr == SDP_ATTR_SVCLASS_ID_LIST)
+ extract_svclass_uuid(d, &rec->svclass);
+
+ return 0;
+}
+
+void sdp_attr_remove(sdp_record_t *rec, uint16_t attr)
+{
+ sdp_data_t *d = sdp_data_get(rec, attr);
+
+ if (d)
+ rec->attrlist = sdp_list_remove(rec->attrlist, d);
+
+ if (attr == SDP_ATTR_SVCLASS_ID_LIST)
+ memset(&rec->svclass, 0, sizeof(rec->svclass));
+}
+
+void sdp_set_seq_len(uint8_t *ptr, uint32_t length)
+{
+ uint8_t dtd = *ptr++;
+
+ switch (dtd) {
+ case SDP_SEQ8:
+ case SDP_ALT8:
+ case SDP_TEXT_STR8:
+ case SDP_URL_STR8:
+ *ptr = (uint8_t) length;
+ break;
+ case SDP_SEQ16:
+ case SDP_ALT16:
+ case SDP_TEXT_STR16:
+ case SDP_URL_STR16:
+ bt_put_unaligned(htons(length), (uint16_t *) ptr);
+ break;
+ case SDP_SEQ32:
+ case SDP_ALT32:
+ case SDP_TEXT_STR32:
+ case SDP_URL_STR32:
+ bt_put_unaligned(htonl(length), (uint32_t *) ptr);
+ break;
+ }
+}
+
+static int sdp_get_data_type(sdp_buf_t *buf, uint8_t dtd)
+{
+ int data_type = 0;
+
+ data_type += sizeof(uint8_t);
+
+ switch (dtd) {
+ case SDP_SEQ8:
+ case SDP_TEXT_STR8:
+ case SDP_URL_STR8:
+ case SDP_ALT8:
+ data_type += sizeof(uint8_t);
+ break;
+ case SDP_SEQ16:
+ case SDP_TEXT_STR16:
+ case SDP_URL_STR16:
+ case SDP_ALT16:
+ data_type += sizeof(uint16_t);
+ break;
+ case SDP_SEQ32:
+ case SDP_TEXT_STR32:
+ case SDP_URL_STR32:
+ case SDP_ALT32:
+ data_type += sizeof(uint32_t);
+ break;
+ }
+
+ if (!buf->data)
+ buf->buf_size += data_type;
+
+ return data_type;
+}
+
+static int sdp_set_data_type(sdp_buf_t *buf, uint8_t dtd)
+{
+ int data_type = 0;
+ uint8_t *p = buf->data + buf->data_size;
+
+ *p++ = dtd;
+ data_type = sdp_get_data_type(buf, dtd);
+ buf->data_size += data_type;
+
+ return data_type;
+}
+
+void sdp_set_attrid(sdp_buf_t *buf, uint16_t attr)
+{
+ uint8_t *p = buf->data;
+
+ /* data type for attr */
+ *p++ = SDP_UINT16;
+ buf->data_size = sizeof(uint8_t);
+ bt_put_unaligned(htons(attr), (uint16_t *) p);
+ p += sizeof(uint16_t);
+ buf->data_size += sizeof(uint16_t);
+}
+
+static int get_data_size(sdp_buf_t *buf, sdp_data_t *sdpdata)
+{
+ sdp_data_t *d;
+ int n = 0;
+
+ for (d = sdpdata->val.dataseq; d; d = d->next) {
+ if (buf->data)
+ n += sdp_gen_pdu(buf, d);
+ else
+ n += sdp_gen_buffer(buf, d);
+ }
+
+ return n;
+}
+
+static int sdp_get_data_size(sdp_buf_t *buf, sdp_data_t *d)
+{
+ uint32_t data_size = 0;
+ uint8_t dtd = d->dtd;
+
+ switch (dtd) {
+ case SDP_DATA_NIL:
+ break;
+ case SDP_UINT8:
+ data_size = sizeof(uint8_t);
+ break;
+ case SDP_UINT16:
+ data_size = sizeof(uint16_t);
+ break;
+ case SDP_UINT32:
+ data_size = sizeof(uint32_t);
+ break;
+ case SDP_UINT64:
+ data_size = sizeof(uint64_t);
+ break;
+ case SDP_UINT128:
+ data_size = sizeof(uint128_t);
+ break;
+ case SDP_INT8:
+ case SDP_BOOL:
+ data_size = sizeof(int8_t);
+ break;
+ case SDP_INT16:
+ data_size = sizeof(int16_t);
+ break;
+ case SDP_INT32:
+ data_size = sizeof(int32_t);
+ break;
+ case SDP_INT64:
+ data_size = sizeof(int64_t);
+ break;
+ case SDP_INT128:
+ data_size = sizeof(uint128_t);
+ break;
+ case SDP_TEXT_STR8:
+ case SDP_TEXT_STR16:
+ case SDP_TEXT_STR32:
+ case SDP_URL_STR8:
+ case SDP_URL_STR16:
+ case SDP_URL_STR32:
+ data_size = d->unitSize - sizeof(uint8_t);
+ break;
+ case SDP_SEQ8:
+ case SDP_SEQ16:
+ case SDP_SEQ32:
+ data_size = get_data_size(buf, d);
+ break;
+ case SDP_ALT8:
+ case SDP_ALT16:
+ case SDP_ALT32:
+ data_size = get_data_size(buf, d);
+ break;
+ case SDP_UUID16:
+ data_size = sizeof(uint16_t);
+ break;
+ case SDP_UUID32:
+ data_size = sizeof(uint32_t);
+ break;
+ case SDP_UUID128:
+ data_size = sizeof(uint128_t);
+ break;
+ default:
+ break;
+ }
+
+ if (!buf->data)
+ buf->buf_size += data_size;
+
+ return data_size;
+}
+
+static int sdp_gen_buffer(sdp_buf_t *buf, sdp_data_t *d)
+{
+ int orig = buf->buf_size;
+
+ if (buf->buf_size == 0 && d->dtd == 0) {
+ /* create initial sequence */
+ buf->buf_size += sizeof(uint8_t);
+
+ /* reserve space for sequence size */
+ buf->buf_size += sizeof(uint8_t);
+ }
+
+ /* attribute length */
+ buf->buf_size += sizeof(uint8_t) + sizeof(uint16_t);
+
+ sdp_get_data_type(buf, d->dtd);
+ sdp_get_data_size(buf, d);
+
+ if (buf->buf_size > UCHAR_MAX && d->dtd == SDP_SEQ8)
+ buf->buf_size += sizeof(uint8_t);
+
+ return buf->buf_size - orig;
+}
+
+int sdp_gen_pdu(sdp_buf_t *buf, sdp_data_t *d)
+{
+ uint32_t pdu_size = 0, data_size = 0;
+ unsigned char *src = NULL, is_seq = 0, is_alt = 0;
+ uint8_t dtd = d->dtd;
+ uint16_t u16;
+ uint32_t u32;
+ uint64_t u64;
+ uint128_t u128;
+ uint8_t *seqp = buf->data + buf->data_size;
+
+ pdu_size = sdp_set_data_type(buf, dtd);
+ data_size = sdp_get_data_size(buf, d);
+
+ switch (dtd) {
+ case SDP_DATA_NIL:
+ break;
+ case SDP_UINT8:
+ src = &d->val.uint8;
+ break;
+ case SDP_UINT16:
+ u16 = htons(d->val.uint16);
+ src = (unsigned char *) &u16;
+ break;
+ case SDP_UINT32:
+ u32 = htonl(d->val.uint32);
+ src = (unsigned char *) &u32;
+ break;
+ case SDP_UINT64:
+ u64 = hton64(d->val.uint64);
+ src = (unsigned char *) &u64;
+ break;
+ case SDP_UINT128:
+ hton128(&d->val.uint128, &u128);
+ src = (unsigned char *) &u128;
+ break;
+ case SDP_INT8:
+ case SDP_BOOL:
+ src = (unsigned char *) &d->val.int8;
+ break;
+ case SDP_INT16:
+ u16 = htons(d->val.int16);
+ src = (unsigned char *) &u16;
+ break;
+ case SDP_INT32:
+ u32 = htonl(d->val.int32);
+ src = (unsigned char *) &u32;
+ break;
+ case SDP_INT64:
+ u64 = hton64(d->val.int64);
+ src = (unsigned char *) &u64;
+ break;
+ case SDP_INT128:
+ hton128(&d->val.int128, &u128);
+ src = (unsigned char *) &u128;
+ break;
+ case SDP_TEXT_STR8:
+ case SDP_TEXT_STR16:
+ case SDP_TEXT_STR32:
+ case SDP_URL_STR8:
+ case SDP_URL_STR16:
+ case SDP_URL_STR32:
+ src = (unsigned char *) d->val.str;
+ sdp_set_seq_len(seqp, data_size);
+ break;
+ case SDP_SEQ8:
+ case SDP_SEQ16:
+ case SDP_SEQ32:
+ is_seq = 1;
+ sdp_set_seq_len(seqp, data_size);
+ break;
+ case SDP_ALT8:
+ case SDP_ALT16:
+ case SDP_ALT32:
+ is_alt = 1;
+ sdp_set_seq_len(seqp, data_size);
+ break;
+ case SDP_UUID16:
+ u16 = htons(d->val.uuid.value.uuid16);
+ src = (unsigned char *) &u16;
+ break;
+ case SDP_UUID32:
+ u32 = htonl(d->val.uuid.value.uuid32);
+ src = (unsigned char *) &u32;
+ break;
+ case SDP_UUID128:
+ src = (unsigned char *) &d->val.uuid.value.uuid128;
+ break;
+ default:
+ break;
+ }
+
+ if (!is_seq && !is_alt) {
+ if (src && buf && buf->buf_size >= buf->data_size + data_size) {
+ memcpy(buf->data + buf->data_size, src, data_size);
+ buf->data_size += data_size;
+ } else if (dtd != SDP_DATA_NIL) {
+ SDPDBG("Gen PDU : Can't copy from invalid source or dest\n");
+ }
+ }
+
+ pdu_size += data_size;
+
+ return pdu_size;
+}
+
+static void sdp_attr_pdu(void *value, void *udata)
+{
+ sdp_append_to_pdu((sdp_buf_t *)udata, (sdp_data_t *)value);
+}
+
+static void sdp_attr_size(void *value, void *udata)
+{
+ sdp_gen_buffer((sdp_buf_t *)udata, (sdp_data_t *)value);
+}
+
+int sdp_gen_record_pdu(const sdp_record_t *rec, sdp_buf_t *buf)
+{
+ memset(buf, 0, sizeof(sdp_buf_t));
+ sdp_list_foreach(rec->attrlist, sdp_attr_size, buf);
+
+ buf->data = malloc(buf->buf_size);
+ if (!buf->data)
+ return -ENOMEM;
+ buf->data_size = 0;
+ memset(buf->data, 0, buf->buf_size);
+
+ sdp_list_foreach(rec->attrlist, sdp_attr_pdu, buf);
+
+ return 0;
+}
+
+void sdp_attr_replace(sdp_record_t *rec, uint16_t attr, sdp_data_t *d)
+{
+ sdp_data_t *p = sdp_data_get(rec, attr);
+
+ if (p) {
+ rec->attrlist = sdp_list_remove(rec->attrlist, p);
+ sdp_data_free(p);
+ }
+
+ d->attrId = attr;
+ rec->attrlist = sdp_list_insert_sorted(rec->attrlist, d, sdp_attrid_comp_func);
+
+ if (attr == SDP_ATTR_SVCLASS_ID_LIST)
+ extract_svclass_uuid(d, &rec->svclass);
+}
+
+int sdp_attrid_comp_func(const void *key1, const void *key2)
+{
+ const sdp_data_t *d1 = (const sdp_data_t *)key1;
+ const sdp_data_t *d2 = (const sdp_data_t *)key2;
+
+ if (d1 && d2)
+ return d1->attrId - d2->attrId;
+ return 0;
+}
+
+static void data_seq_free(sdp_data_t *seq)
+{
+ sdp_data_t *d = seq->val.dataseq;
+
+ while (d) {
+ sdp_data_t *next = d->next;
+ sdp_data_free(d);
+ d = next;
+ }
+}
+
+void sdp_data_free(sdp_data_t *d)
+{
+ switch (d->dtd) {
+ case SDP_SEQ8:
+ case SDP_SEQ16:
+ case SDP_SEQ32:
+ data_seq_free(d);
+ break;
+ case SDP_URL_STR8:
+ case SDP_URL_STR16:
+ case SDP_URL_STR32:
+ case SDP_TEXT_STR8:
+ case SDP_TEXT_STR16:
+ case SDP_TEXT_STR32:
+ free(d->val.str);
+ break;
+ }
+ free(d);
+}
+
+int sdp_uuid_extract(const uint8_t *p, int bufsize, uuid_t *uuid, int *scanned)
+{
+ uint8_t type;
+
+ if (bufsize < (int) sizeof(uint8_t)) {
+ SDPERR("Unexpected end of packet");
+ return -1;
+ }
+
+ type = *(const uint8_t *) p;
+
+ if (!SDP_IS_UUID(type)) {
+ SDPERR("Unknown data type : %d expecting a svc UUID\n", type);
+ return -1;
+ }
+ p += sizeof(uint8_t);
+ *scanned += sizeof(uint8_t);
+ bufsize -= sizeof(uint8_t);
+ if (type == SDP_UUID16) {
+ if (bufsize < (int) sizeof(uint16_t)) {
+ SDPERR("Not enough room for 16-bit UUID");
+ return -1;
+ }
+ sdp_uuid16_create(uuid, ntohs(bt_get_unaligned((uint16_t *) p)));
+ *scanned += sizeof(uint16_t);
+ p += sizeof(uint16_t);
+ } else if (type == SDP_UUID32) {
+ if (bufsize < (int) sizeof(uint32_t)) {
+ SDPERR("Not enough room for 32-bit UUID");
+ return -1;
+ }
+ sdp_uuid32_create(uuid, ntohl(bt_get_unaligned((uint32_t *) p)));
+ *scanned += sizeof(uint32_t);
+ p += sizeof(uint32_t);
+ } else {
+ if (bufsize < (int) sizeof(uint128_t)) {
+ SDPERR("Not enough room for 128-bit UUID");
+ return -1;
+ }
+ sdp_uuid128_create(uuid, p);
+ *scanned += sizeof(uint128_t);
+ p += sizeof(uint128_t);
+ }
+ return 0;
+}
+
+static sdp_data_t *extract_int(const void *p, int bufsize, int *len)
+{
+ sdp_data_t *d;
+
+ if (bufsize < (int) sizeof(uint8_t)) {
+ SDPERR("Unexpected end of packet");
+ return NULL;
+ }
+
+ d = malloc(sizeof(sdp_data_t));
+ if (!d)
+ return NULL;
+
+ SDPDBG("Extracting integer\n");
+ memset(d, 0, sizeof(sdp_data_t));
+ d->dtd = *(uint8_t *) p;
+ p += sizeof(uint8_t);
+ *len += sizeof(uint8_t);
+ bufsize -= sizeof(uint8_t);
+
+ switch (d->dtd) {
+ case SDP_DATA_NIL:
+ break;
+ case SDP_BOOL:
+ case SDP_INT8:
+ case SDP_UINT8:
+ if (bufsize < (int) sizeof(uint8_t)) {
+ SDPERR("Unexpected end of packet");
+ free(d);
+ return NULL;
+ }
+ *len += sizeof(uint8_t);
+ d->val.uint8 = *(uint8_t *) p;
+ break;
+ case SDP_INT16:
+ case SDP_UINT16:
+ if (bufsize < (int) sizeof(uint16_t)) {
+ SDPERR("Unexpected end of packet");
+ free(d);
+ return NULL;
+ }
+ *len += sizeof(uint16_t);
+ d->val.uint16 = ntohs(bt_get_unaligned((uint16_t *) p));
+ break;
+ case SDP_INT32:
+ case SDP_UINT32:
+ if (bufsize < (int) sizeof(uint32_t)) {
+ SDPERR("Unexpected end of packet");
+ free(d);
+ return NULL;
+ }
+ *len += sizeof(uint32_t);
+ d->val.uint32 = ntohl(bt_get_unaligned((uint32_t *) p));
+ break;
+ case SDP_INT64:
+ case SDP_UINT64:
+ if (bufsize < (int) sizeof(uint64_t)) {
+ SDPERR("Unexpected end of packet");
+ free(d);
+ return NULL;
+ }
+ *len += sizeof(uint64_t);
+ d->val.uint64 = ntoh64(bt_get_unaligned((uint64_t *) p));
+ break;
+ case SDP_INT128:
+ case SDP_UINT128:
+ if (bufsize < (int) sizeof(uint128_t)) {
+ SDPERR("Unexpected end of packet");
+ free(d);
+ return NULL;
+ }
+ *len += sizeof(uint128_t);
+ ntoh128((uint128_t *) p, &d->val.uint128);
+ break;
+ default:
+ free(d);
+ d = NULL;
+ }
+ return d;
+}
+
+static sdp_data_t *extract_uuid(const uint8_t *p, int bufsize, int *len,
+ sdp_record_t *rec)
+{
+ sdp_data_t *d = malloc(sizeof(sdp_data_t));
+
+ if (!d)
+ return NULL;
+
+ SDPDBG("Extracting UUID");
+ memset(d, 0, sizeof(sdp_data_t));
+ if (sdp_uuid_extract(p, bufsize, &d->val.uuid, len) < 0) {
+ free(d);
+ return NULL;
+ }
+ d->dtd = *p;
+ if (rec)
+ sdp_pattern_add_uuid(rec, &d->val.uuid);
+ return d;
+}
+
+/*
+ * Extract strings from the PDU (could be service description and similar info)
+ */
+static sdp_data_t *extract_str(const void *p, int bufsize, int *len)
+{
+ char *s;
+ int n;
+ sdp_data_t *d;
+
+ if (bufsize < (int) sizeof(uint8_t)) {
+ SDPERR("Unexpected end of packet");
+ return NULL;
+ }
+
+ d = malloc(sizeof(sdp_data_t));
+ if (!d)
+ return NULL;
+
+ memset(d, 0, sizeof(sdp_data_t));
+ d->dtd = *(uint8_t *) p;
+ p += sizeof(uint8_t);
+ *len += sizeof(uint8_t);
+ bufsize -= sizeof(uint8_t);
+
+ switch (d->dtd) {
+ case SDP_TEXT_STR8:
+ case SDP_URL_STR8:
+ if (bufsize < (int) sizeof(uint8_t)) {
+ SDPERR("Unexpected end of packet");
+ free(d);
+ return NULL;
+ }
+ n = *(uint8_t *) p;
+ p += sizeof(uint8_t);
+ *len += sizeof(uint8_t);
+ bufsize -= sizeof(uint8_t);
+ break;
+ case SDP_TEXT_STR16:
+ case SDP_URL_STR16:
+ if (bufsize < (int) sizeof(uint16_t)) {
+ SDPERR("Unexpected end of packet");
+ free(d);
+ return NULL;
+ }
+ n = ntohs(bt_get_unaligned((uint16_t *) p));
+ p += sizeof(uint16_t);
+ *len += sizeof(uint16_t) + n;
+ bufsize -= sizeof(uint16_t);
+ break;
+ default:
+ SDPERR("Sizeof text string > UINT16_MAX\n");
+ free(d);
+ return NULL;
+ }
+
+ if (bufsize < n) {
+ SDPERR("String too long to fit in packet");
+ free(d);
+ return NULL;
+ }
+
+ s = malloc(n + 1);
+ if (!s) {
+ SDPERR("Not enough memory for incoming string");
+ free(d);
+ return NULL;
+ }
+ memset(s, 0, n + 1);
+ memcpy(s, p, n);
+
+ *len += n;
+
+ SDPDBG("Len : %d\n", n);
+ SDPDBG("Str : %s\n", s);
+
+ d->val.str = s;
+ d->unitSize = n + sizeof(uint8_t);
+ return d;
+}
+
+/*
+ * Extract the sequence type and its length, and return offset into buf
+ * or 0 on failure.
+ */
+int sdp_extract_seqtype(const uint8_t *buf, int bufsize, uint8_t *dtdp, int *size)
+{
+ uint8_t dtd;
+ int scanned = sizeof(uint8_t);
+
+ if (bufsize < (int) sizeof(uint8_t)) {
+ SDPERR("Unexpected end of packet");
+ return 0;
+ }
+
+ dtd = *(uint8_t *) buf;
+ buf += sizeof(uint8_t);
+ bufsize -= sizeof(uint8_t);
+ *dtdp = dtd;
+ switch (dtd) {
+ case SDP_SEQ8:
+ case SDP_ALT8:
+ if (bufsize < (int) sizeof(uint8_t)) {
+ SDPERR("Unexpected end of packet");
+ return 0;
+ }
+ *size = *(uint8_t *) buf;
+ scanned += sizeof(uint8_t);
+ break;
+ case SDP_SEQ16:
+ case SDP_ALT16:
+ if (bufsize < (int) sizeof(uint16_t)) {
+ SDPERR("Unexpected end of packet");
+ return 0;
+ }
+ *size = ntohs(bt_get_unaligned((uint16_t *) buf));
+ scanned += sizeof(uint16_t);
+ break;
+ case SDP_SEQ32:
+ case SDP_ALT32:
+ if (bufsize < (int) sizeof(uint32_t)) {
+ SDPERR("Unexpected end of packet");
+ return 0;
+ }
+ *size = ntohl(bt_get_unaligned((uint32_t *) buf));
+ scanned += sizeof(uint32_t);
+ break;
+ default:
+ SDPERR("Unknown sequence type, aborting\n");
+ return 0;
+ }
+ return scanned;
+}
+
+static sdp_data_t *extract_seq(const void *p, int bufsize, int *len,
+ sdp_record_t *rec)
+{
+ int seqlen, n = 0;
+ sdp_data_t *curr, *prev;
+ sdp_data_t *d = malloc(sizeof(sdp_data_t));
+
+ if (!d)
+ return NULL;
+
+ SDPDBG("Extracting SEQ");
+ memset(d, 0, sizeof(sdp_data_t));
+ *len = sdp_extract_seqtype(p, bufsize, &d->dtd, &seqlen);
+ SDPDBG("Sequence Type : 0x%x length : 0x%x\n", d->dtd, seqlen);
+
+ if (*len == 0)
+ return d;
+
+ if (*len > bufsize) {
+ SDPERR("Packet not big enough to hold sequence.");
+ free(d);
+ return NULL;
+ }
+
+ p += *len;
+ bufsize -= *len;
+ prev = NULL;
+ while (n < seqlen) {
+ int attrlen = 0;
+ curr = sdp_extract_attr(p, bufsize, &attrlen, rec);
+ if (curr == NULL)
+ break;
+
+ if (prev)
+ prev->next = curr;
+ else
+ d->val.dataseq = curr;
+ prev = curr;
+ p += attrlen;
+ n += attrlen;
+ bufsize -= attrlen;
+
+ SDPDBG("Extracted: %d SequenceLength: %d", n, seqlen);
+ }
+
+ *len += n;
+ return d;
+}
+
+sdp_data_t *sdp_extract_attr(const uint8_t *p, int bufsize, int *size,
+ sdp_record_t *rec)
+{
+ sdp_data_t *elem;
+ int n = 0;
+ uint8_t dtd;
+
+ if (bufsize < (int) sizeof(uint8_t)) {
+ SDPERR("Unexpected end of packet");
+ return NULL;
+ }
+
+ dtd = *(const uint8_t *)p;
+
+ SDPDBG("extract_attr: dtd=0x%x", dtd);
+ switch (dtd) {
+ case SDP_DATA_NIL:
+ case SDP_BOOL:
+ case SDP_UINT8:
+ case SDP_UINT16:
+ case SDP_UINT32:
+ case SDP_UINT64:
+ case SDP_UINT128:
+ case SDP_INT8:
+ case SDP_INT16:
+ case SDP_INT32:
+ case SDP_INT64:
+ case SDP_INT128:
+ elem = extract_int(p, bufsize, &n);
+ break;
+ case SDP_UUID16:
+ case SDP_UUID32:
+ case SDP_UUID128:
+ elem = extract_uuid(p, bufsize, &n, rec);
+ break;
+ case SDP_TEXT_STR8:
+ case SDP_TEXT_STR16:
+ case SDP_TEXT_STR32:
+ case SDP_URL_STR8:
+ case SDP_URL_STR16:
+ case SDP_URL_STR32:
+ elem = extract_str(p, bufsize, &n);
+ break;
+ case SDP_SEQ8:
+ case SDP_SEQ16:
+ case SDP_SEQ32:
+ case SDP_ALT8:
+ case SDP_ALT16:
+ case SDP_ALT32:
+ elem = extract_seq(p, bufsize, &n, rec);
+ break;
+ default:
+ SDPERR("Unknown data descriptor : 0x%x terminating\n", dtd);
+ return NULL;
+ }
+ *size += n;
+ return elem;
+}
+
+#ifdef SDP_DEBUG
+static void attr_print_func(void *value, void *userData)
+{
+ sdp_data_t *d = (sdp_data_t *)value;
+
+ SDPDBG("=====================================\n");
+ SDPDBG("ATTRIBUTE IDENTIFIER : 0x%x\n", d->attrId);
+ SDPDBG("ATTRIBUTE VALUE PTR : 0x%x\n", (uint32_t)value);
+ if (d)
+ sdp_data_print(d);
+ else
+ SDPDBG("NULL value\n");
+ SDPDBG("=====================================\n");
+}
+
+void sdp_print_service_attr(sdp_list_t *svcAttrList)
+{
+ SDPDBG("Printing service attr list %p\n", svcAttrList);
+ sdp_list_foreach(svcAttrList, attr_print_func, NULL);
+ SDPDBG("Printed service attr list %p\n", svcAttrList);
+}
+#endif
+
+sdp_record_t *sdp_extract_pdu(const uint8_t *buf, int bufsize, int *scanned)
+{
+ int extracted = 0, seqlen = 0;
+ uint8_t dtd;
+ uint16_t attr;
+ sdp_record_t *rec = sdp_record_alloc();
+ const uint8_t *p = buf;
+
+ *scanned = sdp_extract_seqtype(buf, bufsize, &dtd, &seqlen);
+ p += *scanned;
+ bufsize -= *scanned;
+ rec->attrlist = NULL;
+
+ while (extracted < seqlen && bufsize > 0) {
+ int n = sizeof(uint8_t), attrlen = 0;
+ sdp_data_t *data = NULL;
+
+ SDPDBG("Extract PDU, sequenceLength: %d localExtractedLength: %d",
+ seqlen, extracted);
+
+ if (bufsize < n + (int) sizeof(uint16_t)) {
+ SDPERR("Unexpected end of packet");
+ break;
+ }
+
+ dtd = *(uint8_t *) p;
+ attr = ntohs(bt_get_unaligned((uint16_t *) (p + n)));
+ n += sizeof(uint16_t);
+
+ SDPDBG("DTD of attrId : %d Attr id : 0x%x \n", dtd, attr);
+
+ data = sdp_extract_attr(p + n, bufsize - n, &attrlen, rec);
+
+ SDPDBG("Attr id : 0x%x attrValueLength : %d\n", attr, attrlen);
+
+ n += attrlen;
+ if (data == NULL) {
+ SDPDBG("Terminating extraction of attributes");
+ break;
+ }
+
+ if (attr == SDP_ATTR_RECORD_HANDLE)
+ rec->handle = data->val.uint32;
+
+ if (attr == SDP_ATTR_SVCLASS_ID_LIST)
+ extract_svclass_uuid(data, &rec->svclass);
+
+ extracted += n;
+ p += n;
+ bufsize -= n;
+ sdp_attr_replace(rec, attr, data);
+
+ SDPDBG("Extract PDU, seqLength: %d localExtractedLength: %d",
+ seqlen, extracted);
+ }
+#ifdef SDP_DEBUG
+ SDPDBG("Successful extracting of Svc Rec attributes\n");
+ sdp_print_service_attr(rec->attrlist);
+#endif
+ *scanned += seqlen;
+ return rec;
+}
+
+static void sdp_copy_pattern(void *value, void *udata)
+{
+ uuid_t *uuid = value;
+ sdp_record_t *rec = udata;
+
+ sdp_pattern_add_uuid(rec, uuid);
+}
+
+static void *sdp_data_value(sdp_data_t *data, uint32_t *len)
+{
+ void *val = NULL;
+
+ switch (data->dtd) {
+ case SDP_DATA_NIL:
+ break;
+ case SDP_UINT8:
+ val = &data->val.uint8;
+ break;
+ case SDP_INT8:
+ case SDP_BOOL:
+ val = &data->val.int8;
+ break;
+ case SDP_UINT16:
+ val = &data->val.uint16;
+ break;
+ case SDP_INT16:
+ val = &data->val.int16;
+ break;
+ case SDP_UINT32:
+ val = &data->val.uint32;
+ break;
+ case SDP_INT32:
+ val = &data->val.int32;
+ break;
+ case SDP_INT64:
+ val = &data->val.int64;
+ break;
+ case SDP_UINT64:
+ val = &data->val.uint64;
+ break;
+ case SDP_UINT128:
+ val = &data->val.uint128;
+ break;
+ case SDP_INT128:
+ val = &data->val.int128;
+ break;
+ case SDP_UUID16:
+ val = &data->val.uuid.value.uuid16;
+ break;
+ case SDP_UUID32:
+ val = &data->val.uuid.value.uuid32;
+ break;
+ case SDP_UUID128:
+ val = &data->val.uuid.value.uuid128;
+ break;
+ case SDP_URL_STR8:
+ case SDP_URL_STR16:
+ case SDP_TEXT_STR8:
+ case SDP_TEXT_STR16:
+ case SDP_URL_STR32:
+ case SDP_TEXT_STR32:
+ val = data->val.str;
+ if (len)
+ *len = data->unitSize - 1;
+ break;
+ case SDP_ALT8:
+ case SDP_ALT16:
+ case SDP_ALT32:
+ case SDP_SEQ8:
+ case SDP_SEQ16:
+ case SDP_SEQ32:
+ val = sdp_copy_seq(data->val.dataseq);
+ break;
+ }
+
+ return val;
+}
+
+static sdp_data_t *sdp_copy_seq(sdp_data_t *data)
+{
+ sdp_data_t *tmp, *seq = NULL, *cur = NULL;
+
+ for (tmp = data; tmp; tmp = tmp->next) {
+ sdp_data_t *datatmp;
+ void *value;
+
+ value = sdp_data_value(tmp, NULL);
+ datatmp = sdp_data_alloc_with_length(tmp->dtd, value,
+ tmp->unitSize);
+
+ if (cur)
+ cur->next = datatmp;
+ else
+ seq = datatmp;
+
+ cur = datatmp;
+ }
+
+ return seq;
+}
+
+static void sdp_copy_attrlist(void *value, void *udata)
+{
+ sdp_data_t *data = value;
+ sdp_record_t *rec = udata;
+ void *val;
+ uint32_t len = 0;
+
+ val = sdp_data_value(data, &len);
+
+ if (!len)
+ sdp_attr_add_new(rec, data->attrId, data->dtd, val);
+ else
+ sdp_attr_add_new_with_length(rec, data->attrId,
+ data->dtd, val, len);
+}
+
+sdp_record_t *sdp_copy_record(sdp_record_t *rec)
+{
+ sdp_record_t *cpy;
+
+ cpy = sdp_record_alloc();
+
+ cpy->handle = rec->handle;
+
+ sdp_list_foreach(rec->pattern, sdp_copy_pattern, cpy);
+ sdp_list_foreach(rec->attrlist, sdp_copy_attrlist, cpy);
+
+ cpy->svclass = rec->svclass;
+
+ return cpy;
+}
+
+#ifdef SDP_DEBUG
+static void print_dataseq(sdp_data_t *p)
+{
+ sdp_data_t *d;
+
+ for (d = p; d; d = d->next)
+ sdp_data_print(d);
+}
+#endif
+
+void sdp_record_print(const sdp_record_t *rec)
+{
+ sdp_data_t *d = sdp_data_get(rec, SDP_ATTR_SVCNAME_PRIMARY);
+ if (d)
+ printf("Service Name: %.*s\n", d->unitSize, d->val.str);
+ d = sdp_data_get(rec, SDP_ATTR_SVCDESC_PRIMARY);
+ if (d)
+ printf("Service Description: %.*s\n", d->unitSize, d->val.str);
+ d = sdp_data_get(rec, SDP_ATTR_PROVNAME_PRIMARY);
+ if (d)
+ printf("Service Provider: %.*s\n", d->unitSize, d->val.str);
+}
+
+#ifdef SDP_DEBUG
+void sdp_data_print(sdp_data_t *d)
+{
+ switch (d->dtd) {
+ case SDP_DATA_NIL:
+ SDPDBG("NIL\n");
+ break;
+ case SDP_BOOL:
+ case SDP_UINT8:
+ case SDP_UINT16:
+ case SDP_UINT32:
+ case SDP_UINT64:
+ case SDP_UINT128:
+ case SDP_INT8:
+ case SDP_INT16:
+ case SDP_INT32:
+ case SDP_INT64:
+ case SDP_INT128:
+ SDPDBG("Integer : 0x%x\n", d->val.uint32);
+ break;
+ case SDP_UUID16:
+ case SDP_UUID32:
+ case SDP_UUID128:
+ SDPDBG("UUID\n");
+ sdp_uuid_print(&d->val.uuid);
+ break;
+ case SDP_TEXT_STR8:
+ case SDP_TEXT_STR16:
+ case SDP_TEXT_STR32:
+ SDPDBG("Text : %s\n", d->val.str);
+ break;
+ case SDP_URL_STR8:
+ case SDP_URL_STR16:
+ case SDP_URL_STR32:
+ SDPDBG("URL : %s\n", d->val.str);
+ break;
+ case SDP_SEQ8:
+ case SDP_SEQ16:
+ case SDP_SEQ32:
+ print_dataseq(d->val.dataseq);
+ break;
+ case SDP_ALT8:
+ case SDP_ALT16:
+ case SDP_ALT32:
+ SDPDBG("Data Sequence Alternates\n");
+ print_dataseq(d->val.dataseq);
+ break;
+ }
+}
+#endif
+
+sdp_data_t *sdp_data_get(const sdp_record_t *rec, uint16_t attrId)
+{
+ if (rec->attrlist) {
+ sdp_data_t sdpTemplate;
+ sdp_list_t *p;
+
+ sdpTemplate.attrId = attrId;
+ p = sdp_list_find(rec->attrlist, &sdpTemplate, sdp_attrid_comp_func);
+ if (p)
+ return p->data;
+ }
+ return NULL;
+}
+
+static int sdp_send_req(sdp_session_t *session, uint8_t *buf, uint32_t size)
+{
+ uint32_t sent = 0;
+
+ while (sent < size) {
+ int n = send(session->sock, buf + sent, size - sent, 0);
+ if (n < 0)
+ return -1;
+ sent += n;
+ }
+ return 0;
+}
+
+static int sdp_read_rsp(sdp_session_t *session, uint8_t *buf, uint32_t size)
+{
+ fd_set readFds;
+ struct timeval timeout = { SDP_RESPONSE_TIMEOUT, 0 };
+
+ FD_ZERO(&readFds);
+ FD_SET(session->sock, &readFds);
+ SDPDBG("Waiting for response\n");
+ if (select(session->sock + 1, &readFds, NULL, NULL, &timeout) == 0) {
+ SDPERR("Client timed out\n");
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ return recv(session->sock, buf, size, 0);
+}
+
+/*
+ * generic send request, wait for response method.
+ */
+int sdp_send_req_w4_rsp(sdp_session_t *session, uint8_t *reqbuf,
+ uint8_t *rspbuf, uint32_t reqsize, uint32_t *rspsize)
+{
+ int n;
+ sdp_pdu_hdr_t *reqhdr = (sdp_pdu_hdr_t *) reqbuf;
+ sdp_pdu_hdr_t *rsphdr = (sdp_pdu_hdr_t *) rspbuf;
+
+ SDPDBG("");
+ if (0 > sdp_send_req(session, reqbuf, reqsize)) {
+ SDPERR("Error sending data:%s", strerror(errno));
+ return -1;
+ }
+ n = sdp_read_rsp(session, rspbuf, SDP_RSP_BUFFER_SIZE);
+ if (0 > n)
+ return -1;
+ SDPDBG("Read : %d\n", n);
+ if (n == 0 || reqhdr->tid != rsphdr->tid) {
+ errno = EPROTO;
+ return -1;
+ }
+ *rspsize = n;
+ return 0;
+}
+
+/*
+ * singly-linked lists (after openobex implementation)
+ */
+sdp_list_t *sdp_list_append(sdp_list_t *p, void *d)
+{
+ sdp_list_t *q, *n = malloc(sizeof(sdp_list_t));
+
+ if (!n)
+ return NULL;
+
+ n->data = d;
+ n->next = 0;
+
+ if (!p)
+ return n;
+
+ for (q = p; q->next; q = q->next);
+ q->next = n;
+
+ return p;
+}
+
+sdp_list_t *sdp_list_remove(sdp_list_t *list, void *d)
+{
+ sdp_list_t *p, *q;
+
+ for (q = 0, p = list; p; q = p, p = p->next)
+ if (p->data == d) {
+ if (q)
+ q->next = p->next;
+ else
+ list = p->next;
+ free(p);
+ break;
+ }
+
+ return list;
+}
+
+sdp_list_t *sdp_list_insert_sorted(sdp_list_t *list, void *d,
+ sdp_comp_func_t f)
+{
+ sdp_list_t *q, *p, *n;
+
+ n = malloc(sizeof(sdp_list_t));
+ if (!n)
+ return NULL;
+ n->data = d;
+ for (q = 0, p = list; p; q = p, p = p->next)
+ if (f(p->data, d) >= 0)
+ break;
+ // insert between q and p; if !q insert at head
+ if (q)
+ q->next = n;
+ else
+ list = n;
+ n->next = p;
+ return list;
+}
+
+/*
+ * Every element of the list points to things which need
+ * to be free()'d. This method frees the list's contents
+ */
+void sdp_list_free(sdp_list_t *list, sdp_free_func_t f)
+{
+ sdp_list_t *next;
+ while (list) {
+ next = list->next;
+ if (f)
+ f(list->data);
+ free(list);
+ list = next;
+ }
+}
+
+static inline int __find_port(sdp_data_t *seq, int proto)
+{
+ if (!seq || !seq->next)
+ return 0;
+
+ if (SDP_IS_UUID(seq->dtd) && sdp_uuid_to_proto(&seq->val.uuid) == proto) {
+ seq = seq->next;
+ switch (seq->dtd) {
+ case SDP_UINT8:
+ return seq->val.uint8;
+ case SDP_UINT16:
+ return seq->val.uint16;
+ }
+ }
+ return 0;
+}
+
+int sdp_get_proto_port(const sdp_list_t *list, int proto)
+{
+ if (proto != L2CAP_UUID && proto != RFCOMM_UUID) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ for (; list; list = list->next) {
+ sdp_list_t *p;
+ for (p = list->data; p; p = p->next) {
+ sdp_data_t *seq = p->data;
+ int port = __find_port(seq, proto);
+ if (port)
+ return port;
+ }
+ }
+ return 0;
+}
+
+sdp_data_t *sdp_get_proto_desc(sdp_list_t *list, int proto)
+{
+ for (; list; list = list->next) {
+ sdp_list_t *p;
+ for (p = list->data; p; p = p->next) {
+ sdp_data_t *seq = p->data;
+ if (SDP_IS_UUID(seq->dtd) &&
+ sdp_uuid_to_proto(&seq->val.uuid) == proto)
+ return seq->next;
+ }
+ }
+ return NULL;
+}
+
+int sdp_get_access_protos(const sdp_record_t *rec, sdp_list_t **pap)
+{
+ sdp_data_t *pdlist, *curr;
+ sdp_list_t *ap = 0;
+
+ pdlist = sdp_data_get(rec, SDP_ATTR_PROTO_DESC_LIST);
+ if (pdlist == NULL) {
+ errno = ENODATA;
+ return -1;
+ }
+ SDPDBG("AP type : 0%x\n", pdlist->dtd);
+
+ for (; pdlist; pdlist = pdlist->next) {
+ sdp_list_t *pds = 0;
+ for (curr = pdlist->val.dataseq; curr; curr = curr->next)
+ pds = sdp_list_append(pds, curr->val.dataseq);
+ ap = sdp_list_append(ap, pds);
+ }
+ *pap = ap;
+ return 0;
+}
+
+int sdp_get_add_access_protos(const sdp_record_t *rec, sdp_list_t **pap)
+{
+ sdp_data_t *pdlist, *curr;
+ sdp_list_t *ap = 0;
+
+ pdlist = sdp_data_get(rec, SDP_ATTR_ADD_PROTO_DESC_LIST);
+ if (pdlist == NULL) {
+ errno = ENODATA;
+ return -1;
+ }
+ SDPDBG("AP type : 0%x\n", pdlist->dtd);
+
+ pdlist = pdlist->val.dataseq;
+
+ for (; pdlist; pdlist = pdlist->next) {
+ sdp_list_t *pds = 0;
+ for (curr = pdlist->val.dataseq; curr; curr = curr->next)
+ pds = sdp_list_append(pds, curr->val.dataseq);
+ ap = sdp_list_append(ap, pds);
+ }
+ *pap = ap;
+ return 0;
+}
+
+int sdp_get_uuidseq_attr(const sdp_record_t *rec, uint16_t attr,
+ sdp_list_t **seqp)
+{
+ sdp_data_t *sdpdata = sdp_data_get(rec, attr);
+
+ *seqp = NULL;
+ if (sdpdata && sdpdata->dtd >= SDP_SEQ8 && sdpdata->dtd <= SDP_SEQ32) {
+ sdp_data_t *d;
+ for (d = sdpdata->val.dataseq; d; d = d->next) {
+ uuid_t *u;
+ if (d->dtd < SDP_UUID16 || d->dtd > SDP_UUID128) {
+ errno = EINVAL;
+ goto fail;
+ }
+
+ u = malloc(sizeof(uuid_t));
+ if (!u)
+ goto fail;
+
+ memset(u, 0, sizeof(uuid_t));
+ *u = d->val.uuid;
+ *seqp = sdp_list_append(*seqp, u);
+ }
+ return 0;
+ }
+fail:
+ sdp_list_free(*seqp, free);
+ *seqp = NULL;
+ return -1;
+}
+
+int sdp_set_uuidseq_attr(sdp_record_t *rec, uint16_t aid, sdp_list_t *seq)
+{
+ int status = 0, i, len;
+ void **dtds, **values;
+ uint8_t uuid16 = SDP_UUID16;
+ uint8_t uuid32 = SDP_UUID32;
+ uint8_t uuid128 = SDP_UUID128;
+ sdp_list_t *p;
+
+ len = sdp_list_len(seq);
+ if (!seq || len == 0)
+ return -1;
+ dtds = malloc(len * sizeof(void *));
+ if (!dtds)
+ return -1;
+
+ values = malloc(len * sizeof(void *));
+ if (!values) {
+ free(dtds);
+ return -1;
+ }
+
+ for (p = seq, i = 0; i < len; i++, p = p->next) {
+ uuid_t *uuid = p->data;
+ if (uuid)
+ switch (uuid->type) {
+ case SDP_UUID16:
+ dtds[i] = &uuid16;
+ values[i] = &uuid->value.uuid16;
+ break;
+ case SDP_UUID32:
+ dtds[i] = &uuid32;
+ values[i] = &uuid->value.uuid32;
+ break;
+ case SDP_UUID128:
+ dtds[i] = &uuid128;
+ values[i] = &uuid->value.uuid128;
+ break;
+ default:
+ status = -1;
+ break;
+ }
+ else {
+ status = -1;
+ break;
+ }
+ }
+ if (status == 0) {
+ sdp_data_t *data = sdp_seq_alloc(dtds, values, len);
+ sdp_attr_replace(rec, aid, data);
+ sdp_pattern_add_uuidseq(rec, seq);
+ }
+ free(dtds);
+ free(values);
+ return status;
+}
+
+int sdp_get_lang_attr(const sdp_record_t *rec, sdp_list_t **langSeq)
+{
+ sdp_lang_attr_t *lang;
+ sdp_data_t *sdpdata, *curr_data;
+
+ *langSeq = NULL;
+ sdpdata = sdp_data_get(rec, SDP_ATTR_LANG_BASE_ATTR_ID_LIST);
+ if (sdpdata == NULL) {
+ errno = ENODATA;
+ return -1;
+ }
+ curr_data = sdpdata->val.dataseq;
+ while (curr_data) {
+ sdp_data_t *pCode = curr_data;
+ sdp_data_t *pEncoding = pCode->next;
+ sdp_data_t *pOffset = pEncoding->next;
+ if (pEncoding && pOffset) {
+ lang = malloc(sizeof(sdp_lang_attr_t));
+ if (!lang) {
+ sdp_list_free(*langSeq, free);
+ *langSeq = NULL;
+ return -1;
+ }
+ lang->code_ISO639 = pCode->val.uint16;
+ lang->encoding = pEncoding->val.uint16;
+ lang->base_offset = pOffset->val.uint16;
+ SDPDBG("code_ISO639 : 0x%02x\n", lang->code_ISO639);
+ SDPDBG("encoding : 0x%02x\n", lang->encoding);
+ SDPDBG("base_offfset : 0x%02x\n", lang->base_offset);
+ *langSeq = sdp_list_append(*langSeq, lang);
+ }
+ curr_data = pOffset->next;
+ }
+ return 0;
+}
+
+int sdp_get_profile_descs(const sdp_record_t *rec, sdp_list_t **profDescSeq)
+{
+ sdp_profile_desc_t *profDesc;
+ sdp_data_t *sdpdata, *seq;
+
+ *profDescSeq = NULL;
+ sdpdata = sdp_data_get(rec, SDP_ATTR_PFILE_DESC_LIST);
+ if (!sdpdata || !sdpdata->val.dataseq) {
+ errno = ENODATA;
+ return -1;
+ }
+ for (seq = sdpdata->val.dataseq; seq && seq->val.dataseq; seq = seq->next) {
+ uuid_t *uuid = NULL;
+ uint16_t version = 0x100;
+
+ if (SDP_IS_UUID(seq->dtd)) {
+ uuid = &seq->val.uuid;
+ } else {
+ sdp_data_t *puuid = seq->val.dataseq;
+ sdp_data_t *pVnum = seq->val.dataseq->next;
+ if (puuid && pVnum) {
+ uuid = &puuid->val.uuid;
+ version = pVnum->val.uint16;
+ }
+ }
+
+ if (uuid != NULL) {
+ profDesc = malloc(sizeof(sdp_profile_desc_t));
+ if (!profDesc) {
+ sdp_list_free(*profDescSeq, free);
+ *profDescSeq = NULL;
+ return -1;
+ }
+ profDesc->uuid = *uuid;
+ profDesc->version = version;
+#ifdef SDP_DEBUG
+ sdp_uuid_print(&profDesc->uuid);
+ SDPDBG("Vnum : 0x%04x\n", profDesc->version);
+#endif
+ *profDescSeq = sdp_list_append(*profDescSeq, profDesc);
+ }
+ }
+ return 0;
+}
+
+int sdp_get_server_ver(const sdp_record_t *rec, sdp_list_t **u16)
+{
+ sdp_data_t *d, *curr;
+
+ *u16 = NULL;
+ d = sdp_data_get(rec, SDP_ATTR_VERSION_NUM_LIST);
+ if (d == NULL) {
+ errno = ENODATA;
+ return -1;
+ }
+ for (curr = d->val.dataseq; curr; curr = curr->next)
+ *u16 = sdp_list_append(*u16, &curr->val.uint16);
+ return 0;
+}
+
+/* flexible extraction of basic attributes - Jean II */
+/* How do we expect caller to extract predefined data sequences? */
+int sdp_get_int_attr(const sdp_record_t *rec, uint16_t attrid, int *value)
+{
+ sdp_data_t *sdpdata = sdp_data_get(rec, attrid);
+
+ if (sdpdata)
+ /* Verify that it is what the caller expects */
+ if (sdpdata->dtd == SDP_BOOL || sdpdata->dtd == SDP_UINT8 ||
+ sdpdata->dtd == SDP_UINT16 || sdpdata->dtd == SDP_UINT32 ||
+ sdpdata->dtd == SDP_INT8 || sdpdata->dtd == SDP_INT16 ||
+ sdpdata->dtd == SDP_INT32) {
+ *value = sdpdata->val.uint32;
+ return 0;
+ }
+ errno = EINVAL;
+ return -1;
+}
+
+int sdp_get_string_attr(const sdp_record_t *rec, uint16_t attrid, char *value,
+ int valuelen)
+{
+ sdp_data_t *sdpdata = sdp_data_get(rec, attrid);
+ if (sdpdata)
+ /* Verify that it is what the caller expects */
+ if (sdpdata->dtd == SDP_TEXT_STR8 ||
+ sdpdata->dtd == SDP_TEXT_STR16 ||
+ sdpdata->dtd == SDP_TEXT_STR32)
+ if ((int) strlen(sdpdata->val.str) < valuelen) {
+ strcpy(value, sdpdata->val.str);
+ return 0;
+ }
+ errno = EINVAL;
+ return -1;
+}
+
+#define get_basic_attr(attrID, pAttrValue, fieldName) \
+ sdp_data_t *data = sdp_data_get(rec, attrID); \
+ if (data) { \
+ *pAttrValue = data->val.fieldName; \
+ return 0; \
+ } \
+ errno = EINVAL; \
+ return -1;
+
+int sdp_get_service_id(const sdp_record_t *rec, uuid_t *uuid)
+{
+ get_basic_attr(SDP_ATTR_SERVICE_ID, uuid, uuid);
+}
+
+int sdp_get_group_id(const sdp_record_t *rec, uuid_t *uuid)
+{
+ get_basic_attr(SDP_ATTR_GROUP_ID, uuid, uuid);
+}
+
+int sdp_get_record_state(const sdp_record_t *rec, uint32_t *svcRecState)
+{
+ get_basic_attr(SDP_ATTR_RECORD_STATE, svcRecState, uint32);
+}
+
+int sdp_get_service_avail(const sdp_record_t *rec, uint8_t *svcAvail)
+{
+ get_basic_attr(SDP_ATTR_SERVICE_AVAILABILITY, svcAvail, uint8);
+}
+
+int sdp_get_service_ttl(const sdp_record_t *rec, uint32_t *svcTTLInfo)
+{
+ get_basic_attr(SDP_ATTR_SVCINFO_TTL, svcTTLInfo, uint32);
+}
+
+int sdp_get_database_state(const sdp_record_t *rec, uint32_t *svcDBState)
+{
+ get_basic_attr(SDP_ATTR_SVCDB_STATE, svcDBState, uint32);
+}
+
+/*
+ * NOTE that none of the setXXX() functions below will
+ * actually update the SDP server, unless the
+ * {register, update}sdp_record_t() function is invoked.
+ */
+
+int sdp_attr_add_new(sdp_record_t *rec, uint16_t attr, uint8_t dtd,
+ const void *value)
+{
+ sdp_data_t *d = sdp_data_alloc(dtd, value);
+ if (d) {
+ sdp_attr_replace(rec, attr, d);
+ return 0;
+ }
+ return -1;
+}
+
+static int sdp_attr_add_new_with_length(sdp_record_t *rec,
+ uint16_t attr, uint8_t dtd, const void *value, uint32_t len)
+{
+ sdp_data_t *d;
+
+ d = sdp_data_alloc_with_length(dtd, value, len);
+ if (!d)
+ return -1;
+
+ sdp_attr_replace(rec, attr, d);
+
+ return 0;
+}
+
+/*
+ * Set the information attributes of the service
+ * pointed to by rec. The attributes are
+ * service name, description and provider name
+ */
+void sdp_set_info_attr(sdp_record_t *rec, const char *name, const char *prov,
+ const char *desc)
+{
+ if (name)
+ sdp_attr_add_new(rec, SDP_ATTR_SVCNAME_PRIMARY,
+ SDP_TEXT_STR8, name);
+ if (prov)
+ sdp_attr_add_new(rec, SDP_ATTR_PROVNAME_PRIMARY,
+ SDP_TEXT_STR8, prov);
+ if (desc)
+ sdp_attr_add_new(rec, SDP_ATTR_SVCDESC_PRIMARY,
+ SDP_TEXT_STR8, desc);
+}
+
+static sdp_data_t *access_proto_to_dataseq(sdp_record_t *rec, sdp_list_t *proto)
+{
+ sdp_data_t *seq = NULL;
+ void *dtds[10], *values[10];
+ void **seqDTDs, **seqs;
+ int i, seqlen;
+ sdp_list_t *p;
+
+ seqlen = sdp_list_len(proto);
+ seqDTDs = malloc(seqlen * sizeof(void *));
+ if (!seqDTDs)
+ return NULL;
+
+ seqs = malloc(seqlen * sizeof(void *));
+ if (!seqs) {
+ free(seqDTDs);
+ return NULL;
+ }
+
+ for (i = 0, p = proto; p; p = p->next, i++) {
+ sdp_list_t *elt = p->data;
+ sdp_data_t *s;
+ uuid_t *uuid = NULL;
+ unsigned int pslen = 0;
+ for (; elt && pslen < ARRAY_SIZE(dtds); elt = elt->next, pslen++) {
+ sdp_data_t *d = elt->data;
+ dtds[pslen] = &d->dtd;
+ switch (d->dtd) {
+ case SDP_UUID16:
+ uuid = (uuid_t *) d;
+ values[pslen] = &uuid->value.uuid16;
+ break;
+ case SDP_UUID32:
+ uuid = (uuid_t *) d;
+ values[pslen] = &uuid->value.uuid32;
+ break;
+ case SDP_UUID128:
+ uuid = (uuid_t *) d;
+ values[pslen] = &uuid->value.uuid128;
+ break;
+ case SDP_UINT8:
+ values[pslen] = &d->val.uint8;
+ break;
+ case SDP_UINT16:
+ values[pslen] = &d->val.uint16;
+ break;
+ case SDP_SEQ8:
+ case SDP_SEQ16:
+ case SDP_SEQ32:
+ values[pslen] = d;
+ break;
+ /* FIXME: more */
+ }
+ }
+ s = sdp_seq_alloc(dtds, values, pslen);
+ if (s) {
+ seqDTDs[i] = &s->dtd;
+ seqs[i] = s;
+ if (uuid)
+ sdp_pattern_add_uuid(rec, uuid);
+ }
+ }
+ seq = sdp_seq_alloc(seqDTDs, seqs, seqlen);
+ free(seqDTDs);
+ free(seqs);
+ return seq;
+}
+
+/*
+ * sets the access protocols of the service specified
+ * to the value specified in "access_proto"
+ *
+ * Note that if there are alternate mechanisms by
+ * which the service is accessed, then they should
+ * be specified as sequences
+ *
+ * Using a value of NULL for accessProtocols has
+ * effect of removing this attribute (if previously set)
+ *
+ * This function replaces the existing sdp_access_proto_t
+ * structure (if any) with the new one specified.
+ *
+ * returns 0 if successful or -1 if there is a failure.
+ */
+int sdp_set_access_protos(sdp_record_t *rec, const sdp_list_t *ap)
+{
+ const sdp_list_t *p;
+ sdp_data_t *protos = NULL;
+
+ for (p = ap; p; p = p->next) {
+ sdp_data_t *seq = access_proto_to_dataseq(rec, p->data);
+ protos = sdp_seq_append(protos, seq);
+ }
+
+ sdp_attr_add(rec, SDP_ATTR_PROTO_DESC_LIST, protos);
+
+ return 0;
+}
+
+int sdp_set_add_access_protos(sdp_record_t *rec, const sdp_list_t *ap)
+{
+ const sdp_list_t *p;
+ sdp_data_t *protos = NULL;
+
+ for (p = ap; p; p = p->next) {
+ sdp_data_t *seq = access_proto_to_dataseq(rec, p->data);
+ protos = sdp_seq_append(protos, seq);
+ }
+
+ sdp_attr_add(rec, SDP_ATTR_ADD_PROTO_DESC_LIST,
+ protos ? sdp_data_alloc(SDP_SEQ8, protos) : NULL);
+
+ return 0;
+}
+
+/*
+ * set the "LanguageBase" attributes of the service record
+ * record to the value specified in "langAttrList".
+ *
+ * "langAttrList" is a linked list of "sdp_lang_attr_t"
+ * objects, one for each language in which user visible
+ * attributes are present in the service record.
+ *
+ * Using a value of NULL for langAttrList has
+ * effect of removing this attribute (if previously set)
+ *
+ * This function replaces the exisiting sdp_lang_attr_t
+ * structure (if any) with the new one specified.
+ *
+ * returns 0 if successful or -1 if there is a failure.
+ */
+int sdp_set_lang_attr(sdp_record_t *rec, const sdp_list_t *seq)
+{
+ uint8_t uint16 = SDP_UINT16;
+ int status = 0, i = 0, seqlen = sdp_list_len(seq);
+ void **dtds, **values;
+ const sdp_list_t *p;
+
+ dtds = malloc(3 * seqlen * sizeof(void *));
+ if (!dtds)
+ return -1;
+
+ values = malloc(3 * seqlen * sizeof(void *));
+ if (!values) {
+ free(dtds);
+ return -1;
+ }
+
+ for (p = seq; p; p = p->next) {
+ sdp_lang_attr_t *lang = p->data;
+ if (!lang) {
+ status = -1;
+ break;
+ }
+ dtds[i] = &uint16;
+ values[i] = &lang->code_ISO639;
+ i++;
+ dtds[i] = &uint16;
+ values[i] = &lang->encoding;
+ i++;
+ dtds[i] = &uint16;
+ values[i] = &lang->base_offset;
+ i++;
+ }
+ if (status == 0) {
+ sdp_data_t *seq = sdp_seq_alloc(dtds, values, 3 * seqlen);
+ sdp_attr_add(rec, SDP_ATTR_LANG_BASE_ATTR_ID_LIST, seq);
+ }
+ free(dtds);
+ free(values);
+ return status;
+}
+
+/*
+ * set the "ServiceID" attribute of the service.
+ *
+ * This is the UUID of the service.
+ *
+ * returns 0 if successful or -1 if there is a failure.
+ */
+void sdp_set_service_id(sdp_record_t *rec, uuid_t uuid)
+{
+ switch (uuid.type) {
+ case SDP_UUID16:
+ sdp_attr_add_new(rec, SDP_ATTR_SERVICE_ID, SDP_UUID16,
+ &uuid.value.uuid16);
+ break;
+ case SDP_UUID32:
+ sdp_attr_add_new(rec, SDP_ATTR_SERVICE_ID, SDP_UUID32,
+ &uuid.value.uuid32);
+ break;
+ case SDP_UUID128:
+ sdp_attr_add_new(rec, SDP_ATTR_SERVICE_ID, SDP_UUID128,
+ &uuid.value.uuid128);
+ break;
+ }
+ sdp_pattern_add_uuid(rec, &uuid);
+}
+
+/*
+ * set the GroupID attribute of the service record defining a group.
+ *
+ * This is the UUID of the group.
+ *
+ * returns 0 if successful or -1 if there is a failure.
+ */
+void sdp_set_group_id(sdp_record_t *rec, uuid_t uuid)
+{
+ switch (uuid.type) {
+ case SDP_UUID16:
+ sdp_attr_add_new(rec, SDP_ATTR_GROUP_ID, SDP_UUID16,
+ &uuid.value.uuid16);
+ break;
+ case SDP_UUID32:
+ sdp_attr_add_new(rec, SDP_ATTR_GROUP_ID, SDP_UUID32,
+ &uuid.value.uuid32);
+ break;
+ case SDP_UUID128:
+ sdp_attr_add_new(rec, SDP_ATTR_GROUP_ID, SDP_UUID128,
+ &uuid.value.uuid128);
+ break;
+ }
+ sdp_pattern_add_uuid(rec, &uuid);
+}
+
+/*
+ * set the ProfileDescriptorList attribute of the service record
+ * pointed to by record to the value specified in "profileDesc".
+ *
+ * Each element in the list is an object of type
+ * sdp_profile_desc_t which is a definition of the
+ * Bluetooth profile that this service conforms to.
+ *
+ * Using a value of NULL for profileDesc has
+ * effect of removing this attribute (if previously set)
+ *
+ * This function replaces the exisiting ProfileDescriptorList
+ * structure (if any) with the new one specified.
+ *
+ * returns 0 if successful or -1 if there is a failure.
+ */
+int sdp_set_profile_descs(sdp_record_t *rec, const sdp_list_t *profiles)
+{
+ int status = 0;
+ uint8_t uuid16 = SDP_UUID16;
+ uint8_t uuid32 = SDP_UUID32;
+ uint8_t uuid128 = SDP_UUID128;
+ uint8_t uint16 = SDP_UINT16;
+ int i = 0, seqlen = sdp_list_len(profiles);
+ void **seqDTDs, **seqs;
+ const sdp_list_t *p;
+
+ seqDTDs = malloc(seqlen * sizeof(void *));
+ if (!seqDTDs)
+ return -1;
+
+ seqs = malloc(seqlen * sizeof(void *));
+ if (!seqs) {
+ free(seqDTDs);
+ return -1;
+ }
+
+ for (p = profiles; p; p = p->next) {
+ sdp_data_t *seq;
+ void *dtds[2], *values[2];
+ sdp_profile_desc_t *profile = p->data;
+ if (!profile) {
+ status = -1;
+ break;
+ }
+ switch (profile->uuid.type) {
+ case SDP_UUID16:
+ dtds[0] = &uuid16;
+ values[0] = &profile->uuid.value.uuid16;
+ break;
+ case SDP_UUID32:
+ dtds[0] = &uuid32;
+ values[0] = &profile->uuid.value.uuid32;
+ break;
+ case SDP_UUID128:
+ dtds[0] = &uuid128;
+ values[0] = &profile->uuid.value.uuid128;
+ break;
+ default:
+ status = -1;
+ break;
+ }
+ dtds[1] = &uint16;
+ values[1] = &profile->version;
+ seq = sdp_seq_alloc(dtds, values, 2);
+ if (seq) {
+ seqDTDs[i] = &seq->dtd;
+ seqs[i] = seq;
+ sdp_pattern_add_uuid(rec, &profile->uuid);
+ }
+ i++;
+ }
+ if (status == 0) {
+ sdp_data_t *pAPSeq = sdp_seq_alloc(seqDTDs, seqs, seqlen);
+ sdp_attr_add(rec, SDP_ATTR_PFILE_DESC_LIST, pAPSeq);
+ }
+ free(seqDTDs);
+ free(seqs);
+ return status;
+}
+
+/*
+ * sets various URL attributes of the service
+ * pointed to by record. The URL include
+ *
+ * client: a URL to the client's
+ * platform specific (WinCE, PalmOS) executable
+ * code that can be used to access this service.
+ *
+ * doc: a URL pointing to service documentation
+ *
+ * icon: a URL to an icon that can be used to represent
+ * this service.
+ *
+ * Note that you need to pass NULL for any URLs
+ * that you don't want to set or remove
+ */
+void sdp_set_url_attr(sdp_record_t *rec, const char *client, const char *doc,
+ const char *icon)
+{
+ sdp_attr_add_new(rec, SDP_ATTR_CLNT_EXEC_URL, SDP_URL_STR8, client);
+ sdp_attr_add_new(rec, SDP_ATTR_DOC_URL, SDP_URL_STR8, doc);
+ sdp_attr_add_new(rec, SDP_ATTR_ICON_URL, SDP_URL_STR8, icon);
+}
+
+uuid_t *sdp_uuid16_create(uuid_t *u, uint16_t val)
+{
+ memset(u, 0, sizeof(uuid_t));
+ u->type = SDP_UUID16;
+ u->value.uuid16 = val;
+ return u;
+}
+
+uuid_t *sdp_uuid32_create(uuid_t *u, uint32_t val)
+{
+ memset(u, 0, sizeof(uuid_t));
+ u->type = SDP_UUID32;
+ u->value.uuid32 = val;
+ return u;
+}
+
+uuid_t *sdp_uuid128_create(uuid_t *u, const void *val)
+{
+ memset(u, 0, sizeof(uuid_t));
+ u->type = SDP_UUID128;
+ memcpy(&u->value.uuid128, val, sizeof(uint128_t));
+ return u;
+}
+
+/*
+ * UUID comparison function
+ * returns 0 if uuidValue1 == uuidValue2 else -1
+ */
+int sdp_uuid_cmp(const void *p1, const void *p2)
+{
+ uuid_t *u1 = sdp_uuid_to_uuid128(p1);
+ uuid_t *u2 = sdp_uuid_to_uuid128(p2);
+ int ret;
+
+ ret = sdp_uuid128_cmp(u1, u2);
+
+ bt_free(u1);
+ bt_free(u2);
+
+ return ret;
+}
+
+/*
+ * UUID comparison function
+ * returns 0 if uuidValue1 == uuidValue2 else -1
+ */
+int sdp_uuid16_cmp(const void *p1, const void *p2)
+{
+ const uuid_t *u1 = p1;
+ const uuid_t *u2 = p2;
+ return memcmp(&u1->value.uuid16, &u2->value.uuid16, sizeof(uint16_t));
+}
+
+/*
+ * UUID comparison function
+ * returns 0 if uuidValue1 == uuidValue2 else -1
+ */
+int sdp_uuid128_cmp(const void *p1, const void *p2)
+{
+ const uuid_t *u1 = p1;
+ const uuid_t *u2 = p2;
+ return memcmp(&u1->value.uuid128, &u2->value.uuid128, sizeof(uint128_t));
+}
+
+/*
+ * 128 to 16 bit and 32 to 16 bit UUID conversion functions
+ * yet to be implemented. Note that the input is in NBO in
+ * both 32 and 128 bit UUIDs and conversion is needed
+ */
+void sdp_uuid16_to_uuid128(uuid_t *uuid128, const uuid_t *uuid16)
+{
+ /*
+ * We have a 16 bit value, which needs to be added to
+ * bytes 3 and 4 (at indices 2 and 3) of the Bluetooth base
+ */
+ unsigned short data1;
+
+ /* allocate a 128bit UUID and init to the Bluetooth base UUID */
+ uuid128->value.uuid128 = bluetooth_base_uuid;
+ uuid128->type = SDP_UUID128;
+
+ /* extract bytes 2 and 3 of 128bit BT base UUID */
+ memcpy(&data1, &bluetooth_base_uuid.data[2], 2);
+
+ /* add the given UUID (16 bits) */
+ data1 += htons(uuid16->value.uuid16);
+
+ /* set bytes 2 and 3 of the 128 bit value */
+ memcpy(&uuid128->value.uuid128.data[2], &data1, 2);
+}
+
+void sdp_uuid32_to_uuid128(uuid_t *uuid128, const uuid_t *uuid32)
+{
+ /*
+ * We have a 32 bit value, which needs to be added to
+ * bytes 1->4 (at indices 0 thru 3) of the Bluetooth base
+ */
+ unsigned int data0;
+
+ /* allocate a 128bit UUID and init to the Bluetooth base UUID */
+ uuid128->value.uuid128 = bluetooth_base_uuid;
+ uuid128->type = SDP_UUID128;
+
+ /* extract first 4 bytes */
+ memcpy(&data0, &bluetooth_base_uuid.data[0], 4);
+
+ /* add the given UUID (32bits) */
+ data0 += htonl(uuid32->value.uuid32);
+
+ /* set the 4 bytes of the 128 bit value */
+ memcpy(&uuid128->value.uuid128.data[0], &data0, 4);
+}
+
+uuid_t *sdp_uuid_to_uuid128(const uuid_t *uuid)
+{
+ uuid_t *uuid128 = bt_malloc(sizeof(uuid_t));
+
+ if (!uuid128)
+ return NULL;
+
+ memset(uuid128, 0, sizeof(uuid_t));
+ switch (uuid->type) {
+ case SDP_UUID128:
+ *uuid128 = *uuid;
+ break;
+ case SDP_UUID32:
+ sdp_uuid32_to_uuid128(uuid128, uuid);
+ break;
+ case SDP_UUID16:
+ sdp_uuid16_to_uuid128(uuid128, uuid);
+ break;
+ }
+ return uuid128;
+}
+
+/*
+ * converts a 128-bit uuid to a 16/32-bit one if possible
+ * returns true if uuid contains a 16/32-bit UUID at exit
+ */
+int sdp_uuid128_to_uuid(uuid_t *uuid)
+{
+ uint128_t *b = &bluetooth_base_uuid;
+ uint128_t *u = &uuid->value.uuid128;
+ uint32_t data;
+ unsigned int i;
+
+ if (uuid->type != SDP_UUID128)
+ return 1;
+
+ for (i = 4; i < sizeof(b->data); i++)
+ if (b->data[i] != u->data[i])
+ return 0;
+
+ memcpy(&data, u->data, 4);
+ data = htonl(data);
+ if (data <= 0xffff) {
+ uuid->type = SDP_UUID16;
+ uuid->value.uuid16 = (uint16_t) data;
+ } else {
+ uuid->type = SDP_UUID32;
+ uuid->value.uuid32 = data;
+ }
+ return 1;
+}
+
+/*
+ * convert a UUID to the 16-bit short-form
+ */
+int sdp_uuid_to_proto(uuid_t *uuid)
+{
+ uuid_t u = *uuid;
+ if (sdp_uuid128_to_uuid(&u)) {
+ switch (u.type) {
+ case SDP_UUID16:
+ return u.value.uuid16;
+ case SDP_UUID32:
+ return u.value.uuid32;
+ }
+ }
+ return 0;
+}
+
+/*
+ * This function appends data to the PDU buffer "dst" from source "src".
+ * The data length is also computed and set.
+ * Should the PDU length exceed 2^8, then sequence type is
+ * set accordingly and the data is memmove()'d.
+ */
+void sdp_append_to_buf(sdp_buf_t *dst, uint8_t *data, uint32_t len)
+{
+ uint8_t *p = dst->data;
+ uint8_t dtd = *p;
+
+ SDPDBG("Append src size: %d\n", len);
+ SDPDBG("Append dst size: %d\n", dst->data_size);
+ SDPDBG("Dst buffer size: %d\n", dst->buf_size);
+ if (dst->data_size == 0 && dtd == 0) {
+ /* create initial sequence */
+ *p = SDP_SEQ8;
+ p += sizeof(uint8_t);
+ dst->data_size += sizeof(uint8_t);
+ /* reserve space for sequence size */
+ p += sizeof(uint8_t);
+ dst->data_size += sizeof(uint8_t);
+ }
+
+ memcpy(dst->data + dst->data_size, data, len);
+ dst->data_size += len;
+
+ dtd = *(uint8_t *) dst->data;
+ if (dst->data_size > UCHAR_MAX && dtd == SDP_SEQ8) {
+ short offset = sizeof(uint8_t) + sizeof(uint8_t);
+ memmove(dst->data + offset + 1, dst->data + offset,
+ dst->data_size - offset);
+ p = dst->data;
+ *p = SDP_SEQ16;
+ p += sizeof(uint8_t);
+ dst->data_size += 1;
+ }
+ p = dst->data;
+ dtd = *(uint8_t *) p;
+ p += sizeof(uint8_t);
+ switch (dtd) {
+ case SDP_SEQ8:
+ *(uint8_t *) p = dst->data_size - sizeof(uint8_t) - sizeof(uint8_t);
+ break;
+ case SDP_SEQ16:
+ bt_put_unaligned(htons(dst->data_size - sizeof(uint8_t) - sizeof(uint16_t)), (uint16_t *) p);
+ break;
+ case SDP_SEQ32:
+ bt_put_unaligned(htonl(dst->data_size - sizeof(uint8_t) - sizeof(uint32_t)), (uint32_t *) p);
+ break;
+ }
+}
+
+void sdp_append_to_pdu(sdp_buf_t *pdu, sdp_data_t *d)
+{
+ sdp_buf_t append;
+
+ memset(&append, 0, sizeof(sdp_buf_t));
+ sdp_gen_buffer(&append, d);
+ append.data = malloc(append.buf_size);
+ if (!append.data)
+ return;
+
+ sdp_set_attrid(&append, d->attrId);
+ sdp_gen_pdu(&append, d);
+ sdp_append_to_buf(pdu, append.data, append.data_size);
+ free(append.data);
+}
+
+/*
+ * Registers an sdp record.
+ *
+ * It is incorrect to call this method on a record that
+ * has been already registered with the server.
+ *
+ * Returns zero on success, otherwise -1 (and sets errno).
+ */
+int sdp_device_record_register_binary(sdp_session_t *session, bdaddr_t *device, uint8_t *data, uint32_t size, uint8_t flags, uint32_t *handle)
+{
+ uint8_t *req, *rsp, *p;
+ uint32_t reqsize, rspsize;
+ sdp_pdu_hdr_t *reqhdr, *rsphdr;
+ int status;
+
+ SDPDBG("");
+
+ if (!session->local) {
+ errno = EREMOTE;
+ return -1;
+ }
+ req = malloc(SDP_REQ_BUFFER_SIZE);
+ rsp = malloc(SDP_RSP_BUFFER_SIZE);
+ if (req == NULL || rsp == NULL) {
+ status = -1;
+ errno = ENOMEM;
+ goto end;
+ }
+
+ reqhdr = (sdp_pdu_hdr_t *)req;
+ reqhdr->pdu_id = SDP_SVC_REGISTER_REQ;
+ reqhdr->tid = htons(sdp_gen_tid(session));
+ reqsize = sizeof(sdp_pdu_hdr_t) + 1;
+ p = req + sizeof(sdp_pdu_hdr_t);
+
+ if (bacmp(device, BDADDR_ANY)) {
+ *p++ = flags | SDP_DEVICE_RECORD;
+ bacpy((bdaddr_t *) p, device);
+ p += sizeof(bdaddr_t);
+ reqsize += sizeof(bdaddr_t);
+ } else
+ *p++ = flags;
+
+ memcpy(p, data, size);
+ reqsize += size;
+ reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t));
+
+ status = sdp_send_req_w4_rsp(session, req, rsp, reqsize, &rspsize);
+ if (status < 0)
+ goto end;
+
+ if (rspsize < sizeof(sdp_pdu_hdr_t)) {
+ SDPERR("Unexpected end of packet");
+ errno = EPROTO;
+ status = -1;
+ goto end;
+ }
+
+ rsphdr = (sdp_pdu_hdr_t *) rsp;
+ p = rsp + sizeof(sdp_pdu_hdr_t);
+
+ if (rsphdr->pdu_id == SDP_ERROR_RSP) {
+ /* Invalid service record */
+ errno = EINVAL;
+ status = -1;
+ } else if (rsphdr->pdu_id != SDP_SVC_REGISTER_RSP) {
+ errno = EPROTO;
+ status = -1;
+ } else {
+ if (rspsize < sizeof(sdp_pdu_hdr_t) + sizeof(uint32_t)) {
+ SDPERR("Unexpected end of packet");
+ errno = EPROTO;
+ status = -1;
+ goto end;
+ }
+ if (handle)
+ *handle = ntohl(bt_get_unaligned((uint32_t *) p));
+ }
+
+end:
+ free(req);
+ free(rsp);
+
+ return status;
+}
+
+int sdp_device_record_register(sdp_session_t *session, bdaddr_t *device, sdp_record_t *rec, uint8_t flags)
+{
+ sdp_buf_t pdu;
+ uint32_t handle;
+ int err;
+
+ SDPDBG("");
+
+ if (rec->handle && rec->handle != 0xffffffff) {
+ uint32_t handle = rec->handle;
+ sdp_data_t *data = sdp_data_alloc(SDP_UINT32, &handle);
+ sdp_attr_replace(rec, SDP_ATTR_RECORD_HANDLE, data);
+ }
+
+ if (sdp_gen_record_pdu(rec, &pdu) < 0) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ err = sdp_device_record_register_binary(session, device,
+ pdu.data, pdu.data_size, flags, &handle);
+
+ free(pdu.data);
+
+ if (err == 0) {
+ sdp_data_t *data = sdp_data_alloc(SDP_UINT32, &handle);
+ rec->handle = handle;
+ sdp_attr_replace(rec, SDP_ATTR_RECORD_HANDLE, data);
+ }
+
+ return err;
+}
+
+int sdp_record_register(sdp_session_t *session, sdp_record_t *rec, uint8_t flags)
+{
+ return sdp_device_record_register(session, BDADDR_ANY, rec, flags);
+}
+
+/*
+ * unregister a service record
+ */
+int sdp_device_record_unregister_binary(sdp_session_t *session, bdaddr_t *device, uint32_t handle)
+{
+ uint8_t *reqbuf, *rspbuf, *p;
+ uint32_t reqsize = 0, rspsize = 0;
+ sdp_pdu_hdr_t *reqhdr, *rsphdr;
+ int status;
+
+ SDPDBG("");
+
+ if (handle == SDP_SERVER_RECORD_HANDLE) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!session->local) {
+ errno = EREMOTE;
+ return -1;
+ }
+
+ reqbuf = malloc(SDP_REQ_BUFFER_SIZE);
+ rspbuf = malloc(SDP_RSP_BUFFER_SIZE);
+ if (!reqbuf || !rspbuf) {
+ errno = ENOMEM;
+ status = -1;
+ goto end;
+ }
+ reqhdr = (sdp_pdu_hdr_t *) reqbuf;
+ reqhdr->pdu_id = SDP_SVC_REMOVE_REQ;
+ reqhdr->tid = htons(sdp_gen_tid(session));
+
+ p = reqbuf + sizeof(sdp_pdu_hdr_t);
+ reqsize = sizeof(sdp_pdu_hdr_t);
+ bt_put_unaligned(htonl(handle), (uint32_t *) p);
+ reqsize += sizeof(uint32_t);
+
+ reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t));
+ status = sdp_send_req_w4_rsp(session, reqbuf, rspbuf, reqsize, &rspsize);
+ if (status < 0)
+ goto end;
+
+ if (rspsize < sizeof(sdp_pdu_hdr_t) + sizeof(uint16_t)) {
+ SDPERR("Unexpected end of packet");
+ errno = EPROTO;
+ status = -1;
+ goto end;
+ }
+
+ rsphdr = (sdp_pdu_hdr_t *) rspbuf;
+ p = rspbuf + sizeof(sdp_pdu_hdr_t);
+ status = bt_get_unaligned((uint16_t *) p);
+
+ if (rsphdr->pdu_id == SDP_ERROR_RSP) {
+ /* For this case the status always is invalid record handle */
+ errno = EINVAL;
+ status = -1;
+ } else if (rsphdr->pdu_id != SDP_SVC_REMOVE_RSP) {
+ errno = EPROTO;
+ status = -1;
+ }
+end:
+ free(reqbuf);
+ free(rspbuf);
+
+ return status;
+}
+
+int sdp_device_record_unregister(sdp_session_t *session, bdaddr_t *device, sdp_record_t *rec)
+{
+ int err;
+
+ err = sdp_device_record_unregister_binary(session, device, rec->handle);
+ if (err == 0)
+ sdp_record_free(rec);
+
+ return err;
+}
+
+int sdp_record_unregister(sdp_session_t *session, sdp_record_t *rec)
+{
+ return sdp_device_record_unregister(session, BDADDR_ANY, rec);
+}
+
+/*
+ * modify an existing service record
+ */
+int sdp_device_record_update_binary(sdp_session_t *session, bdaddr_t *device, uint32_t handle, uint8_t *data, uint32_t size)
+{
+ return -1;
+}
+
+int sdp_device_record_update(sdp_session_t *session, bdaddr_t *device, const sdp_record_t *rec)
+{
+ uint8_t *reqbuf, *rspbuf, *p;
+ uint32_t reqsize, rspsize;
+ sdp_pdu_hdr_t *reqhdr, *rsphdr;
+ uint32_t handle;
+ sdp_buf_t pdu;
+ int status;
+
+ SDPDBG("");
+
+ handle = rec->handle;
+
+ if (handle == SDP_SERVER_RECORD_HANDLE) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (!session->local) {
+ errno = EREMOTE;
+ return -1;
+ }
+ reqbuf = malloc(SDP_REQ_BUFFER_SIZE);
+ rspbuf = malloc(SDP_RSP_BUFFER_SIZE);
+ if (!reqbuf || !rspbuf) {
+ errno = ENOMEM;
+ status = -1;
+ goto end;
+ }
+ reqhdr = (sdp_pdu_hdr_t *) reqbuf;
+ reqhdr->pdu_id = SDP_SVC_UPDATE_REQ;
+ reqhdr->tid = htons(sdp_gen_tid(session));
+
+ p = reqbuf + sizeof(sdp_pdu_hdr_t);
+ reqsize = sizeof(sdp_pdu_hdr_t);
+
+ bt_put_unaligned(htonl(handle), (uint32_t *) p);
+ reqsize += sizeof(uint32_t);
+ p += sizeof(uint32_t);
+
+ if (sdp_gen_record_pdu(rec, &pdu) < 0) {
+ errno = ENOMEM;
+ status = -1;
+ goto end;
+ }
+ memcpy(p, pdu.data, pdu.data_size);
+ reqsize += pdu.data_size;
+ free(pdu.data);
+
+ reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t));
+ status = sdp_send_req_w4_rsp(session, reqbuf, rspbuf, reqsize, &rspsize);
+ if (status < 0)
+ goto end;
+
+ if (rspsize < sizeof(sdp_pdu_hdr_t) + sizeof(uint16_t)) {
+ SDPERR("Unexpected end of packet");
+ errno = EPROTO;
+ status = -1;
+ goto end;
+ }
+
+ SDPDBG("Send req status : %d\n", status);
+
+ rsphdr = (sdp_pdu_hdr_t *) rspbuf;
+ p = rspbuf + sizeof(sdp_pdu_hdr_t);
+ status = bt_get_unaligned((uint16_t *) p);
+
+ if (rsphdr->pdu_id == SDP_ERROR_RSP) {
+ /* The status can be invalid sintax or invalid record handle */
+ errno = EINVAL;
+ status = -1;
+ } else if (rsphdr->pdu_id != SDP_SVC_UPDATE_RSP) {
+ errno = EPROTO;
+ status = -1;
+ }
+end:
+ free(reqbuf);
+ free(rspbuf);
+ return status;
+}
+
+int sdp_record_update(sdp_session_t *session, const sdp_record_t *rec)
+{
+ return sdp_device_record_update(session, BDADDR_ANY, rec);
+}
+
+sdp_record_t *sdp_record_alloc()
+{
+ sdp_record_t *rec = malloc(sizeof(sdp_record_t));
+
+ if (!rec)
+ return NULL;
+
+ memset(rec, 0, sizeof(sdp_record_t));
+ rec->handle = 0xffffffff;
+ return rec;
+}
+
+/*
+ * Free the contents of a service record
+ */
+void sdp_record_free(sdp_record_t *rec)
+{
+ sdp_list_free(rec->attrlist, (sdp_free_func_t) sdp_data_free);
+ sdp_list_free(rec->pattern, free);
+ free(rec);
+}
+
+void sdp_pattern_add_uuid(sdp_record_t *rec, uuid_t *uuid)
+{
+ uuid_t *uuid128 = sdp_uuid_to_uuid128(uuid);
+
+ SDPDBG("Elements in target pattern : %d\n", sdp_list_len(rec->pattern));
+ SDPDBG("Trying to add : 0x%lx\n", (unsigned long) uuid128);
+
+ if (sdp_list_find(rec->pattern, uuid128, sdp_uuid128_cmp) == NULL)
+ rec->pattern = sdp_list_insert_sorted(rec->pattern, uuid128, sdp_uuid128_cmp);
+ else
+ bt_free(uuid128);
+
+ SDPDBG("Elements in target pattern : %d\n", sdp_list_len(rec->pattern));
+}
+
+void sdp_pattern_add_uuidseq(sdp_record_t *rec, sdp_list_t *seq)
+{
+ for (; seq; seq = seq->next) {
+ uuid_t *uuid = (uuid_t *)seq->data;
+ sdp_pattern_add_uuid(rec, uuid);
+ }
+}
+
+/*
+ * Extract a sequence of service record handles from a PDU buffer
+ * and add the entries to a sdp_list_t. Note that the service record
+ * handles are not in "data element sequence" form, but just like
+ * an array of service handles
+ */
+static void extract_record_handle_seq(uint8_t *pdu, int bufsize, sdp_list_t **seq, int count, unsigned int *scanned)
+{
+ sdp_list_t *pSeq = *seq;
+ uint8_t *pdata = pdu;
+ int n;
+
+ for (n = 0; n < count; n++) {
+ uint32_t *pSvcRec;
+ if (bufsize < (int) sizeof(uint32_t)) {
+ SDPERR("Unexpected end of packet");
+ break;
+ }
+ pSvcRec = malloc(sizeof(uint32_t));
+ if (!pSvcRec)
+ break;
+ *pSvcRec = ntohl(bt_get_unaligned((uint32_t *) pdata));
+ pSeq = sdp_list_append(pSeq, pSvcRec);
+ pdata += sizeof(uint32_t);
+ *scanned += sizeof(uint32_t);
+ bufsize -= sizeof(uint32_t);
+ }
+ *seq = pSeq;
+}
+/*
+ * Generate the attribute sequence pdu form
+ * from sdp_list_t elements. Return length of attr seq
+ */
+static int gen_dataseq_pdu(uint8_t *dst, const sdp_list_t *seq, uint8_t dtd)
+{
+ sdp_data_t *dataseq;
+ void **types, **values;
+ sdp_buf_t buf;
+ int i, seqlen = sdp_list_len(seq);
+
+ // Fill up the value and the dtd arrays
+ SDPDBG("");
+
+ SDPDBG("Seq length : %d\n", seqlen);
+
+ types = malloc(seqlen * sizeof(void *));
+ if (!types)
+ return -ENOMEM;
+
+ values = malloc(seqlen * sizeof(void *));
+ if (!values) {
+ free(types);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < seqlen; i++) {
+ void *data = seq->data;
+ types[i] = &dtd;
+ if (SDP_IS_UUID(dtd))
+ data = &((uuid_t *)data)->value;
+ values[i] = data;
+ seq = seq->next;
+ }
+
+ dataseq = sdp_seq_alloc(types, values, seqlen);
+ if (!dataseq) {
+ free(types);
+ free(values);
+ return -ENOMEM;
+ }
+
+ memset(&buf, 0, sizeof(sdp_buf_t));
+ sdp_gen_buffer(&buf, dataseq);
+ buf.data = malloc(buf.buf_size);
+
+ if (!buf.data) {
+ sdp_data_free(dataseq);
+ free(types);
+ free(values);
+ return -ENOMEM;
+ }
+
+ SDPDBG("Data Seq : 0x%p\n", seq);
+ seqlen = sdp_gen_pdu(&buf, dataseq);
+ SDPDBG("Copying : %d\n", buf.data_size);
+ memcpy(dst, buf.data, buf.data_size);
+
+ sdp_data_free(dataseq);
+
+ free(types);
+ free(values);
+ free(buf.data);
+ return seqlen;
+}
+
+static int gen_searchseq_pdu(uint8_t *dst, const sdp_list_t *seq)
+{
+ uuid_t *uuid = seq->data;
+ return gen_dataseq_pdu(dst, seq, uuid->type);
+}
+
+static int gen_attridseq_pdu(uint8_t *dst, const sdp_list_t *seq, uint8_t dataType)
+{
+ return gen_dataseq_pdu(dst, seq, dataType);
+}
+
+typedef struct {
+ uint8_t length;
+ unsigned char data[16];
+} __attribute__ ((packed)) sdp_cstate_t;
+
+static int copy_cstate(uint8_t *pdata, int pdata_len, const sdp_cstate_t *cstate)
+{
+ if (cstate) {
+ uint8_t len = cstate->length;
+ if (len >= pdata_len) {
+ SDPERR("Continuation state size exceeds internal buffer");
+ len = pdata_len - 1;
+ }
+ *pdata++ = len;
+ memcpy(pdata, cstate->data, len);
+ return len + 1;
+ }
+ *pdata = 0;
+ return 1;
+}
+
+/*
+ * This is a service search request.
+ *
+ * INPUT :
+ *
+ * sdp_list_t *search
+ * Singly linked list containing elements of the search
+ * pattern. Each entry in the list is a UUID (DataTypeSDP_UUID16)
+ * of the service to be searched
+ *
+ * uint16_t max_rec_num
+ * A 16 bit integer which tells the service, the maximum
+ * entries that the client can handle in the response. The
+ * server is obliged not to return > max_rec_num entries
+ *
+ * OUTPUT :
+ *
+ * int return value
+ * 0:
+ * The request completed successfully. This does not
+ * mean the requested services were found
+ * -1:
+ * On any failure and sets errno
+ *
+ * sdp_list_t **rsp_list
+ * This variable is set on a successful return if there are
+ * non-zero service handles. It is a singly linked list of
+ * service record handles (uint16_t)
+ */
+int sdp_service_search_req(sdp_session_t *session, const sdp_list_t *search,
+ uint16_t max_rec_num, sdp_list_t **rsp)
+{
+ int status = 0;
+ uint32_t reqsize = 0, _reqsize;
+ uint32_t rspsize = 0, rsplen;
+ int seqlen = 0;
+ int total_rec_count, rec_count;
+ unsigned scanned, pdata_len;
+ uint8_t *pdata, *_pdata;
+ uint8_t *reqbuf, *rspbuf;
+ sdp_pdu_hdr_t *reqhdr, *rsphdr;
+ sdp_cstate_t *cstate = NULL;
+
+ reqbuf = malloc(SDP_REQ_BUFFER_SIZE);
+ rspbuf = malloc(SDP_RSP_BUFFER_SIZE);
+ if (!reqbuf || !rspbuf) {
+ errno = ENOMEM;
+ status = -1;
+ goto end;
+ }
+ reqhdr = (sdp_pdu_hdr_t *) reqbuf;
+ reqhdr->pdu_id = SDP_SVC_SEARCH_REQ;
+ pdata = reqbuf + sizeof(sdp_pdu_hdr_t);
+ reqsize = sizeof(sdp_pdu_hdr_t);
+
+ // add service class IDs for search
+ seqlen = gen_searchseq_pdu(pdata, search);
+
+ SDPDBG("Data seq added : %d\n", seqlen);
+
+ // set the length and increment the pointer
+ reqsize += seqlen;
+ pdata += seqlen;
+
+ // specify the maximum svc rec count that client expects
+ bt_put_unaligned(htons(max_rec_num), (uint16_t *) pdata);
+ reqsize += sizeof(uint16_t);
+ pdata += sizeof(uint16_t);
+
+ _reqsize = reqsize;
+ _pdata = pdata;
+ *rsp = NULL;
+
+ do {
+ // Add continuation state or NULL (first time)
+ reqsize = _reqsize + copy_cstate(_pdata,
+ SDP_REQ_BUFFER_SIZE - _reqsize, cstate);
+
+ // Set the request header's param length
+ reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t));
+
+ reqhdr->tid = htons(sdp_gen_tid(session));
+ /*
+ * Send the request, wait for response and if
+ * no error, set the appropriate values and return
+ */
+ status = sdp_send_req_w4_rsp(session, reqbuf, rspbuf, reqsize, &rspsize);
+ if (status < 0)
+ goto end;
+
+ if (rspsize < sizeof(sdp_pdu_hdr_t)) {
+ SDPERR("Unexpected end of packet");
+ status = -1;
+ goto end;
+ }
+
+ rsphdr = (sdp_pdu_hdr_t *) rspbuf;
+ rsplen = ntohs(rsphdr->plen);
+
+ if (rsphdr->pdu_id == SDP_ERROR_RSP) {
+ SDPDBG("Status : 0x%x\n", rsphdr->pdu_id);
+ status = -1;
+ goto end;
+ }
+ scanned = 0;
+ pdata = rspbuf + sizeof(sdp_pdu_hdr_t);
+ pdata_len = rspsize - sizeof(sdp_pdu_hdr_t);
+
+ if (pdata_len < sizeof(uint16_t) + sizeof(uint16_t)) {
+ SDPERR("Unexpected end of packet");
+ status = -1;
+ goto end;
+ }
+
+ // net service record match count
+ total_rec_count = ntohs(bt_get_unaligned((uint16_t *) pdata));
+ pdata += sizeof(uint16_t);
+ scanned += sizeof(uint16_t);
+ pdata_len -= sizeof(uint16_t);
+ rec_count = ntohs(bt_get_unaligned((uint16_t *) pdata));
+ pdata += sizeof(uint16_t);
+ scanned += sizeof(uint16_t);
+ pdata_len -= sizeof(uint16_t);
+
+ SDPDBG("Total svc count: %d\n", total_rec_count);
+ SDPDBG("Current svc count: %d\n", rec_count);
+ SDPDBG("ResponseLength: %d\n", rsplen);
+
+ if (!rec_count) {
+ status = -1;
+ goto end;
+ }
+ extract_record_handle_seq(pdata, pdata_len, rsp, rec_count, &scanned);
+ SDPDBG("BytesScanned : %d\n", scanned);
+
+ if (rsplen > scanned) {
+ uint8_t cstate_len;
+
+ if (rspsize < sizeof(sdp_pdu_hdr_t) + scanned + sizeof(uint8_t)) {
+ SDPERR("Unexpected end of packet: continuation state data missing");
+ status = -1;
+ goto end;
+ }
+
+ pdata = rspbuf + sizeof(sdp_pdu_hdr_t) + scanned;
+ cstate_len = *(uint8_t *) pdata;
+ if (cstate_len > 0) {
+ cstate = (sdp_cstate_t *)pdata;
+ SDPDBG("Cont state length: %d\n", cstate_len);
+ } else
+ cstate = NULL;
+ }
+ } while (cstate);
+
+end:
+ free(reqbuf);
+ free(rspbuf);
+
+ return status;
+}
+
+/*
+ * This is a service attribute request.
+ *
+ * INPUT :
+ *
+ * uint32_t handle
+ * The handle of the service for which the attribute(s) are
+ * requested
+ *
+ * sdp_attrreq_type_t reqtype
+ * Attribute identifiers are 16 bit unsigned integers specified
+ * in one of 2 ways described below :
+ * SDP_ATTR_REQ_INDIVIDUAL - 16bit individual identifiers
+ * They are the actual attribute identifiers in ascending order
+ *
+ * SDP_ATTR_REQ_RANGE - 32bit identifier range
+ * The high-order 16bits is the start of range
+ * the low-order 16bits are the end of range
+ * 0x0000 to 0xFFFF gets all attributes
+ *
+ * sdp_list_t *attrid
+ * Singly linked list containing attribute identifiers desired.
+ * Every element is either a uint16_t(attrSpec = SDP_ATTR_REQ_INDIVIDUAL)
+ * or a uint32_t(attrSpec=SDP_ATTR_REQ_RANGE)
+ *
+ * OUTPUT :
+ * return sdp_record_t *
+ * 0:
+ * On any error and sets errno
+ * !0:
+ * The service record
+ */
+sdp_record_t *sdp_service_attr_req(sdp_session_t *session, uint32_t handle,
+ sdp_attrreq_type_t reqtype, const sdp_list_t *attrids)
+{
+ uint32_t reqsize = 0, _reqsize;
+ uint32_t rspsize = 0, rsp_count;
+ int attr_list_len = 0;
+ int seqlen = 0;
+ unsigned int pdata_len;
+ uint8_t *pdata, *_pdata;
+ uint8_t *reqbuf, *rspbuf;
+ sdp_pdu_hdr_t *reqhdr, *rsphdr;
+ sdp_cstate_t *cstate = NULL;
+ uint8_t cstate_len = 0;
+ sdp_buf_t rsp_concat_buf;
+ sdp_record_t *rec = 0;
+
+ if (reqtype != SDP_ATTR_REQ_INDIVIDUAL && reqtype != SDP_ATTR_REQ_RANGE) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ memset(&rsp_concat_buf, 0, sizeof(sdp_buf_t));
+
+ reqbuf = malloc(SDP_REQ_BUFFER_SIZE);
+ rspbuf = malloc(SDP_RSP_BUFFER_SIZE);
+ if (!reqbuf || !rspbuf) {
+ errno = ENOMEM;
+ goto end;
+ }
+ reqhdr = (sdp_pdu_hdr_t *) reqbuf;
+ reqhdr->pdu_id = SDP_SVC_ATTR_REQ;
+
+ pdata = reqbuf + sizeof(sdp_pdu_hdr_t);
+ reqsize = sizeof(sdp_pdu_hdr_t);
+
+ // add the service record handle
+ bt_put_unaligned(htonl(handle), (uint32_t *) pdata);
+ reqsize += sizeof(uint32_t);
+ pdata += sizeof(uint32_t);
+
+ // specify the response limit
+ bt_put_unaligned(htons(65535), (uint16_t *) pdata);
+ reqsize += sizeof(uint16_t);
+ pdata += sizeof(uint16_t);
+
+ // get attr seq PDU form
+ seqlen = gen_attridseq_pdu(pdata, attrids,
+ reqtype == SDP_ATTR_REQ_INDIVIDUAL? SDP_UINT16 : SDP_UINT32);
+ if (seqlen == -1) {
+ errno = EINVAL;
+ goto end;
+ }
+ pdata += seqlen;
+ reqsize += seqlen;
+ SDPDBG("Attr list length : %d\n", seqlen);
+
+ // save before Continuation State
+ _pdata = pdata;
+ _reqsize = reqsize;
+
+ do {
+ int status;
+
+ // add NULL continuation state
+ reqsize = _reqsize + copy_cstate(_pdata,
+ SDP_REQ_BUFFER_SIZE - _reqsize, cstate);
+
+ // set the request header's param length
+ reqhdr->tid = htons(sdp_gen_tid(session));
+ reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t));
+
+ status = sdp_send_req_w4_rsp(session, reqbuf, rspbuf, reqsize, &rspsize);
+ if (status < 0)
+ goto end;
+
+ if (rspsize < sizeof(sdp_pdu_hdr_t)) {
+ SDPERR("Unexpected end of packet");
+ goto end;
+ }
+
+ rsphdr = (sdp_pdu_hdr_t *) rspbuf;
+ if (rsphdr->pdu_id == SDP_ERROR_RSP) {
+ SDPDBG("PDU ID : 0x%x\n", rsphdr->pdu_id);
+ goto end;
+ }
+ pdata = rspbuf + sizeof(sdp_pdu_hdr_t);
+ pdata_len = rspsize - sizeof(sdp_pdu_hdr_t);
+
+ if (pdata_len < sizeof(uint16_t)) {
+ SDPERR("Unexpected end of packet");
+ goto end;
+ }
+
+ rsp_count = ntohs(bt_get_unaligned((uint16_t *) pdata));
+ attr_list_len += rsp_count;
+ pdata += sizeof(uint16_t);
+ pdata_len -= sizeof(uint16_t);
+
+ // if continuation state set need to re-issue request before parsing
+ if (pdata_len < rsp_count + sizeof(uint8_t)) {
+ SDPERR("Unexpected end of packet: continuation state data missing");
+ goto end;
+ }
+ cstate_len = *(uint8_t *) (pdata + rsp_count);
+
+ SDPDBG("Response id : %d\n", rsphdr->pdu_id);
+ SDPDBG("Attrlist byte count : %d\n", rsp_count);
+ SDPDBG("sdp_cstate_t length : %d\n", cstate_len);
+
+ /*
+ * a split response: concatenate intermediate responses
+ * and the last one (which has cstate_len == 0)
+ */
+ if (cstate_len > 0 || rsp_concat_buf.data_size != 0) {
+ uint8_t *targetPtr = NULL;
+
+ cstate = cstate_len > 0 ? (sdp_cstate_t *) (pdata + rsp_count) : 0;
+
+ // build concatenated response buffer
+ rsp_concat_buf.data = realloc(rsp_concat_buf.data, rsp_concat_buf.data_size + rsp_count);
+ rsp_concat_buf.buf_size = rsp_concat_buf.data_size + rsp_count;
+ targetPtr = rsp_concat_buf.data + rsp_concat_buf.data_size;
+ memcpy(targetPtr, pdata, rsp_count);
+ rsp_concat_buf.data_size += rsp_count;
+ }
+ } while (cstate);
+
+ if (attr_list_len > 0) {
+ int scanned = 0;
+ if (rsp_concat_buf.data_size != 0) {
+ pdata = rsp_concat_buf.data;
+ pdata_len = rsp_concat_buf.data_size;
+ }
+ rec = sdp_extract_pdu(pdata, pdata_len, &scanned);
+ }
+
+end:
+ free(reqbuf);
+ free(rsp_concat_buf.data);
+ free(rspbuf);
+ return rec;
+}
+
+/*
+ * SDP transaction structure for asynchronous search
+ */
+struct sdp_transaction {
+ sdp_callback_t *cb; /* called when the transaction finishes */
+ void *udata; /* client user data */
+ uint8_t *reqbuf; /* pointer to request PDU */
+ sdp_buf_t rsp_concat_buf;
+ uint32_t reqsize; /* without cstate */
+ int err; /* ZERO if success or the errno if failed */
+};
+
+/*
+ * Creates a new sdp session for asynchronous search
+ * INPUT:
+ * int sk
+ * non-blocking L2CAP socket
+ *
+ * RETURN:
+ * sdp_session_t *
+ * NULL - On memory allocation failure
+ */
+sdp_session_t *sdp_create(int sk, uint32_t flags)
+{
+ sdp_session_t *session;
+ struct sdp_transaction *t;
+
+ session = malloc(sizeof(sdp_session_t));
+ if (!session) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ memset(session, 0, sizeof(*session));
+
+ session->flags = flags;
+ session->sock = sk;
+
+ t = malloc(sizeof(struct sdp_transaction));
+ if (!t) {
+ errno = ENOMEM;
+ free(session);
+ return NULL;
+ }
+ memset(t, 0, sizeof(*t));
+
+ session->priv = t;
+
+ return session;
+}
+
+/*
+ * Sets the callback function/user data used to notify the application
+ * that the asynchronous transaction finished. This function must be
+ * called before request an asynchronous search.
+ *
+ * INPUT:
+ * sdp_session_t *session
+ * Current sdp session to be handled
+ * sdp_callback_t *cb
+ * callback to be called when the transaction finishes
+ * void *udata
+ * user data passed to callback
+ * RETURN:
+ * 0 - Success
+ * -1 - Failure
+ */
+int sdp_set_notify(sdp_session_t *session, sdp_callback_t *func, void *udata)
+{
+ struct sdp_transaction *t;
+
+ if (!session || !session->priv)
+ return -1;
+
+ t = session->priv;
+ t->cb = func;
+ t->udata = udata;
+
+ return 0;
+}
+
+/*
+ * This function starts an asynchronous service search request.
+ * The incomming and outgoing data are stored in the transaction structure
+ * buffers. When there is incomming data the sdp_process function must be
+ * called to get the data and handle the continuation state.
+ *
+ * INPUT :
+ * sdp_session_t *session
+ * Current sdp session to be handled
+ *
+ * sdp_list_t *search
+ * Singly linked list containing elements of the search
+ * pattern. Each entry in the list is a UUID (DataTypeSDP_UUID16)
+ * of the service to be searched
+ *
+ * uint16_t max_rec_num
+ * A 16 bit integer which tells the service, the maximum
+ * entries that the client can handle in the response. The
+ * server is obliged not to return > max_rec_num entries
+ *
+ * OUTPUT :
+ *
+ * int return value
+ * 0 - if the request has been sent properly
+ * -1 - On any failure and sets errno
+ */
+
+int sdp_service_search_async(sdp_session_t *session, const sdp_list_t *search, uint16_t max_rec_num)
+{
+ struct sdp_transaction *t;
+ sdp_pdu_hdr_t *reqhdr;
+ uint8_t *pdata;
+ int cstate_len, seqlen = 0;
+
+ if (!session || !session->priv)
+ return -1;
+
+ t = session->priv;
+
+ /* clean possible allocated buffer */
+ free(t->rsp_concat_buf.data);
+ memset(&t->rsp_concat_buf, 0, sizeof(sdp_buf_t));
+
+ if (!t->reqbuf) {
+ t->reqbuf = malloc(SDP_REQ_BUFFER_SIZE);
+ if (!t->reqbuf) {
+ t->err = ENOMEM;
+ goto end;
+ }
+ }
+ memset(t->reqbuf, 0, SDP_REQ_BUFFER_SIZE);
+
+ reqhdr = (sdp_pdu_hdr_t *) t->reqbuf;
+ reqhdr->tid = htons(sdp_gen_tid(session));
+ reqhdr->pdu_id = SDP_SVC_SEARCH_REQ;
+
+ // generate PDU
+ pdata = t->reqbuf + sizeof(sdp_pdu_hdr_t);
+ t->reqsize = sizeof(sdp_pdu_hdr_t);
+
+ // add service class IDs for search
+ seqlen = gen_searchseq_pdu(pdata, search);
+
+ SDPDBG("Data seq added : %d\n", seqlen);
+
+ // now set the length and increment the pointer
+ t->reqsize += seqlen;
+ pdata += seqlen;
+
+ bt_put_unaligned(htons(max_rec_num), (uint16_t *) pdata);
+ t->reqsize += sizeof(uint16_t);
+ pdata += sizeof(uint16_t);
+
+ // set the request header's param length
+ cstate_len = copy_cstate(pdata, SDP_REQ_BUFFER_SIZE - t->reqsize, NULL);
+ reqhdr->plen = htons((t->reqsize + cstate_len) - sizeof(sdp_pdu_hdr_t));
+
+ if (sdp_send_req(session, t->reqbuf, t->reqsize + cstate_len) < 0) {
+ SDPERR("Error sendind data:%s", strerror(errno));
+ t->err = errno;
+ goto end;
+ }
+
+ return 0;
+end:
+
+ free(t->reqbuf);
+ t->reqbuf = NULL;
+
+ return -1;
+}
+
+/*
+ * This function starts an asynchronous service attribute request.
+ * The incomming and outgoing data are stored in the transaction structure
+ * buffers. When there is incomming data the sdp_process function must be
+ * called to get the data and handle the continuation state.
+ *
+ * INPUT :
+ * sdp_session_t *session
+ * Current sdp session to be handled
+ *
+ * uint32_t handle
+ * The handle of the service for which the attribute(s) are
+ * requested
+ *
+ * sdp_attrreq_type_t reqtype
+ * Attribute identifiers are 16 bit unsigned integers specified
+ * in one of 2 ways described below :
+ * SDP_ATTR_REQ_INDIVIDUAL - 16bit individual identifiers
+ * They are the actual attribute identifiers in ascending order
+ *
+ * SDP_ATTR_REQ_RANGE - 32bit identifier range
+ * The high-order 16bits is the start of range
+ * the low-order 16bits are the end of range
+ * 0x0000 to 0xFFFF gets all attributes
+ *
+ * sdp_list_t *attrid_list
+ * Singly linked list containing attribute identifiers desired.
+ * Every element is either a uint16_t(attrSpec = SDP_ATTR_REQ_INDIVIDUAL)
+ * or a uint32_t(attrSpec=SDP_ATTR_REQ_RANGE)
+ *
+ * OUTPUT :
+ * int return value
+ * 0 - if the request has been sent properly
+ * -1 - On any failure and sets errno
+ */
+
+int sdp_service_attr_async(sdp_session_t *session, uint32_t handle, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list)
+{
+ struct sdp_transaction *t;
+ sdp_pdu_hdr_t *reqhdr;
+ uint8_t *pdata;
+ int cstate_len, seqlen = 0;
+
+ if (!session || !session->priv)
+ return -1;
+
+ t = session->priv;
+
+ /* clean possible allocated buffer */
+ free(t->rsp_concat_buf.data);
+ memset(&t->rsp_concat_buf, 0, sizeof(sdp_buf_t));
+
+ if (!t->reqbuf) {
+ t->reqbuf = malloc(SDP_REQ_BUFFER_SIZE);
+ if (!t->reqbuf) {
+ t->err = ENOMEM;
+ goto end;
+ }
+ }
+ memset(t->reqbuf, 0, SDP_REQ_BUFFER_SIZE);
+
+ reqhdr = (sdp_pdu_hdr_t *) t->reqbuf;
+ reqhdr->tid = htons(sdp_gen_tid(session));
+ reqhdr->pdu_id = SDP_SVC_ATTR_REQ;
+
+ // generate PDU
+ pdata = t->reqbuf + sizeof(sdp_pdu_hdr_t);
+ t->reqsize = sizeof(sdp_pdu_hdr_t);
+
+ // add the service record handle
+ bt_put_unaligned(htonl(handle), (uint32_t *) pdata);
+ t->reqsize += sizeof(uint32_t);
+ pdata += sizeof(uint32_t);
+
+ // specify the response limit
+ bt_put_unaligned(htons(65535), (uint16_t *) pdata);
+ t->reqsize += sizeof(uint16_t);
+ pdata += sizeof(uint16_t);
+
+ // get attr seq PDU form
+ seqlen = gen_attridseq_pdu(pdata, attrid_list,
+ reqtype == SDP_ATTR_REQ_INDIVIDUAL? SDP_UINT16 : SDP_UINT32);
+ if (seqlen == -1) {
+ t->err = EINVAL;
+ goto end;
+ }
+
+ // now set the length and increment the pointer
+ t->reqsize += seqlen;
+ pdata += seqlen;
+ SDPDBG("Attr list length : %d\n", seqlen);
+
+ // set the request header's param length
+ cstate_len = copy_cstate(pdata, SDP_REQ_BUFFER_SIZE - t->reqsize, NULL);
+ reqhdr->plen = htons((t->reqsize + cstate_len) - sizeof(sdp_pdu_hdr_t));
+
+ if (sdp_send_req(session, t->reqbuf, t->reqsize + cstate_len) < 0) {
+ SDPERR("Error sendind data:%s", strerror(errno));
+ t->err = errno;
+ goto end;
+ }
+
+ return 0;
+end:
+
+ free(t->reqbuf);
+ t->reqbuf = NULL;
+
+ return -1;
+}
+
+/*
+ * This function starts an asynchronous service search attributes.
+ * It is a service search request combined with attribute request. The incomming
+ * and outgoing data are stored in the transaction structure buffers. When there
+ * is incomming data the sdp_process function must be called to get the data
+ * and handle the continuation state.
+ *
+ * INPUT:
+ * sdp_session_t *session
+ * Current sdp session to be handled
+ *
+ * sdp_list_t *search
+ * Singly linked list containing elements of the search
+ * pattern. Each entry in the list is a UUID(DataTypeSDP_UUID16)
+ * of the service to be searched
+ *
+ * AttributeSpecification attrSpec
+ * Attribute identifiers are 16 bit unsigned integers specified
+ * in one of 2 ways described below :
+ * SDP_ATTR_REQ_INDIVIDUAL - 16bit individual identifiers
+ * They are the actual attribute identifiers in ascending order
+ *
+ * SDP_ATTR_REQ_RANGE - 32bit identifier range
+ * The high-order 16bits is the start of range
+ * the low-order 16bits are the end of range
+ * 0x0000 to 0xFFFF gets all attributes
+ *
+ * sdp_list_t *attrid_list
+ * Singly linked list containing attribute identifiers desired.
+ * Every element is either a uint16_t(attrSpec = SDP_ATTR_REQ_INDIVIDUAL)
+ * or a uint32_t(attrSpec=SDP_ATTR_REQ_RANGE)
+ *
+
+ * RETURN:
+ * 0 - if the request has been sent properly
+ * -1 - On any failure
+ */
+int sdp_service_search_attr_async(sdp_session_t *session, const sdp_list_t *search, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list)
+{
+ struct sdp_transaction *t;
+ sdp_pdu_hdr_t *reqhdr;
+ uint8_t *pdata;
+ int cstate_len, seqlen = 0;
+
+ if (!session || !session->priv)
+ return -1;
+
+ t = session->priv;
+
+ /* clean possible allocated buffer */
+ free(t->rsp_concat_buf.data);
+ memset(&t->rsp_concat_buf, 0, sizeof(sdp_buf_t));
+
+ if (!t->reqbuf) {
+ t->reqbuf = malloc(SDP_REQ_BUFFER_SIZE);
+ if (!t->reqbuf) {
+ t->err = ENOMEM;
+ goto end;
+ }
+ }
+ memset(t->reqbuf, 0, SDP_REQ_BUFFER_SIZE);
+
+ reqhdr = (sdp_pdu_hdr_t *) t->reqbuf;
+ reqhdr->tid = htons(sdp_gen_tid(session));
+ reqhdr->pdu_id = SDP_SVC_SEARCH_ATTR_REQ;
+
+ // generate PDU
+ pdata = t->reqbuf + sizeof(sdp_pdu_hdr_t);
+ t->reqsize = sizeof(sdp_pdu_hdr_t);
+
+ // add service class IDs for search
+ seqlen = gen_searchseq_pdu(pdata, search);
+
+ SDPDBG("Data seq added : %d\n", seqlen);
+
+ // now set the length and increment the pointer
+ t->reqsize += seqlen;
+ pdata += seqlen;
+
+ bt_put_unaligned(htons(SDP_MAX_ATTR_LEN), (uint16_t *) pdata);
+ t->reqsize += sizeof(uint16_t);
+ pdata += sizeof(uint16_t);
+
+ SDPDBG("Max attr byte count : %d\n", SDP_MAX_ATTR_LEN);
+
+ // get attr seq PDU form
+ seqlen = gen_attridseq_pdu(pdata, attrid_list,
+ reqtype == SDP_ATTR_REQ_INDIVIDUAL ? SDP_UINT16 : SDP_UINT32);
+ if (seqlen == -1) {
+ t->err = EINVAL;
+ goto end;
+ }
+
+ pdata += seqlen;
+ SDPDBG("Attr list length : %d\n", seqlen);
+ t->reqsize += seqlen;
+
+ // set the request header's param length
+ cstate_len = copy_cstate(pdata, SDP_REQ_BUFFER_SIZE - t->reqsize, NULL);
+ reqhdr->plen = htons((t->reqsize + cstate_len) - sizeof(sdp_pdu_hdr_t));
+
+ if (sdp_send_req(session, t->reqbuf, t->reqsize + cstate_len) < 0) {
+ SDPERR("Error sendind data:%s", strerror(errno));
+ t->err = errno;
+ goto end;
+ }
+
+ return 0;
+end:
+
+ free(t->reqbuf);
+ t->reqbuf = NULL;
+
+ return -1;
+}
+
+/*
+ * Function used to get the error reason after sdp_callback_t function has been called
+ * and the status is 0xffff or if sdp_service_{search, attr, search_attr}_async returns -1.
+ * It indicates that an error NOT related to SDP_ErrorResponse happened. Get errno directly
+ * is not safe because multiple transactions can be triggered.
+ * This function must be used with asynchronous sdp functions only.
+ *
+ * INPUT:
+ * sdp_session_t *session
+ * Current sdp session to be handled
+ * RETURN:
+ * 0 = No error in the current transaction
+ * -1 - if the session is invalid
+ * positive value - the errno value
+ *
+ */
+int sdp_get_error(sdp_session_t *session)
+{
+ struct sdp_transaction *t;
+
+ if (!session || !session->priv) {
+ SDPERR("Invalid session");
+ return -1;
+ }
+
+ t = session->priv;
+
+ return t->err;
+}
+
+/*
+ * Receive the incomming SDP PDU. This function must be called when there is data
+ * available to be read. On continuation state, the original request (with a new
+ * transaction ID) and the continuation state data will be appended in the initial PDU.
+ * If an error happens or the transaction finishes the callback function will be called.
+ *
+ * INPUT:
+ * sdp_session_t *session
+ * Current sdp session to be handled
+ * RETURN:
+ * 0 - if the transaction is on continuation state
+ * -1 - On any failure or the transaction finished
+ */
+int sdp_process(sdp_session_t *session)
+{
+ struct sdp_transaction *t;
+ sdp_pdu_hdr_t *reqhdr, *rsphdr;
+ sdp_cstate_t *pcstate;
+ uint8_t *pdata, *rspbuf, *targetPtr;
+ int rsp_count, err = -1;
+ size_t size = 0;
+ int n, plen;
+ uint16_t status = 0xffff;
+ uint8_t pdu_id = 0x00;
+
+ if (!session || !session->priv) {
+ SDPERR("Invalid session");
+ return -1;
+ }
+
+ rspbuf = malloc(SDP_RSP_BUFFER_SIZE);
+ if (!rspbuf) {
+ SDPERR("Response buffer alloc failure:%s (%d)",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ memset(rspbuf, 0, SDP_RSP_BUFFER_SIZE);
+
+ t = session->priv;
+ reqhdr = (sdp_pdu_hdr_t *)t->reqbuf;
+ rsphdr = (sdp_pdu_hdr_t *)rspbuf;
+
+ pdata = rspbuf + sizeof(sdp_pdu_hdr_t);
+
+ n = sdp_read_rsp(session, rspbuf, SDP_RSP_BUFFER_SIZE);
+ if (n < 0) {
+ SDPERR("Read response:%s (%d)", strerror(errno), errno);
+ t->err = errno;
+ goto end;
+ }
+
+ if (n == 0 || reqhdr->tid != rsphdr->tid ||
+ (n != (ntohs(rsphdr->plen) + (int) sizeof(sdp_pdu_hdr_t)))) {
+ t->err = EPROTO;
+ SDPERR("Protocol error.");
+ goto end;
+ }
+
+ pdu_id = rsphdr->pdu_id;
+ switch (rsphdr->pdu_id) {
+ uint8_t *ssr_pdata;
+ uint16_t tsrc, csrc;
+ case SDP_SVC_SEARCH_RSP:
+ /*
+ * TSRC: Total Service Record Count (2 bytes)
+ * CSRC: Current Service Record Count (2 bytes)
+ */
+ ssr_pdata = pdata;
+ tsrc = ntohs(bt_get_unaligned((uint16_t *) ssr_pdata));
+ ssr_pdata += sizeof(uint16_t);
+ csrc = ntohs(bt_get_unaligned((uint16_t *) ssr_pdata));
+
+ /* csrc should never be larger than tsrc */
+ if (csrc > tsrc) {
+ t->err = EPROTO;
+ SDPERR("Protocol error: wrong current service record count value.");
+ goto end;
+ }
+
+ SDPDBG("Total svc count: %d\n", tsrc);
+ SDPDBG("Current svc count: %d\n", csrc);
+
+ /* parameter length without continuation state */
+ plen = sizeof(tsrc) + sizeof(csrc) + csrc * 4;
+
+ if (t->rsp_concat_buf.data_size == 0) {
+ /* first fragment */
+ rsp_count = sizeof(tsrc) + sizeof(csrc) + csrc * 4;
+ } else {
+ /* point to the first csrc */
+ uint16_t *pcsrc = (uint16_t *) (t->rsp_concat_buf.data + 2);
+
+ /* FIXME: update the interface later. csrc doesn't need be passed to clients */
+
+ pdata += sizeof(uint16_t); /* point to csrc */
+
+ /* the first csrc contains the sum of partial csrc responses */
+ *pcsrc += bt_get_unaligned((uint16_t *) pdata);
+
+ pdata += sizeof(uint16_t); /* point to the first handle */
+ rsp_count = csrc * 4;
+ }
+ status = 0x0000;
+ break;
+ case SDP_SVC_ATTR_RSP:
+ case SDP_SVC_SEARCH_ATTR_RSP:
+ rsp_count = ntohs(bt_get_unaligned((uint16_t *) pdata));
+ SDPDBG("Attrlist byte count : %d\n", rsp_count);
+
+ /*
+ * Number of bytes in the AttributeLists parameter(without
+ * continuation state) + AttributeListsByteCount field size.
+ */
+ plen = sizeof(uint16_t) + rsp_count;
+
+ pdata += sizeof(uint16_t); // points to attribute list
+ status = 0x0000;
+ break;
+ case SDP_ERROR_RSP:
+ status = ntohs(bt_get_unaligned((uint16_t *) pdata));
+ size = ntohs(rsphdr->plen);
+
+ /* error code + error info */
+ plen = size;
+ goto end;
+ default:
+ t->err = EPROTO;
+ SDPERR("Illegal PDU ID: 0x%x", rsphdr->pdu_id);
+ goto end;
+ }
+
+ pcstate = (sdp_cstate_t *) (pdata + rsp_count);
+
+ SDPDBG("Cstate length : %d\n", pcstate->length);
+
+ /*
+ * Check out of bound. Continuation state must have at least
+ * 1 byte: ZERO to indicate that it is not a partial response.
+ */
+ if ((n - (int) sizeof(sdp_pdu_hdr_t)) != (plen + pcstate->length + 1)) {
+ t->err = EPROTO;
+ SDPERR("Protocol error: wrong PDU size.");
+ status = 0xffff;
+ goto end;
+ }
+
+ /*
+ * This is a split response, need to concatenate intermediate
+ * responses and the last one which will have cstate length == 0
+ */
+ t->rsp_concat_buf.data = realloc(t->rsp_concat_buf.data, t->rsp_concat_buf.data_size + rsp_count);
+ targetPtr = t->rsp_concat_buf.data + t->rsp_concat_buf.data_size;
+ t->rsp_concat_buf.buf_size = t->rsp_concat_buf.data_size + rsp_count;
+ memcpy(targetPtr, pdata, rsp_count);
+ t->rsp_concat_buf.data_size += rsp_count;
+
+ if (pcstate->length > 0) {
+ int reqsize, cstate_len;
+
+ reqhdr->tid = htons(sdp_gen_tid(session));
+
+ // add continuation state
+ cstate_len = copy_cstate(t->reqbuf + t->reqsize,
+ SDP_REQ_BUFFER_SIZE - t->reqsize, pcstate);
+
+ reqsize = t->reqsize + cstate_len;
+
+ // set the request header's param length
+ reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t));
+
+ if (sdp_send_req(session, t->reqbuf, reqsize) < 0) {
+ SDPERR("Error sendind data:%s(%d)", strerror(errno), errno);
+ status = 0xffff;
+ t->err = errno;
+ goto end;
+ }
+ err = 0;
+ }
+
+end:
+ if (err) {
+ if (t->rsp_concat_buf.data_size != 0) {
+ pdata = t->rsp_concat_buf.data;
+ size = t->rsp_concat_buf.data_size;
+ }
+ if (t->cb)
+ t->cb(pdu_id, status, pdata, size, t->udata);
+ }
+
+ free(rspbuf);
+
+ return err;
+}
+
+/*
+ * This is a service search request combined with the service
+ * attribute request. First a service class match is done and
+ * for matching service, requested attributes are extracted
+ *
+ * INPUT :
+ *
+ * sdp_list_t *search
+ * Singly linked list containing elements of the search
+ * pattern. Each entry in the list is a UUID(DataTypeSDP_UUID16)
+ * of the service to be searched
+ *
+ * AttributeSpecification attrSpec
+ * Attribute identifiers are 16 bit unsigned integers specified
+ * in one of 2 ways described below :
+ * SDP_ATTR_REQ_INDIVIDUAL - 16bit individual identifiers
+ * They are the actual attribute identifiers in ascending order
+ *
+ * SDP_ATTR_REQ_RANGE - 32bit identifier range
+ * The high-order 16bits is the start of range
+ * the low-order 16bits are the end of range
+ * 0x0000 to 0xFFFF gets all attributes
+ *
+ * sdp_list_t *attrids
+ * Singly linked list containing attribute identifiers desired.
+ * Every element is either a uint16_t(attrSpec = SDP_ATTR_REQ_INDIVIDUAL)
+ * or a uint32_t(attrSpec=SDP_ATTR_REQ_RANGE)
+ *
+ * OUTPUT :
+ * int return value
+ * 0:
+ * The request completed successfully. This does not
+ * mean the requested services were found
+ * -1:
+ * On any error and sets errno
+ *
+ * sdp_list_t **rsp
+ * This variable is set on a successful return to point to
+ * service(s) found. Each element of this list is of type
+ * sdp_record_t* (of the services which matched the search list)
+ */
+int sdp_service_search_attr_req(sdp_session_t *session, const sdp_list_t *search, sdp_attrreq_type_t reqtype, const sdp_list_t *attrids, sdp_list_t **rsp)
+{
+ int status = 0;
+ uint32_t reqsize = 0, _reqsize;
+ uint32_t rspsize = 0;
+ int seqlen = 0, attr_list_len = 0;
+ int rsp_count = 0, cstate_len = 0;
+ unsigned int pdata_len;
+ uint8_t *pdata, *_pdata;
+ uint8_t *reqbuf, *rspbuf;
+ sdp_pdu_hdr_t *reqhdr, *rsphdr;
+ uint8_t dataType;
+ sdp_list_t *rec_list = NULL;
+ sdp_buf_t rsp_concat_buf;
+ sdp_cstate_t *cstate = NULL;
+
+ if (reqtype != SDP_ATTR_REQ_INDIVIDUAL && reqtype != SDP_ATTR_REQ_RANGE) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ memset(&rsp_concat_buf, 0, sizeof(sdp_buf_t));
+
+ reqbuf = malloc(SDP_REQ_BUFFER_SIZE);
+ rspbuf = malloc(SDP_RSP_BUFFER_SIZE);
+ if (!reqbuf || !rspbuf) {
+ errno = ENOMEM;
+ status = -1;
+ goto end;
+ }
+
+ reqhdr = (sdp_pdu_hdr_t *) reqbuf;
+ reqhdr->pdu_id = SDP_SVC_SEARCH_ATTR_REQ;
+
+ // generate PDU
+ pdata = reqbuf + sizeof(sdp_pdu_hdr_t);
+ reqsize = sizeof(sdp_pdu_hdr_t);
+
+ // add service class IDs for search
+ seqlen = gen_searchseq_pdu(pdata, search);
+
+ SDPDBG("Data seq added : %d\n", seqlen);
+
+ /* now set the length and increment the pointer */
+ reqsize += seqlen;
+ pdata += seqlen;
+
+ bt_put_unaligned(htons(SDP_MAX_ATTR_LEN), (uint16_t *) pdata);
+ reqsize += sizeof(uint16_t);
+ pdata += sizeof(uint16_t);
+
+ SDPDBG("Max attr byte count : %d\n", SDP_MAX_ATTR_LEN);
+
+ /* get attr seq PDU form */
+ seqlen = gen_attridseq_pdu(pdata, attrids,
+ reqtype == SDP_ATTR_REQ_INDIVIDUAL ? SDP_UINT16 : SDP_UINT32);
+ if (seqlen == -1) {
+ status = EINVAL;
+ goto end;
+ }
+ pdata += seqlen;
+ SDPDBG("Attr list length : %d\n", seqlen);
+ reqsize += seqlen;
+ *rsp = 0;
+
+ /* save before Continuation State */
+ _pdata = pdata;
+ _reqsize = reqsize;
+
+ do {
+ reqhdr->tid = htons(sdp_gen_tid(session));
+
+ /* add continuation state (can be null) */
+ reqsize = _reqsize + copy_cstate(_pdata,
+ SDP_REQ_BUFFER_SIZE - _reqsize, cstate);
+
+ /* set the request header's param length */
+ reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t));
+ rsphdr = (sdp_pdu_hdr_t *) rspbuf;
+ status = sdp_send_req_w4_rsp(session, reqbuf, rspbuf, reqsize, &rspsize);
+ if (rspsize < sizeof(sdp_pdu_hdr_t)) {
+ SDPERR("Unexpected end of packet");
+ status = -1;
+ goto end;
+ }
+
+ if (status < 0) {
+ SDPDBG("Status : 0x%x\n", rsphdr->pdu_id);
+ goto end;
+ }
+
+ if (rsphdr->pdu_id == SDP_ERROR_RSP) {
+ status = -1;
+ goto end;
+ }
+
+ pdata = rspbuf + sizeof(sdp_pdu_hdr_t);
+ pdata_len = rspsize - sizeof(sdp_pdu_hdr_t);
+
+ if (pdata_len < sizeof(uint16_t)) {
+ SDPERR("Unexpected end of packet");
+ status = -1;
+ goto end;
+ }
+
+ rsp_count = ntohs(bt_get_unaligned((uint16_t *) pdata));
+ attr_list_len += rsp_count;
+ pdata += sizeof(uint16_t); // pdata points to attribute list
+ pdata_len -= sizeof(uint16_t);
+
+ if (pdata_len < rsp_count + sizeof(uint8_t)) {
+ SDPERR("Unexpected end of packet: continuation state data missing");
+ status = -1;
+ goto end;
+ }
+
+ cstate_len = *(uint8_t *) (pdata + rsp_count);
+
+ SDPDBG("Attrlist byte count : %d\n", attr_list_len);
+ SDPDBG("Response byte count : %d\n", rsp_count);
+ SDPDBG("Cstate length : %d\n", cstate_len);
+ /*
+ * This is a split response, need to concatenate intermediate
+ * responses and the last one which will have cstate_len == 0
+ */
+ if (cstate_len > 0 || rsp_concat_buf.data_size != 0) {
+ uint8_t *targetPtr = NULL;
+
+ cstate = cstate_len > 0 ? (sdp_cstate_t *) (pdata + rsp_count) : 0;
+
+ /* build concatenated response buffer */
+ rsp_concat_buf.data = realloc(rsp_concat_buf.data, rsp_concat_buf.data_size + rsp_count);
+ targetPtr = rsp_concat_buf.data + rsp_concat_buf.data_size;
+ rsp_concat_buf.buf_size = rsp_concat_buf.data_size + rsp_count;
+ memcpy(targetPtr, pdata, rsp_count);
+ rsp_concat_buf.data_size += rsp_count;
+ }
+ } while (cstate);
+
+ if (attr_list_len > 0) {
+ int scanned = 0;
+
+ if (rsp_concat_buf.data_size != 0) {
+ pdata = rsp_concat_buf.data;
+ pdata_len = rsp_concat_buf.data_size;
+ }
+
+ /*
+ * Response is a sequence of sequence(s) for one or
+ * more data element sequence(s) representing services
+ * for which attributes are returned
+ */
+ scanned = sdp_extract_seqtype(pdata, pdata_len, &dataType, &seqlen);
+
+ SDPDBG("Bytes scanned : %d\n", scanned);
+ SDPDBG("Seq length : %d\n", seqlen);
+
+ if (scanned && seqlen) {
+ pdata += scanned;
+ pdata_len -= scanned;
+ do {
+ int recsize = 0;
+ sdp_record_t *rec = sdp_extract_pdu(pdata, pdata_len, &recsize);
+ if (rec == NULL) {
+ SDPERR("SVC REC is null\n");
+ status = -1;
+ goto end;
+ }
+ if (!recsize) {
+ sdp_record_free(rec);
+ break;
+ }
+ scanned += recsize;
+ pdata += recsize;
+ pdata_len -= recsize;
+
+ SDPDBG("Loc seq length : %d\n", recsize);
+ SDPDBG("Svc Rec Handle : 0x%x\n", rec->handle);
+ SDPDBG("Bytes scanned : %d\n", scanned);
+ SDPDBG("Attrlist byte count : %d\n", attr_list_len);
+ rec_list = sdp_list_append(rec_list, rec);
+ } while (scanned < attr_list_len && pdata_len > 0);
+
+ SDPDBG("Successful scan of service attr lists\n");
+ *rsp = rec_list;
+ }
+ }
+end:
+ free(rsp_concat_buf.data);
+ free(reqbuf);
+ free(rspbuf);
+ return status;
+}
+
+/*
+ * Find devices in the piconet.
+ */
+int sdp_general_inquiry(inquiry_info *ii, int num_dev, int duration, uint8_t *found)
+{
+ int n = hci_inquiry(-1, 10, num_dev, NULL, &ii, 0);
+ if (n < 0) {
+ SDPERR("Inquiry failed:%s", strerror(errno));
+ return -1;
+ }
+ *found = n;
+ return 0;
+}
+
+int sdp_close(sdp_session_t *session)
+{
+ struct sdp_transaction *t;
+ int ret;
+
+ if (!session)
+ return -1;
+
+ ret = close(session->sock);
+
+ t = session->priv;
+
+ if (t) {
+ free(t->reqbuf);
+
+ free(t->rsp_concat_buf.data);
+
+ free(t);
+ }
+ free(session);
+ return ret;
+}
+
+static inline int sdp_is_local(const bdaddr_t *device)
+{
+ return memcmp(device, BDADDR_LOCAL, sizeof(bdaddr_t)) == 0;
+}
+
+static int sdp_connect_local(sdp_session_t *session)
+{
+ struct sockaddr_un sa;
+
+ session->sock = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (session->sock < 0)
+ return -1;
+ session->local = 1;
+
+ sa.sun_family = AF_UNIX;
+ strcpy(sa.sun_path, SDP_UNIX_PATH);
+
+ return connect(session->sock, (struct sockaddr *) &sa, sizeof(sa));
+}
+
+static int sdp_connect_l2cap(const bdaddr_t *src,
+ const bdaddr_t *dst, sdp_session_t *session)
+{
+ uint32_t flags = session->flags;
+ struct sockaddr_l2 sa;
+ int sk;
+
+ session->sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+ if (session->sock < 0)
+ return -1;
+ session->local = 0;
+
+ sk = session->sock;
+
+ if (flags & SDP_NON_BLOCKING) {
+ long arg = fcntl(sk, F_GETFL, 0);
+ fcntl(sk, F_SETFL, arg | O_NONBLOCK);
+ }
+
+ memset(&sa, 0, sizeof(sa));
+
+ sa.l2_family = AF_BLUETOOTH;
+ sa.l2_psm = 0;
+
+ if (bacmp(src, BDADDR_ANY)) {
+ sa.l2_bdaddr = *src;
+ if (bind(sk, (struct sockaddr *) &sa, sizeof(sa)) < 0)
+ return -1;
+ }
+
+ if (flags & SDP_WAIT_ON_CLOSE) {
+ struct linger l = { .l_onoff = 1, .l_linger = 1 };
+ setsockopt(sk, SOL_SOCKET, SO_LINGER, &l, sizeof(l));
+ }
+
+ sa.l2_psm = htobs(SDP_PSM);
+ sa.l2_bdaddr = *dst;
+
+ do {
+ int ret = connect(sk, (struct sockaddr *) &sa, sizeof(sa));
+ if (!ret)
+ return 0;
+ if (ret < 0 && (flags & SDP_NON_BLOCKING) &&
+ (errno == EAGAIN || errno == EINPROGRESS))
+ return 0;
+ } while (errno == EBUSY && (flags & SDP_RETRY_IF_BUSY));
+
+ return -1;
+}
+
+sdp_session_t *sdp_connect(const bdaddr_t *src,
+ const bdaddr_t *dst, uint32_t flags)
+{
+ sdp_session_t *session;
+ int err;
+
+ if ((flags & SDP_RETRY_IF_BUSY) && (flags & SDP_NON_BLOCKING)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ session = sdp_create(-1, flags);
+ if (!session)
+ return NULL;
+
+ if (sdp_is_local(dst)) {
+ if (sdp_connect_local(session) < 0)
+ goto fail;
+ } else {
+ if (sdp_connect_l2cap(src, dst, session) < 0)
+ goto fail;
+ }
+
+ return session;
+
+fail:
+ err = errno;
+ if (session->sock >= 0)
+ close(session->sock);
+ free(session->priv);
+ free(session);
+ errno = err;
+
+ return NULL;
+}
+
+int sdp_get_socket(const sdp_session_t *session)
+{
+ return session->sock;
+}
+
+uint16_t sdp_gen_tid(sdp_session_t *session)
+{
+ return session->tid++;
+}
+
+/*
+ * Set the supported features
+ */
+int sdp_set_supp_feat(sdp_record_t *rec, const sdp_list_t *sf)
+{
+ const sdp_list_t *p, *r;
+ sdp_data_t *feat, *seq_feat;
+ int seqlen, i;
+ void **seqDTDs, **seqVals;
+
+ seqlen = sdp_list_len(sf);
+ seqDTDs = malloc(seqlen * sizeof(void *));
+ if (!seqDTDs)
+ return -1;
+ seqVals = malloc(seqlen * sizeof(void *));
+ if (!seqVals) {
+ free(seqDTDs);
+ return -1;
+ }
+
+ for (p = sf, i = 0; p; p = p->next, i++) {
+ int plen, j;
+ void **dtds, **vals;
+ int *lengths;
+
+ plen = sdp_list_len(p->data);
+ dtds = malloc(plen * sizeof(void *));
+ if (!dtds)
+ goto fail;
+ vals = malloc(plen * sizeof(void *));
+ if (!vals) {
+ free(dtds);
+ goto fail;
+ }
+ lengths = malloc(plen * sizeof(int *));
+ if (!lengths) {
+ free(dtds);
+ free(vals);
+ goto fail;
+ }
+ for (r = p->data, j = 0; r; r = r->next, j++) {
+ sdp_data_t *data = (sdp_data_t*)r->data;
+ dtds[j] = &data->dtd;
+ switch (data->dtd) {
+ case SDP_URL_STR8:
+ case SDP_URL_STR16:
+ case SDP_TEXT_STR8:
+ case SDP_TEXT_STR16:
+ vals[j] = data->val.str;
+ lengths[j] = data->unitSize - sizeof(uint8_t);
+ break;
+ case SDP_ALT8:
+ case SDP_ALT16:
+ case SDP_ALT32:
+ case SDP_SEQ8:
+ case SDP_SEQ16:
+ case SDP_SEQ32:
+ vals[j] = data->val.dataseq;
+ lengths[j] = 0;
+ break;
+ default:
+ vals[j] = &data->val;
+ lengths[j] = 0;
+ break;
+ }
+ }
+ feat = sdp_seq_alloc_with_length(dtds, vals, lengths, plen);
+ free(dtds);
+ free(vals);
+ free(lengths);
+ if (!feat)
+ goto fail;
+ seqDTDs[i] = &feat->dtd;
+ seqVals[i] = feat;
+ }
+ seq_feat = sdp_seq_alloc(seqDTDs, seqVals, seqlen);
+ if (!seq_feat)
+ goto fail;
+ sdp_attr_replace(rec, SDP_ATTR_SUPPORTED_FEATURES_LIST, seq_feat);
+
+ free(seqVals);
+ free(seqDTDs);
+ return 0;
+
+fail:
+ free(seqVals);
+ free(seqDTDs);
+ return -1;
+}
+
+/*
+ * Get the supported features
+ * If an error occurred -1 is returned and errno is set
+ */
+int sdp_get_supp_feat(const sdp_record_t *rec, sdp_list_t **seqp)
+{
+ sdp_data_t *sdpdata, *d;
+ sdp_list_t *tseq;
+ tseq = NULL;
+
+ sdpdata = sdp_data_get(rec, SDP_ATTR_SUPPORTED_FEATURES_LIST);
+
+ if (!sdpdata || sdpdata->dtd < SDP_SEQ8 || sdpdata->dtd > SDP_SEQ32)
+ return sdp_get_uuidseq_attr(rec,
+ SDP_ATTR_SUPPORTED_FEATURES_LIST, seqp);
+
+ for (d = sdpdata->val.dataseq; d; d = d->next) {
+ sdp_data_t *dd;
+ sdp_list_t *subseq;
+
+ if (d->dtd < SDP_SEQ8 || d->dtd > SDP_SEQ32)
+ goto fail;
+
+ subseq = NULL;
+
+ for (dd = d->val.dataseq; dd; dd = dd->next) {
+ sdp_data_t *data;
+ void *val;
+ int length;
+
+ switch (dd->dtd) {
+ case SDP_URL_STR8:
+ case SDP_URL_STR16:
+ case SDP_TEXT_STR8:
+ case SDP_TEXT_STR16:
+ val = dd->val.str;
+ length = dd->unitSize - sizeof(uint8_t);
+ break;
+ case SDP_UINT8:
+ case SDP_UINT16:
+ val = &dd->val;
+ length = 0;
+ break;
+ default:
+ goto fail;
+ }
+
+ data = sdp_data_alloc_with_length(dd->dtd, val, length);
+ if (data)
+ subseq = sdp_list_append(subseq, data);
+ }
+ tseq = sdp_list_append(tseq, subseq);
+ }
+ *seqp = tseq;
+ return 0;
+
+fail:
+ while (tseq) {
+ sdp_list_t * next;
+
+ next = tseq->next;
+ sdp_list_free(tseq, free);
+ tseq = next;
+ }
+ errno = EINVAL;
+ return -1;
+}
+
diff --git a/lib/sdp.h b/lib/sdp.h
new file mode 100644
index 0000000..5f7d271
--- /dev/null
+++ b/lib/sdp.h
@@ -0,0 +1,518 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2001-2002 Nokia Corporation
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2002-2003 Stephen Crane <steve.crane@rococosoft.com>
+ *
+ *
+ * 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
+ *
+ */
+
+#ifndef __SDP_H
+#define __SDP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <bluetooth/bluetooth.h>
+
+#define SDP_UNIX_PATH "/var/run/sdp"
+#define SDP_RESPONSE_TIMEOUT 20
+#define SDP_REQ_BUFFER_SIZE 2048
+#define SDP_RSP_BUFFER_SIZE 65535
+#define SDP_PDU_CHUNK_SIZE 1024
+
+/*
+ * All definitions are based on Bluetooth Assigned Numbers
+ * of the Bluetooth Specification
+ */
+#define SDP_PSM 0x0001
+
+/*
+ * Protocol UUIDs
+ */
+#define SDP_UUID 0x0001
+#define UDP_UUID 0x0002
+#define RFCOMM_UUID 0x0003
+#define TCP_UUID 0x0004
+#define TCS_BIN_UUID 0x0005
+#define TCS_AT_UUID 0x0006
+#define ATT_UUID 0x0007
+#define OBEX_UUID 0x0008
+#define IP_UUID 0x0009
+#define FTP_UUID 0x000a
+#define HTTP_UUID 0x000c
+#define WSP_UUID 0x000e
+#define BNEP_UUID 0x000f
+#define UPNP_UUID 0x0010
+#define HIDP_UUID 0x0011
+#define HCRP_CTRL_UUID 0x0012
+#define HCRP_DATA_UUID 0x0014
+#define HCRP_NOTE_UUID 0x0016
+#define AVCTP_UUID 0x0017
+#define AVDTP_UUID 0x0019
+#define CMTP_UUID 0x001b
+#define UDI_UUID 0x001d
+#define MCAP_CTRL_UUID 0x001e
+#define MCAP_DATA_UUID 0x001f
+#define L2CAP_UUID 0x0100
+
+/*
+ * Service class identifiers of standard services and service groups
+ */
+#define SDP_SERVER_SVCLASS_ID 0x1000
+#define BROWSE_GRP_DESC_SVCLASS_ID 0x1001
+#define PUBLIC_BROWSE_GROUP 0x1002
+#define SERIAL_PORT_SVCLASS_ID 0x1101
+#define LAN_ACCESS_SVCLASS_ID 0x1102
+#define DIALUP_NET_SVCLASS_ID 0x1103
+#define IRMC_SYNC_SVCLASS_ID 0x1104
+#define OBEX_OBJPUSH_SVCLASS_ID 0x1105
+#define OBEX_FILETRANS_SVCLASS_ID 0x1106
+#define IRMC_SYNC_CMD_SVCLASS_ID 0x1107
+#define HEADSET_SVCLASS_ID 0x1108
+#define CORDLESS_TELEPHONY_SVCLASS_ID 0x1109
+#define AUDIO_SOURCE_SVCLASS_ID 0x110a
+#define AUDIO_SINK_SVCLASS_ID 0x110b
+#define AV_REMOTE_TARGET_SVCLASS_ID 0x110c
+#define ADVANCED_AUDIO_SVCLASS_ID 0x110d
+#define AV_REMOTE_SVCLASS_ID 0x110e
+#define VIDEO_CONF_SVCLASS_ID 0x110f
+#define INTERCOM_SVCLASS_ID 0x1110
+#define FAX_SVCLASS_ID 0x1111
+#define HEADSET_AGW_SVCLASS_ID 0x1112
+#define WAP_SVCLASS_ID 0x1113
+#define WAP_CLIENT_SVCLASS_ID 0x1114
+#define PANU_SVCLASS_ID 0x1115
+#define NAP_SVCLASS_ID 0x1116
+#define GN_SVCLASS_ID 0x1117
+#define DIRECT_PRINTING_SVCLASS_ID 0x1118
+#define REFERENCE_PRINTING_SVCLASS_ID 0x1119
+#define IMAGING_SVCLASS_ID 0x111a
+#define IMAGING_RESPONDER_SVCLASS_ID 0x111b
+#define IMAGING_ARCHIVE_SVCLASS_ID 0x111c
+#define IMAGING_REFOBJS_SVCLASS_ID 0x111d
+#define HANDSFREE_SVCLASS_ID 0x111e
+#define HANDSFREE_AGW_SVCLASS_ID 0x111f
+#define DIRECT_PRT_REFOBJS_SVCLASS_ID 0x1120
+#define REFLECTED_UI_SVCLASS_ID 0x1121
+#define BASIC_PRINTING_SVCLASS_ID 0x1122
+#define PRINTING_STATUS_SVCLASS_ID 0x1123
+#define HID_SVCLASS_ID 0x1124
+#define HCR_SVCLASS_ID 0x1125
+#define HCR_PRINT_SVCLASS_ID 0x1126
+#define HCR_SCAN_SVCLASS_ID 0x1127
+#define CIP_SVCLASS_ID 0x1128
+#define VIDEO_CONF_GW_SVCLASS_ID 0x1129
+#define UDI_MT_SVCLASS_ID 0x112a
+#define UDI_TA_SVCLASS_ID 0x112b
+#define AV_SVCLASS_ID 0x112c
+#define SAP_SVCLASS_ID 0x112d
+#define PBAP_PCE_SVCLASS_ID 0x112e
+#define PBAP_PSE_SVCLASS_ID 0x112f
+#define PBAP_SVCLASS_ID 0x1130
+#define PNP_INFO_SVCLASS_ID 0x1200
+#define GENERIC_NETWORKING_SVCLASS_ID 0x1201
+#define GENERIC_FILETRANS_SVCLASS_ID 0x1202
+#define GENERIC_AUDIO_SVCLASS_ID 0x1203
+#define GENERIC_TELEPHONY_SVCLASS_ID 0x1204
+#define UPNP_SVCLASS_ID 0x1205
+#define UPNP_IP_SVCLASS_ID 0x1206
+#define UPNP_PAN_SVCLASS_ID 0x1300
+#define UPNP_LAP_SVCLASS_ID 0x1301
+#define UPNP_L2CAP_SVCLASS_ID 0x1302
+#define VIDEO_SOURCE_SVCLASS_ID 0x1303
+#define VIDEO_SINK_SVCLASS_ID 0x1304
+#define VIDEO_DISTRIBUTION_SVCLASS_ID 0x1305
+#define HDP_SVCLASS_ID 0x1400
+#define HDP_SOURCE_SVCLASS_ID 0x1401
+#define HDP_SINK_SVCLASS_ID 0x1402
+#define APPLE_AGENT_SVCLASS_ID 0x2112
+#define GENERIC_ATTRIB_SVCLASS_ID 0x1801
+
+/*
+ * Standard profile descriptor identifiers; note these
+ * may be identical to some of the service classes defined above
+ */
+#define SDP_SERVER_PROFILE_ID SDP_SERVER_SVCLASS_ID
+#define BROWSE_GRP_DESC_PROFILE_ID BROWSE_GRP_DESC_SVCLASS_ID
+#define SERIAL_PORT_PROFILE_ID SERIAL_PORT_SVCLASS_ID
+#define LAN_ACCESS_PROFILE_ID LAN_ACCESS_SVCLASS_ID
+#define DIALUP_NET_PROFILE_ID DIALUP_NET_SVCLASS_ID
+#define IRMC_SYNC_PROFILE_ID IRMC_SYNC_SVCLASS_ID
+#define OBEX_OBJPUSH_PROFILE_ID OBEX_OBJPUSH_SVCLASS_ID
+#define OBEX_FILETRANS_PROFILE_ID OBEX_FILETRANS_SVCLASS_ID
+#define IRMC_SYNC_CMD_PROFILE_ID IRMC_SYNC_CMD_SVCLASS_ID
+#define HEADSET_PROFILE_ID HEADSET_SVCLASS_ID
+#define CORDLESS_TELEPHONY_PROFILE_ID CORDLESS_TELEPHONY_SVCLASS_ID
+#define AUDIO_SOURCE_PROFILE_ID AUDIO_SOURCE_SVCLASS_ID
+#define AUDIO_SINK_PROFILE_ID AUDIO_SINK_SVCLASS_ID
+#define AV_REMOTE_TARGET_PROFILE_ID AV_REMOTE_TARGET_SVCLASS_ID
+#define ADVANCED_AUDIO_PROFILE_ID ADVANCED_AUDIO_SVCLASS_ID
+#define AV_REMOTE_PROFILE_ID AV_REMOTE_SVCLASS_ID
+#define VIDEO_CONF_PROFILE_ID VIDEO_CONF_SVCLASS_ID
+#define INTERCOM_PROFILE_ID INTERCOM_SVCLASS_ID
+#define FAX_PROFILE_ID FAX_SVCLASS_ID
+#define HEADSET_AGW_PROFILE_ID HEADSET_AGW_SVCLASS_ID
+#define WAP_PROFILE_ID WAP_SVCLASS_ID
+#define WAP_CLIENT_PROFILE_ID WAP_CLIENT_SVCLASS_ID
+#define PANU_PROFILE_ID PANU_SVCLASS_ID
+#define NAP_PROFILE_ID NAP_SVCLASS_ID
+#define GN_PROFILE_ID GN_SVCLASS_ID
+#define DIRECT_PRINTING_PROFILE_ID DIRECT_PRINTING_SVCLASS_ID
+#define REFERENCE_PRINTING_PROFILE_ID REFERENCE_PRINTING_SVCLASS_ID
+#define IMAGING_PROFILE_ID IMAGING_SVCLASS_ID
+#define IMAGING_RESPONDER_PROFILE_ID IMAGING_RESPONDER_SVCLASS_ID
+#define IMAGING_ARCHIVE_PROFILE_ID IMAGING_ARCHIVE_SVCLASS_ID
+#define IMAGING_REFOBJS_PROFILE_ID IMAGING_REFOBJS_SVCLASS_ID
+#define HANDSFREE_PROFILE_ID HANDSFREE_SVCLASS_ID
+#define HANDSFREE_AGW_PROFILE_ID HANDSFREE_AGW_SVCLASS_ID
+#define DIRECT_PRT_REFOBJS_PROFILE_ID DIRECT_PRT_REFOBJS_SVCLASS_ID
+#define REFLECTED_UI_PROFILE_ID REFLECTED_UI_SVCLASS_ID
+#define BASIC_PRINTING_PROFILE_ID BASIC_PRINTING_SVCLASS_ID
+#define PRINTING_STATUS_PROFILE_ID PRINTING_STATUS_SVCLASS_ID
+#define HID_PROFILE_ID HID_SVCLASS_ID
+#define HCR_PROFILE_ID HCR_SCAN_SVCLASS_ID
+#define HCR_PRINT_PROFILE_ID HCR_PRINT_SVCLASS_ID
+#define HCR_SCAN_PROFILE_ID HCR_SCAN_SVCLASS_ID
+#define CIP_PROFILE_ID CIP_SVCLASS_ID
+#define VIDEO_CONF_GW_PROFILE_ID VIDEO_CONF_GW_SVCLASS_ID
+#define UDI_MT_PROFILE_ID UDI_MT_SVCLASS_ID
+#define UDI_TA_PROFILE_ID UDI_TA_SVCLASS_ID
+#define AV_PROFILE_ID AV_SVCLASS_ID
+#define SAP_PROFILE_ID SAP_SVCLASS_ID
+#define PBAP_PCE_PROFILE_ID PBAP_PCE_SVCLASS_ID
+#define PBAP_PSE_PROFILE_ID PBAP_PSE_SVCLASS_ID
+#define PBAP_PROFILE_ID PBAP_SVCLASS_ID
+#define PNP_INFO_PROFILE_ID PNP_INFO_SVCLASS_ID
+#define GENERIC_NETWORKING_PROFILE_ID GENERIC_NETWORKING_SVCLASS_ID
+#define GENERIC_FILETRANS_PROFILE_ID GENERIC_FILETRANS_SVCLASS_ID
+#define GENERIC_AUDIO_PROFILE_ID GENERIC_AUDIO_SVCLASS_ID
+#define GENERIC_TELEPHONY_PROFILE_ID GENERIC_TELEPHONY_SVCLASS_ID
+#define UPNP_PROFILE_ID UPNP_SVCLASS_ID
+#define UPNP_IP_PROFILE_ID UPNP_IP_SVCLASS_ID
+#define UPNP_PAN_PROFILE_ID UPNP_PAN_SVCLASS_ID
+#define UPNP_LAP_PROFILE_ID UPNP_LAP_SVCLASS_ID
+#define UPNP_L2CAP_PROFILE_ID UPNP_L2CAP_SVCLASS_ID
+#define VIDEO_SOURCE_PROFILE_ID VIDEO_SOURCE_SVCLASS_ID
+#define VIDEO_SINK_PROFILE_ID VIDEO_SINK_SVCLASS_ID
+#define VIDEO_DISTRIBUTION_PROFILE_ID VIDEO_DISTRIBUTION_SVCLASS_ID
+#define HDP_PROFILE_ID HDP_SVCLASS_ID
+#define HDP_SOURCE_PROFILE_ID HDP_SOURCE_SVCLASS_ID
+#define HDP_SINK_PROFILE_ID HDP_SINK_SVCLASS_ID
+#define APPLE_AGENT_PROFILE_ID APPLE_AGENT_SVCLASS_ID
+#define GENERIC_ACCESS_PROFILE_ID 0x1800
+#define GENERIC_ATTRIB_PROFILE_ID GENERIC_ATTRIB_SVCLASS_ID
+
+/*
+ * Compatibility macros for the old MDP acronym
+ */
+#define MDP_SVCLASS_ID HDP_SVCLASS_ID
+#define MDP_SOURCE_SVCLASS_ID HDP_SOURCE_SVCLASS_ID
+#define MDP_SINK_SVCLASS_ID HDP_SINK_SVCLASS_ID
+#define MDP_PROFILE_ID HDP_PROFILE_ID
+#define MDP_SOURCE_PROFILE_ID HDP_SOURCE_PROFILE_ID
+#define MDP_SINK_PROFILE_ID HDP_SINK_PROFILE_ID
+
+/*
+ * Attribute identifier codes
+ */
+#define SDP_SERVER_RECORD_HANDLE 0x0000
+
+/*
+ * Possible values for attribute-id are listed below.
+ * See SDP Spec, section "Service Attribute Definitions" for more details.
+ */
+#define SDP_ATTR_RECORD_HANDLE 0x0000
+#define SDP_ATTR_SVCLASS_ID_LIST 0x0001
+#define SDP_ATTR_RECORD_STATE 0x0002
+#define SDP_ATTR_SERVICE_ID 0x0003
+#define SDP_ATTR_PROTO_DESC_LIST 0x0004
+#define SDP_ATTR_BROWSE_GRP_LIST 0x0005
+#define SDP_ATTR_LANG_BASE_ATTR_ID_LIST 0x0006
+#define SDP_ATTR_SVCINFO_TTL 0x0007
+#define SDP_ATTR_SERVICE_AVAILABILITY 0x0008
+#define SDP_ATTR_PFILE_DESC_LIST 0x0009
+#define SDP_ATTR_DOC_URL 0x000a
+#define SDP_ATTR_CLNT_EXEC_URL 0x000b
+#define SDP_ATTR_ICON_URL 0x000c
+#define SDP_ATTR_ADD_PROTO_DESC_LIST 0x000d
+
+#define SDP_ATTR_GROUP_ID 0x0200
+#define SDP_ATTR_IP_SUBNET 0x0200
+#define SDP_ATTR_VERSION_NUM_LIST 0x0200
+#define SDP_ATTR_SUPPORTED_FEATURES_LIST 0x0200
+#define SDP_ATTR_SVCDB_STATE 0x0201
+
+#define SDP_ATTR_SERVICE_VERSION 0x0300
+#define SDP_ATTR_EXTERNAL_NETWORK 0x0301
+#define SDP_ATTR_SUPPORTED_DATA_STORES_LIST 0x0301
+#define SDP_ATTR_DATA_EXCHANGE_SPEC 0x0301
+#define SDP_ATTR_FAX_CLASS1_SUPPORT 0x0302
+#define SDP_ATTR_REMOTE_AUDIO_VOLUME_CONTROL 0x0302
+#define SDP_ATTR_MCAP_SUPPORTED_PROCEDURES 0x0302
+#define SDP_ATTR_FAX_CLASS20_SUPPORT 0x0303
+#define SDP_ATTR_SUPPORTED_FORMATS_LIST 0x0303
+#define SDP_ATTR_FAX_CLASS2_SUPPORT 0x0304
+#define SDP_ATTR_AUDIO_FEEDBACK_SUPPORT 0x0305
+#define SDP_ATTR_NETWORK_ADDRESS 0x0306
+#define SDP_ATTR_WAP_GATEWAY 0x0307
+#define SDP_ATTR_HOMEPAGE_URL 0x0308
+#define SDP_ATTR_WAP_STACK_TYPE 0x0309
+#define SDP_ATTR_SECURITY_DESC 0x030a
+#define SDP_ATTR_NET_ACCESS_TYPE 0x030b
+#define SDP_ATTR_MAX_NET_ACCESSRATE 0x030c
+#define SDP_ATTR_IP4_SUBNET 0x030d
+#define SDP_ATTR_IP6_SUBNET 0x030e
+#define SDP_ATTR_SUPPORTED_CAPABILITIES 0x0310
+#define SDP_ATTR_SUPPORTED_FEATURES 0x0311
+#define SDP_ATTR_SUPPORTED_FUNCTIONS 0x0312
+#define SDP_ATTR_TOTAL_IMAGING_DATA_CAPACITY 0x0313
+#define SDP_ATTR_SUPPORTED_REPOSITORIES 0x0314
+
+#define SDP_ATTR_SPECIFICATION_ID 0x0200
+#define SDP_ATTR_VENDOR_ID 0x0201
+#define SDP_ATTR_PRODUCT_ID 0x0202
+#define SDP_ATTR_VERSION 0x0203
+#define SDP_ATTR_PRIMARY_RECORD 0x0204
+#define SDP_ATTR_VENDOR_ID_SOURCE 0x0205
+
+#define SDP_ATTR_HID_DEVICE_RELEASE_NUMBER 0x0200
+#define SDP_ATTR_HID_PARSER_VERSION 0x0201
+#define SDP_ATTR_HID_DEVICE_SUBCLASS 0x0202
+#define SDP_ATTR_HID_COUNTRY_CODE 0x0203
+#define SDP_ATTR_HID_VIRTUAL_CABLE 0x0204
+#define SDP_ATTR_HID_RECONNECT_INITIATE 0x0205
+#define SDP_ATTR_HID_DESCRIPTOR_LIST 0x0206
+#define SDP_ATTR_HID_LANG_ID_BASE_LIST 0x0207
+#define SDP_ATTR_HID_SDP_DISABLE 0x0208
+#define SDP_ATTR_HID_BATTERY_POWER 0x0209
+#define SDP_ATTR_HID_REMOTE_WAKEUP 0x020a
+#define SDP_ATTR_HID_PROFILE_VERSION 0x020b
+#define SDP_ATTR_HID_SUPERVISION_TIMEOUT 0x020c
+#define SDP_ATTR_HID_NORMALLY_CONNECTABLE 0x020d
+#define SDP_ATTR_HID_BOOT_DEVICE 0x020e
+
+/*
+ * These identifiers are based on the SDP spec stating that
+ * "base attribute id of the primary (universal) language must be 0x0100"
+ *
+ * Other languages should have their own offset; e.g.:
+ * #define XXXLangBase yyyy
+ * #define AttrServiceName_XXX 0x0000+XXXLangBase
+ */
+#define SDP_PRIMARY_LANG_BASE 0x0100
+
+#define SDP_ATTR_SVCNAME_PRIMARY 0x0000 + SDP_PRIMARY_LANG_BASE
+#define SDP_ATTR_SVCDESC_PRIMARY 0x0001 + SDP_PRIMARY_LANG_BASE
+#define SDP_ATTR_PROVNAME_PRIMARY 0x0002 + SDP_PRIMARY_LANG_BASE
+
+/*
+ * The Data representation in SDP PDUs (pps 339, 340 of BT SDP Spec)
+ * These are the exact data type+size descriptor values
+ * that go into the PDU buffer.
+ *
+ * The datatype (leading 5bits) + size descriptor (last 3 bits)
+ * is 8 bits. The size descriptor is critical to extract the
+ * right number of bytes for the data value from the PDU.
+ *
+ * For most basic types, the datatype+size descriptor is
+ * straightforward. However for constructed types and strings,
+ * the size of the data is in the next "n" bytes following the
+ * 8 bits (datatype+size) descriptor. Exactly what the "n" is
+ * specified in the 3 bits of the data size descriptor.
+ *
+ * TextString and URLString can be of size 2^{8, 16, 32} bytes
+ * DataSequence and DataSequenceAlternates can be of size 2^{8, 16, 32}
+ * The size are computed post-facto in the API and are not known apriori
+ */
+#define SDP_DATA_NIL 0x00
+#define SDP_UINT8 0x08
+#define SDP_UINT16 0x09
+#define SDP_UINT32 0x0A
+#define SDP_UINT64 0x0B
+#define SDP_UINT128 0x0C
+#define SDP_INT8 0x10
+#define SDP_INT16 0x11
+#define SDP_INT32 0x12
+#define SDP_INT64 0x13
+#define SDP_INT128 0x14
+#define SDP_UUID_UNSPEC 0x18
+#define SDP_UUID16 0x19
+#define SDP_UUID32 0x1A
+#define SDP_UUID128 0x1C
+#define SDP_TEXT_STR_UNSPEC 0x20
+#define SDP_TEXT_STR8 0x25
+#define SDP_TEXT_STR16 0x26
+#define SDP_TEXT_STR32 0x27
+#define SDP_BOOL 0x28
+#define SDP_SEQ_UNSPEC 0x30
+#define SDP_SEQ8 0x35
+#define SDP_SEQ16 0x36
+#define SDP_SEQ32 0x37
+#define SDP_ALT_UNSPEC 0x38
+#define SDP_ALT8 0x3D
+#define SDP_ALT16 0x3E
+#define SDP_ALT32 0x3F
+#define SDP_URL_STR_UNSPEC 0x40
+#define SDP_URL_STR8 0x45
+#define SDP_URL_STR16 0x46
+#define SDP_URL_STR32 0x47
+
+/*
+ * The PDU identifiers of SDP packets between client and server
+ */
+#define SDP_ERROR_RSP 0x01
+#define SDP_SVC_SEARCH_REQ 0x02
+#define SDP_SVC_SEARCH_RSP 0x03
+#define SDP_SVC_ATTR_REQ 0x04
+#define SDP_SVC_ATTR_RSP 0x05
+#define SDP_SVC_SEARCH_ATTR_REQ 0x06
+#define SDP_SVC_SEARCH_ATTR_RSP 0x07
+
+/*
+ * Some additions to support service registration.
+ * These are outside the scope of the Bluetooth specification
+ */
+#define SDP_SVC_REGISTER_REQ 0x75
+#define SDP_SVC_REGISTER_RSP 0x76
+#define SDP_SVC_UPDATE_REQ 0x77
+#define SDP_SVC_UPDATE_RSP 0x78
+#define SDP_SVC_REMOVE_REQ 0x79
+#define SDP_SVC_REMOVE_RSP 0x80
+
+/*
+ * SDP Error codes
+ */
+#define SDP_INVALID_VERSION 0x0001
+#define SDP_INVALID_RECORD_HANDLE 0x0002
+#define SDP_INVALID_SYNTAX 0x0003
+#define SDP_INVALID_PDU_SIZE 0x0004
+#define SDP_INVALID_CSTATE 0x0005
+
+/*
+ * SDP PDU
+ */
+typedef struct {
+ uint8_t pdu_id;
+ uint16_t tid;
+ uint16_t plen;
+} __attribute__ ((packed)) sdp_pdu_hdr_t;
+
+/*
+ * Common definitions for attributes in the SDP.
+ * Should the type of any of these change, you need only make a change here.
+ */
+
+typedef struct {
+ uint8_t type;
+ union {
+ uint16_t uuid16;
+ uint32_t uuid32;
+ uint128_t uuid128;
+ } value;
+} uuid_t;
+
+#define SDP_IS_UUID(x) ((x) == SDP_UUID16 || (x) == SDP_UUID32 || (x) ==SDP_UUID128)
+
+typedef struct _sdp_list sdp_list_t;
+struct _sdp_list {
+ sdp_list_t *next;
+ void *data;
+};
+
+/*
+ * User-visible strings can be in many languages
+ * in addition to the universal language.
+ *
+ * Language meta-data includes language code in ISO639
+ * followed by the encoding format. The third field in this
+ * structure is the attribute offset for the language.
+ * User-visible strings in the specified language can be
+ * obtained at this offset.
+ */
+typedef struct {
+ uint16_t code_ISO639;
+ uint16_t encoding;
+ uint16_t base_offset;
+} sdp_lang_attr_t;
+
+/*
+ * Profile descriptor is the Bluetooth profile metadata. If a
+ * service conforms to a well-known profile, then its profile
+ * identifier (UUID) is an attribute of the service. In addition,
+ * if the profile has a version number it is specified here.
+ */
+typedef struct {
+ uuid_t uuid;
+ uint16_t version;
+} sdp_profile_desc_t;
+
+typedef struct {
+ uint8_t major;
+ uint8_t minor;
+} sdp_version_t;
+
+typedef struct {
+ uint8_t *data;
+ uint32_t data_size;
+ uint32_t buf_size;
+} sdp_buf_t;
+
+typedef struct {
+ uint32_t handle;
+
+ /* Search pattern: a sequence of all UUIDs seen in this record */
+ sdp_list_t *pattern;
+ sdp_list_t *attrlist;
+
+ /* Main service class for Extended Inquiry Response */
+ uuid_t svclass;
+} sdp_record_t;
+
+typedef struct sdp_data_struct sdp_data_t;
+struct sdp_data_struct {
+ uint8_t dtd;
+ uint16_t attrId;
+ union {
+ int8_t int8;
+ int16_t int16;
+ int32_t int32;
+ int64_t int64;
+ uint128_t int128;
+ uint8_t uint8;
+ uint16_t uint16;
+ uint32_t uint32;
+ uint64_t uint64;
+ uint128_t uint128;
+ uuid_t uuid;
+ char *str;
+ sdp_data_t *dataseq;
+ } val;
+ sdp_data_t *next;
+ int unitSize;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SDP_H */
diff --git a/lib/sdp_lib.h b/lib/sdp_lib.h
new file mode 100644
index 0000000..e506ac1
--- /dev/null
+++ b/lib/sdp_lib.h
@@ -0,0 +1,631 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2001-2002 Nokia Corporation
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2002-2003 Stephen Crane <steve.crane@rococosoft.com>
+ *
+ *
+ * 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
+ *
+ */
+
+#ifndef __SDP_LIB_H
+#define __SDP_LIB_H
+
+#include <sys/socket.h>
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * SDP lists
+ */
+typedef void(*sdp_list_func_t)(void *, void *);
+typedef void(*sdp_free_func_t)(void *);
+typedef int (*sdp_comp_func_t)(const void *, const void *);
+
+sdp_list_t *sdp_list_append(sdp_list_t *list, void *d);
+sdp_list_t *sdp_list_remove(sdp_list_t *list, void *d);
+sdp_list_t *sdp_list_insert_sorted(sdp_list_t *list, void *data, sdp_comp_func_t f);
+void sdp_list_free(sdp_list_t *list, sdp_free_func_t f);
+
+static inline int sdp_list_len(const sdp_list_t *list)
+{
+ int n = 0;
+ for (; list; list = list->next)
+ n++;
+ return n;
+}
+
+static inline sdp_list_t *sdp_list_find(sdp_list_t *list, void *u, sdp_comp_func_t f)
+{
+ for (; list; list = list->next)
+ if (f(list->data, u) == 0)
+ return list;
+ return NULL;
+}
+
+static inline void sdp_list_foreach(sdp_list_t *list, sdp_list_func_t f, void *u)
+{
+ for (; list; list = list->next)
+ f(list->data, u);
+}
+
+/*
+ * Values of the flags parameter to sdp_record_register
+ */
+#define SDP_RECORD_PERSIST 0x01
+#define SDP_DEVICE_RECORD 0x02
+
+/*
+ * Values of the flags parameter to sdp_connect
+ */
+#define SDP_RETRY_IF_BUSY 0x01
+#define SDP_WAIT_ON_CLOSE 0x02
+#define SDP_NON_BLOCKING 0x04
+
+/*
+ * a session with an SDP server
+ */
+typedef struct {
+ int sock;
+ int state;
+ int local;
+ int flags;
+ uint16_t tid; // Current transaction ID
+ void *priv;
+} sdp_session_t;
+
+typedef enum {
+ /*
+ * Attributes are specified as individual elements
+ */
+ SDP_ATTR_REQ_INDIVIDUAL = 1,
+ /*
+ * Attributes are specified as a range
+ */
+ SDP_ATTR_REQ_RANGE
+} sdp_attrreq_type_t;
+
+/*
+ * When the pdu_id(type) is a sdp error response, check the status value
+ * to figure out the error reason. For status values 0x0001-0x0006 check
+ * Bluetooth SPEC. If the status is 0xffff, call sdp_get_error function
+ * to get the real reason:
+ * - wrong transaction ID(EPROTO)
+ * - wrong PDU id or(EPROTO)
+ * - I/O error
+ */
+typedef void sdp_callback_t(uint8_t type, uint16_t status, uint8_t *rsp, size_t size, void *udata);
+
+/*
+ * create an L2CAP connection to a Bluetooth device
+ *
+ * INPUT:
+ *
+ * bdaddr_t *src:
+ * Address of the local device to use to make the connection
+ * (or BDADDR_ANY)
+ *
+ * bdaddr_t *dst:
+ * Address of the SDP server device
+ */
+sdp_session_t *sdp_connect(const bdaddr_t *src, const bdaddr_t *dst, uint32_t flags);
+int sdp_close(sdp_session_t *session);
+int sdp_get_socket(const sdp_session_t *session);
+
+/*
+ * SDP transaction: functions for asynchronous search.
+ */
+sdp_session_t *sdp_create(int sk, uint32_t flags);
+int sdp_get_error(sdp_session_t *session);
+int sdp_process(sdp_session_t *session);
+int sdp_set_notify(sdp_session_t *session, sdp_callback_t *func, void *udata);
+
+int sdp_service_search_async(sdp_session_t *session, const sdp_list_t *search, uint16_t max_rec_num);
+int sdp_service_attr_async(sdp_session_t *session, uint32_t handle, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list);
+int sdp_service_search_attr_async(sdp_session_t *session, const sdp_list_t *search, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list);
+
+uint16_t sdp_gen_tid(sdp_session_t *session);
+
+/*
+ * find all devices in the piconet
+ */
+int sdp_general_inquiry(inquiry_info *ii, int dev_num, int duration, uint8_t *found);
+
+/* flexible extraction of basic attributes - Jean II */
+int sdp_get_int_attr(const sdp_record_t *rec, uint16_t attr, int *value);
+int sdp_get_string_attr(const sdp_record_t *rec, uint16_t attr, char *value, int valuelen);
+
+/*
+ * Basic sdp data functions
+ */
+sdp_data_t *sdp_data_alloc(uint8_t dtd, const void *value);
+sdp_data_t *sdp_data_alloc_with_length(uint8_t dtd, const void *value, uint32_t length);
+void sdp_data_free(sdp_data_t *data);
+sdp_data_t *sdp_data_get(const sdp_record_t *rec, uint16_t attr_id);
+
+sdp_data_t *sdp_seq_alloc(void **dtds, void **values, int len);
+sdp_data_t *sdp_seq_alloc_with_length(void **dtds, void **values, int *length, int len);
+sdp_data_t *sdp_seq_append(sdp_data_t *seq, sdp_data_t *data);
+
+int sdp_attr_add(sdp_record_t *rec, uint16_t attr, sdp_data_t *data);
+void sdp_attr_remove(sdp_record_t *rec, uint16_t attr);
+void sdp_attr_replace(sdp_record_t *rec, uint16_t attr, sdp_data_t *data);
+int sdp_set_uuidseq_attr(sdp_record_t *rec, uint16_t attr, sdp_list_t *seq);
+int sdp_get_uuidseq_attr(const sdp_record_t *rec, uint16_t attr, sdp_list_t **seqp);
+
+/*
+ * NOTE that none of the functions below will update the SDP server,
+ * unless the {register, update}sdp_record_t() function is invoked.
+ * All functions which return an integer value, return 0 on success
+ * or -1 on failure.
+ */
+
+/*
+ * Create an attribute and add it to the service record's attribute list.
+ * This consists of the data type descriptor of the attribute,
+ * the value of the attribute and the attribute identifier.
+ */
+int sdp_attr_add_new(sdp_record_t *rec, uint16_t attr, uint8_t dtd, const void *p);
+
+/*
+ * Set the information attributes of the service record.
+ * The set of attributes comprises service name, description
+ * and provider name
+ */
+void sdp_set_info_attr(sdp_record_t *rec, const char *name, const char *prov, const char *desc);
+
+/*
+ * Set the ServiceClassID attribute to the sequence specified by seq.
+ * Note that the identifiers need to be in sorted order from the most
+ * specific to the most generic service class that this service
+ * conforms to.
+ */
+static inline int sdp_set_service_classes(sdp_record_t *rec, sdp_list_t *seq)
+{
+ return sdp_set_uuidseq_attr(rec, SDP_ATTR_SVCLASS_ID_LIST, seq);
+}
+
+/*
+ * Get the service classes to which the service conforms.
+ *
+ * When set, the list contains elements of ServiceClassIdentifer(uint16_t)
+ * ordered from most specific to most generic
+ */
+static inline int sdp_get_service_classes(const sdp_record_t *rec, sdp_list_t **seqp)
+{
+ return sdp_get_uuidseq_attr(rec, SDP_ATTR_SVCLASS_ID_LIST, seqp);
+}
+
+/*
+ * Set the BrowseGroupList attribute to the list specified by seq.
+ *
+ * A service can belong to one or more service groups
+ * and the list comprises such group identifiers (UUIDs)
+ */
+static inline int sdp_set_browse_groups(sdp_record_t *rec, sdp_list_t *seq)
+{
+ return sdp_set_uuidseq_attr(rec, SDP_ATTR_BROWSE_GRP_LIST, seq);
+}
+
+/*
+ * Set the access protocols of the record to those specified in proto
+ */
+int sdp_set_access_protos(sdp_record_t *rec, const sdp_list_t *proto);
+
+/*
+ * Set the additional access protocols of the record to those specified in proto
+ */
+int sdp_set_add_access_protos(sdp_record_t *rec, const sdp_list_t *proto);
+
+/*
+ * Get protocol port (i.e. PSM for L2CAP, Channel for RFCOMM)
+ */
+int sdp_get_proto_port(const sdp_list_t *list, int proto);
+
+/*
+ * Get protocol descriptor.
+ */
+sdp_data_t *sdp_get_proto_desc(sdp_list_t *list, int proto);
+
+/*
+ * Set the LanguageBase attributes to the values specified in list
+ * (a linked list of sdp_lang_attr_t objects, one for each language in
+ * which user-visible attributes are present).
+ */
+int sdp_set_lang_attr(sdp_record_t *rec, const sdp_list_t *list);
+
+/*
+ * Set the ServiceInfoTimeToLive attribute of the service.
+ * This is the number of seconds that this record is guaranteed
+ * not to change after being obtained by a client.
+ */
+static inline int sdp_set_service_ttl(sdp_record_t *rec, uint32_t ttl)
+{
+ return sdp_attr_add_new(rec, SDP_ATTR_SVCINFO_TTL, SDP_UINT32, &ttl);
+}
+
+/*
+ * Set the ServiceRecordState attribute of a service. This is
+ * guaranteed to change if there is any kind of modification to
+ * the record.
+ */
+static inline int sdp_set_record_state(sdp_record_t *rec, uint32_t state)
+{
+ return sdp_attr_add_new(rec, SDP_ATTR_RECORD_STATE, SDP_UINT32, &state);
+}
+
+/*
+ * Set the ServiceID attribute of a service.
+ */
+void sdp_set_service_id(sdp_record_t *rec, uuid_t uuid);
+
+/*
+ * Set the GroupID attribute of a service
+ */
+void sdp_set_group_id(sdp_record_t *rec, uuid_t grouuuid);
+
+/*
+ * Set the ServiceAvailability attribute of a service.
+ *
+ * Note that this represents the relative availability
+ * of the service: 0x00 means completely unavailable;
+ * 0xFF means maximum availability.
+ */
+static inline int sdp_set_service_avail(sdp_record_t *rec, uint8_t avail)
+{
+ return sdp_attr_add_new(rec, SDP_ATTR_SERVICE_AVAILABILITY, SDP_UINT8, &avail);
+}
+
+/*
+ * Set the profile descriptor list attribute of a record.
+ *
+ * Each element in the list is an object of type
+ * sdp_profile_desc_t which is a definition of the
+ * Bluetooth profile that this service conforms to.
+ */
+int sdp_set_profile_descs(sdp_record_t *rec, const sdp_list_t *desc);
+
+/*
+ * Set URL attributes of a record.
+ *
+ * ClientExecutableURL: a URL to a client's platform specific (WinCE,
+ * PalmOS) executable code that can be used to access this service.
+ *
+ * DocumentationURL: a URL pointing to service documentation
+ *
+ * IconURL: a URL to an icon that can be used to represent this service.
+ *
+ * Note: pass NULL for any URLs that you don't want to set or remove
+ */
+void sdp_set_url_attr(sdp_record_t *rec, const char *clientExecURL, const char *docURL, const char *iconURL);
+
+/*
+ * a service search request.
+ *
+ * INPUT :
+ *
+ * sdp_list_t *search
+ * list containing elements of the search
+ * pattern. Each entry in the list is a UUID
+ * of the service to be searched
+ *
+ * uint16_t max_rec_num
+ * An integer specifying the maximum number of
+ * entries that the client can handle in the response.
+ *
+ * OUTPUT :
+ *
+ * int return value
+ * 0
+ * The request completed successfully. This does not
+ * mean the requested services were found
+ * -1
+ * The request completed unsuccessfully
+ *
+ * sdp_list_t *rsp_list
+ * This variable is set on a successful return if there are
+ * non-zero service handles. It is a singly linked list of
+ * service record handles (uint16_t)
+ */
+int sdp_service_search_req(sdp_session_t *session, const sdp_list_t *search, uint16_t max_rec_num, sdp_list_t **rsp_list);
+
+/*
+ * a service attribute request.
+ *
+ * INPUT :
+ *
+ * uint32_t handle
+ * The handle of the service for which the attribute(s) are
+ * requested
+ *
+ * sdp_attrreq_type_t reqtype
+ * Attribute identifiers are 16 bit unsigned integers specified
+ * in one of 2 ways described below :
+ * SDP_ATTR_REQ_INDIVIDUAL - 16bit individual identifiers
+ * They are the actual attribute identifiers in ascending order
+ *
+ * SDP_ATTR_REQ_RANGE - 32bit identifier range
+ * The high-order 16bits is the start of range
+ * the low-order 16bits are the end of range
+ * 0x0000 to 0xFFFF gets all attributes
+ *
+ * sdp_list_t *attrid_list
+ * Singly linked list containing attribute identifiers desired.
+ * Every element is either a uint16_t(attrSpec = SDP_ATTR_REQ_INDIVIDUAL)
+ * or a uint32_t(attrSpec=SDP_ATTR_REQ_RANGE)
+ *
+ * OUTPUT :
+ * int return value
+ * 0
+ * The request completed successfully. This does not
+ * mean the requested services were found
+ * -1
+ * The request completed unsuccessfully due to a timeout
+ */
+sdp_record_t *sdp_service_attr_req(sdp_session_t *session, uint32_t handle, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list);
+
+/*
+ * This is a service search request combined with the service
+ * attribute request. First a service class match is done and
+ * for matching service, requested attributes are extracted
+ *
+ * INPUT :
+ *
+ * sdp_list_t *search
+ * Singly linked list containing elements of the search
+ * pattern. Each entry in the list is a UUID(DataTypeSDP_UUID16)
+ * of the service to be searched
+ *
+ * AttributeSpecification attrSpec
+ * Attribute identifiers are 16 bit unsigned integers specified
+ * in one of 2 ways described below :
+ * SDP_ATTR_REQ_INDIVIDUAL - 16bit individual identifiers
+ * They are the actual attribute identifiers in ascending order
+ *
+ * SDP_ATTR_REQ_RANGE - 32bit identifier range
+ * The high-order 16bits is the start of range
+ * the low-order 16bits are the end of range
+ * 0x0000 to 0xFFFF gets all attributes
+ *
+ * sdp_list_t *attrid_list
+ * Singly linked list containing attribute identifiers desired.
+ * Every element is either a uint16_t(attrSpec = SDP_ATTR_REQ_INDIVIDUAL)
+ * or a uint32_t(attrSpec=SDP_ATTR_REQ_RANGE)
+ *
+ * OUTPUT :
+ * int return value
+ * 0
+ * The request completed successfully. This does not
+ * mean the requested services were found
+ * -1
+ * The request completed unsuccessfully due to a timeout
+ *
+ * sdp_list_t *rsp_list
+ * This variable is set on a successful return to point to
+ * service(s) found. Each element of this list is of type
+ * sdp_record_t *.
+ */
+int sdp_service_search_attr_req(sdp_session_t *session, const sdp_list_t *search, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list, sdp_list_t **rsp_list);
+
+/*
+ * Allocate/free a service record and its attributes
+ */
+sdp_record_t *sdp_record_alloc(void);
+void sdp_record_free(sdp_record_t *rec);
+
+/*
+ * Register a service record.
+ *
+ * Note: It is the responsbility of the Service Provider to create the
+ * record first and set its attributes using setXXX() methods.
+ *
+ * The service provider must then call sdp_record_register() to make
+ * the service record visible to SDP clients. This function returns 0
+ * on success or -1 on failure (and sets errno).
+ */
+int sdp_device_record_register_binary(sdp_session_t *session, bdaddr_t *device, uint8_t *data, uint32_t size, uint8_t flags, uint32_t *handle);
+int sdp_device_record_register(sdp_session_t *session, bdaddr_t *device, sdp_record_t *rec, uint8_t flags);
+int sdp_record_register(sdp_session_t *session, sdp_record_t *rec, uint8_t flags);
+
+/*
+ * Unregister a service record.
+ */
+int sdp_device_record_unregister_binary(sdp_session_t *session, bdaddr_t *device, uint32_t handle);
+int sdp_device_record_unregister(sdp_session_t *session, bdaddr_t *device, sdp_record_t *rec);
+int sdp_record_unregister(sdp_session_t *session, sdp_record_t *rec);
+
+/*
+ * Update an existing service record. (Calling this function
+ * before a previous call to sdp_record_register() will result
+ * in an error.)
+ */
+int sdp_device_record_update_binary(sdp_session_t *session, bdaddr_t *device, uint32_t handle, uint8_t *data, uint32_t size);
+int sdp_device_record_update(sdp_session_t *session, bdaddr_t *device, const sdp_record_t *rec);
+int sdp_record_update(sdp_session_t *sess, const sdp_record_t *rec);
+
+void sdp_record_print(const sdp_record_t *rec);
+
+/*
+ * UUID functions
+ */
+uuid_t *sdp_uuid16_create(uuid_t *uuid, uint16_t data);
+uuid_t *sdp_uuid32_create(uuid_t *uuid, uint32_t data);
+uuid_t *sdp_uuid128_create(uuid_t *uuid, const void *data);
+int sdp_uuid16_cmp(const void *p1, const void *p2);
+int sdp_uuid128_cmp(const void *p1, const void *p2);
+int sdp_uuid_cmp(const void *p1, const void *p2);
+uuid_t *sdp_uuid_to_uuid128(const uuid_t *uuid);
+void sdp_uuid16_to_uuid128(uuid_t *uuid128, const uuid_t *uuid16);
+void sdp_uuid32_to_uuid128(uuid_t *uuid128, const uuid_t *uuid32);
+int sdp_uuid128_to_uuid(uuid_t *uuid);
+int sdp_uuid_to_proto(uuid_t *uuid);
+int sdp_uuid_extract(const uint8_t *buffer, int bufsize, uuid_t *uuid, int *scanned);
+void sdp_uuid_print(const uuid_t *uuid);
+
+#define MAX_LEN_UUID_STR 37
+#define MAX_LEN_PROTOCOL_UUID_STR 8
+#define MAX_LEN_SERVICECLASS_UUID_STR 28
+#define MAX_LEN_PROFILEDESCRIPTOR_UUID_STR 28
+
+int sdp_uuid2strn(const uuid_t *uuid, char *str, size_t n);
+int sdp_proto_uuid2strn(const uuid_t *uuid, char *str, size_t n);
+int sdp_svclass_uuid2strn(const uuid_t *uuid, char *str, size_t n);
+int sdp_profile_uuid2strn(const uuid_t *uuid, char *str, size_t n);
+
+/*
+ * In all the sdp_get_XXX(handle, XXX *xxx) functions below,
+ * the XXX * is set to point to the value, should it exist
+ * and 0 is returned. If the value does not exist, -1 is
+ * returned and errno set to ENODATA.
+ *
+ * In all the methods below, the memory management rules are
+ * simple. Don't free anything! The pointer returned, in the
+ * case of constructed types, is a pointer to the contents
+ * of the sdp_record_t.
+ */
+
+/*
+ * Get the access protocols from the service record
+ */
+int sdp_get_access_protos(const sdp_record_t *rec, sdp_list_t **protos);
+
+/*
+ * Get the additional access protocols from the service record
+ */
+int sdp_get_add_access_protos(const sdp_record_t *rec, sdp_list_t **protos);
+
+/*
+ * Extract the list of browse groups to which the service belongs.
+ * When set, seqp contains elements of GroupID (uint16_t)
+ */
+static inline int sdp_get_browse_groups(const sdp_record_t *rec, sdp_list_t **seqp)
+{
+ return sdp_get_uuidseq_attr(rec, SDP_ATTR_BROWSE_GRP_LIST, seqp);
+}
+
+/*
+ * Extract language attribute meta-data of the service record.
+ * For each language in the service record, LangSeq has a struct of type
+ * sdp_lang_attr_t.
+ */
+int sdp_get_lang_attr(const sdp_record_t *rec, sdp_list_t **langSeq);
+
+/*
+ * Extract the Bluetooth profile descriptor sequence from a record.
+ * Each element in the list is of type sdp_profile_desc_t
+ * which contains the UUID of the profile and its version number
+ * (encoded as major and minor in the high-order 8bits
+ * and low-order 8bits respectively of the uint16_t)
+ */
+int sdp_get_profile_descs(const sdp_record_t *rec, sdp_list_t **profDesc);
+
+/*
+ * Extract SDP server version numbers
+ *
+ * Note: that this is an attribute of the SDP server only and
+ * contains a list of uint16_t each of which represent the
+ * major and minor SDP version numbers supported by this server
+ */
+int sdp_get_server_ver(const sdp_record_t *rec, sdp_list_t **pVnumList);
+
+int sdp_get_service_id(const sdp_record_t *rec, uuid_t *uuid);
+int sdp_get_group_id(const sdp_record_t *rec, uuid_t *uuid);
+int sdp_get_record_state(const sdp_record_t *rec, uint32_t *svcRecState);
+int sdp_get_service_avail(const sdp_record_t *rec, uint8_t *svcAvail);
+int sdp_get_service_ttl(const sdp_record_t *rec, uint32_t *svcTTLInfo);
+int sdp_get_database_state(const sdp_record_t *rec, uint32_t *svcDBState);
+
+static inline int sdp_get_service_name(const sdp_record_t *rec, char *str, int len)
+{
+ return sdp_get_string_attr(rec, SDP_ATTR_SVCNAME_PRIMARY, str, len);
+}
+
+static inline int sdp_get_service_desc(const sdp_record_t *rec, char *str, int len)
+{
+ return sdp_get_string_attr(rec, SDP_ATTR_SVCDESC_PRIMARY, str, len);
+}
+
+static inline int sdp_get_provider_name(const sdp_record_t *rec, char *str, int len)
+{
+ return sdp_get_string_attr(rec, SDP_ATTR_PROVNAME_PRIMARY, str, len);
+}
+
+static inline int sdp_get_doc_url(const sdp_record_t *rec, char *str, int len)
+{
+ return sdp_get_string_attr(rec, SDP_ATTR_DOC_URL, str, len);
+}
+
+static inline int sdp_get_clnt_exec_url(const sdp_record_t *rec, char *str, int len)
+{
+ return sdp_get_string_attr(rec, SDP_ATTR_CLNT_EXEC_URL, str, len);
+}
+
+static inline int sdp_get_icon_url(const sdp_record_t *rec, char *str, int len)
+{
+ return sdp_get_string_attr(rec, SDP_ATTR_ICON_URL, str, len);
+}
+
+/*
+ * Set the supported features
+ * sf should be a list of list with each feature data
+ * Returns 0 on success -1 on fail
+ */
+int sdp_set_supp_feat(sdp_record_t *rec, const sdp_list_t *sf);
+
+/*
+ * Get the supported features
+ * seqp is set to a list of list with each feature data
+ * Returns 0 on success, if an error occurred -1 is returned and errno is set
+ */
+int sdp_get_supp_feat(const sdp_record_t *rec, sdp_list_t **seqp);
+
+sdp_record_t *sdp_extract_pdu(const uint8_t *pdata, int bufsize, int *scanned);
+sdp_record_t *sdp_copy_record(sdp_record_t *rec);
+
+void sdp_data_print(sdp_data_t *data);
+void sdp_print_service_attr(sdp_list_t *alist);
+
+int sdp_attrid_comp_func(const void *key1, const void *key2);
+
+void sdp_set_seq_len(uint8_t *ptr, uint32_t length);
+void sdp_set_attrid(sdp_buf_t *pdu, uint16_t id);
+void sdp_append_to_pdu(sdp_buf_t *dst, sdp_data_t *d);
+void sdp_append_to_buf(sdp_buf_t *dst, uint8_t *data, uint32_t len);
+
+int sdp_gen_pdu(sdp_buf_t *pdu, sdp_data_t *data);
+int sdp_gen_record_pdu(const sdp_record_t *rec, sdp_buf_t *pdu);
+
+int sdp_extract_seqtype(const uint8_t *buf, int bufsize, uint8_t *dtdp, int *size);
+
+sdp_data_t *sdp_extract_attr(const uint8_t *pdata, int bufsize, int *extractedLength, sdp_record_t *rec);
+
+void sdp_pattern_add_uuid(sdp_record_t *rec, uuid_t *uuid);
+void sdp_pattern_add_uuidseq(sdp_record_t *rec, sdp_list_t *seq);
+
+int sdp_send_req_w4_rsp(sdp_session_t *session, uint8_t *req, uint8_t *rsp, uint32_t reqsize, uint32_t *rspsize);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SDP_LIB_H */
diff --git a/lib/uuid.c b/lib/uuid.c
new file mode 100644
index 0000000..325016a
--- /dev/null
+++ b/lib/uuid.c
@@ -0,0 +1,273 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 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 <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "uuid.h"
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+static uint128_t bluetooth_base_uuid = {
+ .data = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB }
+};
+
+#define BASE_UUID16_OFFSET 2
+#define BASE_UUID32_OFFSET 0
+
+#else
+static uint128_t bluetooth_base_uuid = {
+ .data = { 0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80,
+ 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
+};
+
+#define BASE_UUID16_OFFSET 12
+#define BASE_UUID32_OFFSET BASE_UUID16_OFFSET
+
+#endif
+
+static void bt_uuid16_to_uuid128(const bt_uuid_t *src, bt_uuid_t *dst)
+{
+ dst->value.u128 = bluetooth_base_uuid;
+ dst->type = BT_UUID128;
+
+ memcpy(&dst->value.u128.data[BASE_UUID16_OFFSET],
+ &src->value.u16, sizeof(src->value.u16));
+}
+
+static void bt_uuid32_to_uuid128(const bt_uuid_t *src, bt_uuid_t *dst)
+{
+ dst->value.u128 = bluetooth_base_uuid;
+ dst->type = BT_UUID128;
+
+ memcpy(&dst->value.u128.data[BASE_UUID32_OFFSET],
+ &src->value.u32, sizeof(src->value.u32));
+}
+
+void bt_uuid_to_uuid128(const bt_uuid_t *src, bt_uuid_t *dst)
+{
+ switch (src->type) {
+ case BT_UUID128:
+ memcpy(dst, src, sizeof(bt_uuid_t));
+ break;
+ case BT_UUID32:
+ bt_uuid32_to_uuid128(src, dst);
+ break;
+ case BT_UUID16:
+ bt_uuid16_to_uuid128(src, dst);
+ break;
+ default:
+ break;
+ }
+}
+
+static int bt_uuid128_cmp(const bt_uuid_t *u1, const bt_uuid_t *u2)
+{
+ return memcmp(&u1->value.u128, &u2->value.u128, sizeof(uint128_t));
+}
+
+int bt_uuid16_create(bt_uuid_t *btuuid, uint16_t value)
+{
+ memset(btuuid, 0, sizeof(bt_uuid_t));
+ btuuid->type = BT_UUID16;
+ btuuid->value.u16 = value;
+
+ return 0;
+}
+
+int bt_uuid32_create(bt_uuid_t *btuuid, uint32_t value)
+{
+ memset(btuuid, 0, sizeof(bt_uuid_t));
+ btuuid->type = BT_UUID32;
+ btuuid->value.u32 = value;
+
+ return 0;
+}
+
+int bt_uuid128_create(bt_uuid_t *btuuid, uint128_t value)
+{
+ memset(btuuid, 0, sizeof(bt_uuid_t));
+ btuuid->type = BT_UUID128;
+ btuuid->value.u128 = value;
+
+ return 0;
+}
+
+int bt_uuid_cmp(const bt_uuid_t *uuid1, const bt_uuid_t *uuid2)
+{
+ bt_uuid_t u1, u2;
+
+ bt_uuid_to_uuid128(uuid1, &u1);
+ bt_uuid_to_uuid128(uuid2, &u2);
+
+ return bt_uuid128_cmp(&u1, &u2);
+}
+
+/*
+ * convert the UUID to string, copying a maximum of n characters.
+ */
+int bt_uuid_to_string(const bt_uuid_t *uuid, char *str, size_t n)
+{
+ if (!uuid) {
+ snprintf(str, n, "NULL");
+ return -EINVAL;
+ }
+
+ switch (uuid->type) {
+ case BT_UUID16:
+ snprintf(str, n, "%.4x", uuid->value.u16);
+ break;
+ case BT_UUID32:
+ snprintf(str, n, "%.8x", uuid->value.u32);
+ break;
+ case BT_UUID128: {
+ unsigned int data0;
+ unsigned short data1;
+ unsigned short data2;
+ unsigned short data3;
+ unsigned int data4;
+ unsigned short data5;
+
+ uint128_t nvalue;
+ const uint8_t *data = (uint8_t *) &nvalue;
+
+ hton128(&uuid->value.u128, &nvalue);
+
+ memcpy(&data0, &data[0], 4);
+ memcpy(&data1, &data[4], 2);
+ memcpy(&data2, &data[6], 2);
+ memcpy(&data3, &data[8], 2);
+ memcpy(&data4, &data[10], 4);
+ memcpy(&data5, &data[14], 2);
+
+ snprintf(str, n, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x",
+ ntohl(data0), ntohs(data1),
+ ntohs(data2), ntohs(data3),
+ ntohl(data4), ntohs(data5));
+ }
+ break;
+ default:
+ snprintf(str, n, "Type of UUID (%x) unknown.", uuid->type);
+ return -EINVAL; /* Enum type of UUID not set */
+ }
+
+ return 0;
+}
+
+static inline int is_uuid128(const char *string)
+{
+ return (strlen(string) == 36 &&
+ string[8] == '-' &&
+ string[13] == '-' &&
+ string[18] == '-' &&
+ string[23] == '-');
+}
+
+static inline int is_uuid32(const char *string)
+{
+ return (strlen(string) == 8 || strlen(string) == 10);
+}
+
+static inline int is_uuid16(const char *string)
+{
+ return (strlen(string) == 4 || strlen(string) == 6);
+}
+
+static int bt_string_to_uuid16(bt_uuid_t *uuid, const char *string)
+{
+ uint16_t u16;
+ char *endptr = NULL;
+
+ u16 = strtol(string, &endptr, 16);
+ if (endptr && *endptr == '\0') {
+ bt_uuid16_create(uuid, u16);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int bt_string_to_uuid32(bt_uuid_t *uuid, const char *string)
+{
+ uint32_t u32;
+ char *endptr = NULL;
+
+ u32 = strtol(string, &endptr, 16);
+ if (endptr && *endptr == '\0') {
+ bt_uuid32_create(uuid, u32);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int bt_string_to_uuid128(bt_uuid_t *uuid, const char *string)
+{
+ uint32_t data0, data4;
+ uint16_t data1, data2, data3, data5;
+ uint128_t n128, u128;
+ uint8_t *val = (uint8_t *) &n128;
+
+ if (sscanf(string, "%08x-%04hx-%04hx-%04hx-%08x%04hx",
+ &data0, &data1, &data2,
+ &data3, &data4, &data5) != 6)
+ return -EINVAL;
+
+ data0 = htonl(data0);
+ data1 = htons(data1);
+ data2 = htons(data2);
+ data3 = htons(data3);
+ data4 = htonl(data4);
+ data5 = htons(data5);
+
+ memcpy(&val[0], &data0, 4);
+ memcpy(&val[4], &data1, 2);
+ memcpy(&val[6], &data2, 2);
+ memcpy(&val[8], &data3, 2);
+ memcpy(&val[10], &data4, 4);
+ memcpy(&val[14], &data5, 2);
+
+ ntoh128(&n128, &u128);
+
+ bt_uuid128_create(uuid, u128);
+
+ return 0;
+}
+
+int bt_string_to_uuid(bt_uuid_t *uuid, const char *string)
+{
+ if (is_uuid128(string))
+ return bt_string_to_uuid128(uuid, string);
+ else if (is_uuid32(string))
+ return bt_string_to_uuid32(uuid, string);
+ else if (is_uuid16(string))
+ return bt_string_to_uuid16(uuid, string);
+
+ return -EINVAL;
+}
diff --git a/lib/uuid.h b/lib/uuid.h
new file mode 100644
index 0000000..9c082d3
--- /dev/null
+++ b/lib/uuid.h
@@ -0,0 +1,65 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 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
+ *
+ */
+
+#ifndef __BLUETOOTH_UUID_H
+#define __BLUETOOTH_UUID_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <bluetooth/bluetooth.h>
+
+typedef struct {
+ enum {
+ BT_UUID_UNSPEC = 0,
+ BT_UUID16 = 16,
+ BT_UUID32 = 32,
+ BT_UUID128 = 128,
+ } type;
+ union {
+ uint16_t u16;
+ uint32_t u32;
+ uint128_t u128;
+ } value;
+} bt_uuid_t;
+
+int bt_uuid16_create(bt_uuid_t *btuuid, uint16_t value);
+int bt_uuid32_create(bt_uuid_t *btuuid, uint32_t value);
+int bt_uuid128_create(bt_uuid_t *btuuid, uint128_t value);
+
+int bt_uuid_cmp(const bt_uuid_t *uuid1, const bt_uuid_t *uuid2);
+void bt_uuid_to_uuid128(const bt_uuid_t *src, bt_uuid_t *dst);
+
+#define MAX_LEN_UUID_STR 37
+
+int bt_uuid_to_string(const bt_uuid_t *uuid, char *str, size_t n);
+int bt_string_to_uuid(bt_uuid_t *uuid, const char *string);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __BLUETOOTH_UUID_H */
diff --git a/ltmain.sh b/ltmain.sh
new file mode 100755
index 0000000..a72f2fd
--- /dev/null
+++ b/ltmain.sh
@@ -0,0 +1,8406 @@
+# Generated from ltmain.m4sh.
+
+# ltmain.sh (GNU libtool) 2.2.6b
+# Written by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
+
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007 2008 Free Software Foundation, Inc.
+# This is free software; see the source for copying conditions. There is NO
+# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+# GNU Libtool 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.
+#
+# As a special exception to the GNU General Public License,
+# if you distribute this file as part of a program or library that
+# is built using GNU Libtool, you may include this file under the
+# same distribution terms that you use for the rest of that program.
+#
+# GNU Libtool 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 GNU Libtool; see the file COPYING. If not, a copy
+# can be downloaded from http://www.gnu.org/licenses/gpl.html,
+# or obtained by writing to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+# Usage: $progname [OPTION]... [MODE-ARG]...
+#
+# Provide generalized library-building support services.
+#
+# --config show all configuration variables
+# --debug enable verbose shell tracing
+# -n, --dry-run display commands without modifying any files
+# --features display basic configuration information and exit
+# --mode=MODE use operation mode MODE
+# --preserve-dup-deps don't remove duplicate dependency libraries
+# --quiet, --silent don't print informational messages
+# --tag=TAG use configuration variables from tag TAG
+# -v, --verbose print informational messages (default)
+# --version print version information
+# -h, --help print short or long help message
+#
+# MODE must be one of the following:
+#
+# clean remove files from the build directory
+# compile compile a source file into a libtool object
+# execute automatically set library path, then run a program
+# finish complete the installation of libtool libraries
+# install install libraries or executables
+# link create a library or an executable
+# uninstall remove libraries from an installed directory
+#
+# MODE-ARGS vary depending on the MODE.
+# Try `$progname --help --mode=MODE' for a more detailed description of MODE.
+#
+# When reporting a bug, please describe a test case to reproduce it and
+# include the following information:
+#
+# host-triplet: $host
+# shell: $SHELL
+# compiler: $LTCC
+# compiler flags: $LTCFLAGS
+# linker: $LD (gnu? $with_gnu_ld)
+# $progname: (GNU libtool) 2.2.6b
+# automake: $automake_version
+# autoconf: $autoconf_version
+#
+# Report bugs to <bug-libtool@gnu.org>.
+
+PROGRAM=ltmain.sh
+PACKAGE=libtool
+VERSION=2.2.6b
+TIMESTAMP=""
+package_revision=1.3017
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac
+fi
+BIN_SH=xpg4; export BIN_SH # for Tru64
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# NLS nuisances: We save the old values to restore during execute mode.
+# Only set LANG and LC_ALL to C if already set.
+# These must not be set unconditionally because not all systems understand
+# e.g. LANG=C (notably SCO).
+lt_user_locale=
+lt_safe_locale=
+for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES
+do
+ eval "if test \"\${$lt_var+set}\" = set; then
+ save_$lt_var=\$$lt_var
+ $lt_var=C
+ export $lt_var
+ lt_user_locale=\"$lt_var=\\\$save_\$lt_var; \$lt_user_locale\"
+ lt_safe_locale=\"$lt_var=C; \$lt_safe_locale\"
+ fi"
+done
+
+$lt_unset CDPATH
+
+
+
+
+
+: ${CP="cp -f"}
+: ${ECHO="echo"}
+: ${EGREP="/bin/grep -E"}
+: ${FGREP="/bin/grep -F"}
+: ${GREP="/bin/grep"}
+: ${LN_S="ln -s"}
+: ${MAKE="make"}
+: ${MKDIR="mkdir"}
+: ${MV="mv -f"}
+: ${RM="rm -f"}
+: ${SED="/bin/sed"}
+: ${SHELL="${CONFIG_SHELL-/bin/sh}"}
+: ${Xsed="$SED -e 1s/^X//"}
+
+# Global variables:
+EXIT_SUCCESS=0
+EXIT_FAILURE=1
+EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing.
+EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake.
+
+exit_status=$EXIT_SUCCESS
+
+# Make sure IFS has a sensible default
+lt_nl='
+'
+IFS=" $lt_nl"
+
+dirname="s,/[^/]*$,,"
+basename="s,^.*/,,"
+
+# func_dirname_and_basename file append nondir_replacement
+# perform func_basename and func_dirname in a single function
+# call:
+# dirname: Compute the dirname of FILE. If nonempty,
+# add APPEND to the result, otherwise set result
+# to NONDIR_REPLACEMENT.
+# value returned in "$func_dirname_result"
+# basename: Compute filename of FILE.
+# value retuned in "$func_basename_result"
+# Implementation must be kept synchronized with func_dirname
+# and func_basename. For efficiency, we do not delegate to
+# those functions but instead duplicate the functionality here.
+func_dirname_and_basename ()
+{
+ # Extract subdirectory from the argument.
+ func_dirname_result=`$ECHO "X${1}" | $Xsed -e "$dirname"`
+ if test "X$func_dirname_result" = "X${1}"; then
+ func_dirname_result="${3}"
+ else
+ func_dirname_result="$func_dirname_result${2}"
+ fi
+ func_basename_result=`$ECHO "X${1}" | $Xsed -e "$basename"`
+}
+
+# Generated shell functions inserted here.
+
+# Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh
+# is ksh but when the shell is invoked as "sh" and the current value of
+# the _XPG environment variable is not equal to 1 (one), the special
+# positional parameter $0, within a function call, is the name of the
+# function.
+progpath="$0"
+
+# The name of this program:
+# In the unlikely event $progname began with a '-', it would play havoc with
+# func_echo (imagine progname=-n), so we prepend ./ in that case:
+func_dirname_and_basename "$progpath"
+progname=$func_basename_result
+case $progname in
+ -*) progname=./$progname ;;
+esac
+
+# Make sure we have an absolute path for reexecution:
+case $progpath in
+ [\\/]*|[A-Za-z]:\\*) ;;
+ *[\\/]*)
+ progdir=$func_dirname_result
+ progdir=`cd "$progdir" && pwd`
+ progpath="$progdir/$progname"
+ ;;
+ *)
+ save_IFS="$IFS"
+ IFS=:
+ for progdir in $PATH; do
+ IFS="$save_IFS"
+ test -x "$progdir/$progname" && break
+ done
+ IFS="$save_IFS"
+ test -n "$progdir" || progdir=`pwd`
+ progpath="$progdir/$progname"
+ ;;
+esac
+
+# Sed substitution that helps us do robust quoting. It backslashifies
+# metacharacters that are still active within double-quoted strings.
+Xsed="${SED}"' -e 1s/^X//'
+sed_quote_subst='s/\([`"$\\]\)/\\\1/g'
+
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\(["`\\]\)/\\\1/g'
+
+# Re-`\' parameter expansions in output of double_quote_subst that were
+# `\'-ed in input to the same. If an odd number of `\' preceded a '$'
+# in input to double_quote_subst, that '$' was protected from expansion.
+# Since each input `\' is now two `\'s, look for any number of runs of
+# four `\'s followed by two `\'s and then a '$'. `\' that '$'.
+bs='\\'
+bs2='\\\\'
+bs4='\\\\\\\\'
+dollar='\$'
+sed_double_backslash="\
+ s/$bs4/&\\
+/g
+ s/^$bs2$dollar/$bs&/
+ s/\\([^$bs]\\)$bs2$dollar/\\1$bs2$bs$dollar/g
+ s/\n//g"
+
+# Standard options:
+opt_dry_run=false
+opt_help=false
+opt_quiet=false
+opt_verbose=false
+opt_warning=:
+
+# func_echo arg...
+# Echo program name prefixed message, along with the current mode
+# name if it has been set yet.
+func_echo ()
+{
+ $ECHO "$progname${mode+: }$mode: $*"
+}
+
+# func_verbose arg...
+# Echo program name prefixed message in verbose mode only.
+func_verbose ()
+{
+ $opt_verbose && func_echo ${1+"$@"}
+
+ # A bug in bash halts the script if the last line of a function
+ # fails when set -e is in force, so we need another command to
+ # work around that:
+ :
+}
+
+# func_error arg...
+# Echo program name prefixed message to standard error.
+func_error ()
+{
+ $ECHO "$progname${mode+: }$mode: "${1+"$@"} 1>&2
+}
+
+# func_warning arg...
+# Echo program name prefixed warning message to standard error.
+func_warning ()
+{
+ $opt_warning && $ECHO "$progname${mode+: }$mode: warning: "${1+"$@"} 1>&2
+
+ # bash bug again:
+ :
+}
+
+# func_fatal_error arg...
+# Echo program name prefixed message to standard error, and exit.
+func_fatal_error ()
+{
+ func_error ${1+"$@"}
+ exit $EXIT_FAILURE
+}
+
+# func_fatal_help arg...
+# Echo program name prefixed message to standard error, followed by
+# a help hint, and exit.
+func_fatal_help ()
+{
+ func_error ${1+"$@"}
+ func_fatal_error "$help"
+}
+help="Try \`$progname --help' for more information." ## default
+
+
+# func_grep expression filename
+# Check whether EXPRESSION matches any line of FILENAME, without output.
+func_grep ()
+{
+ $GREP "$1" "$2" >/dev/null 2>&1
+}
+
+
+# func_mkdir_p directory-path
+# Make sure the entire path to DIRECTORY-PATH is available.
+func_mkdir_p ()
+{
+ my_directory_path="$1"
+ my_dir_list=
+
+ if test -n "$my_directory_path" && test "$opt_dry_run" != ":"; then
+
+ # Protect directory names starting with `-'
+ case $my_directory_path in
+ -*) my_directory_path="./$my_directory_path" ;;
+ esac
+
+ # While some portion of DIR does not yet exist...
+ while test ! -d "$my_directory_path"; do
+ # ...make a list in topmost first order. Use a colon delimited
+ # list incase some portion of path contains whitespace.
+ my_dir_list="$my_directory_path:$my_dir_list"
+
+ # If the last portion added has no slash in it, the list is done
+ case $my_directory_path in */*) ;; *) break ;; esac
+
+ # ...otherwise throw away the child directory and loop
+ my_directory_path=`$ECHO "X$my_directory_path" | $Xsed -e "$dirname"`
+ done
+ my_dir_list=`$ECHO "X$my_dir_list" | $Xsed -e 's,:*$,,'`
+
+ save_mkdir_p_IFS="$IFS"; IFS=':'
+ for my_dir in $my_dir_list; do
+ IFS="$save_mkdir_p_IFS"
+ # mkdir can fail with a `File exist' error if two processes
+ # try to create one of the directories concurrently. Don't
+ # stop in that case!
+ $MKDIR "$my_dir" 2>/dev/null || :
+ done
+ IFS="$save_mkdir_p_IFS"
+
+ # Bail out if we (or some other process) failed to create a directory.
+ test -d "$my_directory_path" || \
+ func_fatal_error "Failed to create \`$1'"
+ fi
+}
+
+
+# func_mktempdir [string]
+# Make a temporary directory that won't clash with other running
+# libtool processes, and avoids race conditions if possible. If
+# given, STRING is the basename for that directory.
+func_mktempdir ()
+{
+ my_template="${TMPDIR-/tmp}/${1-$progname}"
+
+ if test "$opt_dry_run" = ":"; then
+ # Return a directory name, but don't create it in dry-run mode
+ my_tmpdir="${my_template}-$$"
+ else
+
+ # If mktemp works, use that first and foremost
+ my_tmpdir=`mktemp -d "${my_template}-XXXXXXXX" 2>/dev/null`
+
+ if test ! -d "$my_tmpdir"; then
+ # Failing that, at least try and use $RANDOM to avoid a race
+ my_tmpdir="${my_template}-${RANDOM-0}$$"
+
+ save_mktempdir_umask=`umask`
+ umask 0077
+ $MKDIR "$my_tmpdir"
+ umask $save_mktempdir_umask
+ fi
+
+ # If we're not in dry-run mode, bomb out on failure
+ test -d "$my_tmpdir" || \
+ func_fatal_error "cannot create temporary directory \`$my_tmpdir'"
+ fi
+
+ $ECHO "X$my_tmpdir" | $Xsed
+}
+
+
+# func_quote_for_eval arg
+# Aesthetically quote ARG to be evaled later.
+# This function returns two values: FUNC_QUOTE_FOR_EVAL_RESULT
+# is double-quoted, suitable for a subsequent eval, whereas
+# FUNC_QUOTE_FOR_EVAL_UNQUOTED_RESULT has merely all characters
+# which are still active within double quotes backslashified.
+func_quote_for_eval ()
+{
+ case $1 in
+ *[\\\`\"\$]*)
+ func_quote_for_eval_unquoted_result=`$ECHO "X$1" | $Xsed -e "$sed_quote_subst"` ;;
+ *)
+ func_quote_for_eval_unquoted_result="$1" ;;
+ esac
+
+ case $func_quote_for_eval_unquoted_result in
+ # Double-quote args containing shell metacharacters to delay
+ # word splitting, command substitution and and variable
+ # expansion for a subsequent eval.
+ # Many Bourne shells cannot handle close brackets correctly
+ # in scan sets, so we specify it separately.
+ *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"")
+ func_quote_for_eval_result="\"$func_quote_for_eval_unquoted_result\""
+ ;;
+ *)
+ func_quote_for_eval_result="$func_quote_for_eval_unquoted_result"
+ esac
+}
+
+
+# func_quote_for_expand arg
+# Aesthetically quote ARG to be evaled later; same as above,
+# but do not quote variable references.
+func_quote_for_expand ()
+{
+ case $1 in
+ *[\\\`\"]*)
+ my_arg=`$ECHO "X$1" | $Xsed \
+ -e "$double_quote_subst" -e "$sed_double_backslash"` ;;
+ *)
+ my_arg="$1" ;;
+ esac
+
+ case $my_arg in
+ # Double-quote args containing shell metacharacters to delay
+ # word splitting and command substitution for a subsequent eval.
+ # Many Bourne shells cannot handle close brackets correctly
+ # in scan sets, so we specify it separately.
+ *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"")
+ my_arg="\"$my_arg\""
+ ;;
+ esac
+
+ func_quote_for_expand_result="$my_arg"
+}
+
+
+# func_show_eval cmd [fail_exp]
+# Unless opt_silent is true, then output CMD. Then, if opt_dryrun is
+# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP
+# is given, then evaluate it.
+func_show_eval ()
+{
+ my_cmd="$1"
+ my_fail_exp="${2-:}"
+
+ ${opt_silent-false} || {
+ func_quote_for_expand "$my_cmd"
+ eval "func_echo $func_quote_for_expand_result"
+ }
+
+ if ${opt_dry_run-false}; then :; else
+ eval "$my_cmd"
+ my_status=$?
+ if test "$my_status" -eq 0; then :; else
+ eval "(exit $my_status); $my_fail_exp"
+ fi
+ fi
+}
+
+
+# func_show_eval_locale cmd [fail_exp]
+# Unless opt_silent is true, then output CMD. Then, if opt_dryrun is
+# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP
+# is given, then evaluate it. Use the saved locale for evaluation.
+func_show_eval_locale ()
+{
+ my_cmd="$1"
+ my_fail_exp="${2-:}"
+
+ ${opt_silent-false} || {
+ func_quote_for_expand "$my_cmd"
+ eval "func_echo $func_quote_for_expand_result"
+ }
+
+ if ${opt_dry_run-false}; then :; else
+ eval "$lt_user_locale
+ $my_cmd"
+ my_status=$?
+ eval "$lt_safe_locale"
+ if test "$my_status" -eq 0; then :; else
+ eval "(exit $my_status); $my_fail_exp"
+ fi
+ fi
+}
+
+
+
+
+
+# func_version
+# Echo version message to standard output and exit.
+func_version ()
+{
+ $SED -n '/^# '$PROGRAM' (GNU /,/# warranty; / {
+ s/^# //
+ s/^# *$//
+ s/\((C)\)[ 0-9,-]*\( [1-9][0-9]*\)/\1\2/
+ p
+ }' < "$progpath"
+ exit $?
+}
+
+# func_usage
+# Echo short help message to standard output and exit.
+func_usage ()
+{
+ $SED -n '/^# Usage:/,/# -h/ {
+ s/^# //
+ s/^# *$//
+ s/\$progname/'$progname'/
+ p
+ }' < "$progpath"
+ $ECHO
+ $ECHO "run \`$progname --help | more' for full usage"
+ exit $?
+}
+
+# func_help
+# Echo long help message to standard output and exit.
+func_help ()
+{
+ $SED -n '/^# Usage:/,/# Report bugs to/ {
+ s/^# //
+ s/^# *$//
+ s*\$progname*'$progname'*
+ s*\$host*'"$host"'*
+ s*\$SHELL*'"$SHELL"'*
+ s*\$LTCC*'"$LTCC"'*
+ s*\$LTCFLAGS*'"$LTCFLAGS"'*
+ s*\$LD*'"$LD"'*
+ s/\$with_gnu_ld/'"$with_gnu_ld"'/
+ s/\$automake_version/'"`(automake --version) 2>/dev/null |$SED 1q`"'/
+ s/\$autoconf_version/'"`(autoconf --version) 2>/dev/null |$SED 1q`"'/
+ p
+ }' < "$progpath"
+ exit $?
+}
+
+# func_missing_arg argname
+# Echo program name prefixed message to standard error and set global
+# exit_cmd.
+func_missing_arg ()
+{
+ func_error "missing argument for $1"
+ exit_cmd=exit
+}
+
+exit_cmd=:
+
+
+
+
+
+# Check that we have a working $ECHO.
+if test "X$1" = X--no-reexec; then
+ # Discard the --no-reexec flag, and continue.
+ shift
+elif test "X$1" = X--fallback-echo; then
+ # Avoid inline document here, it may be left over
+ :
+elif test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t'; then
+ # Yippee, $ECHO works!
+ :
+else
+ # Restart under the correct shell, and then maybe $ECHO will work.
+ exec $SHELL "$progpath" --no-reexec ${1+"$@"}
+fi
+
+if test "X$1" = X--fallback-echo; then
+ # used as fallback echo
+ shift
+ cat <<EOF
+$*
+EOF
+ exit $EXIT_SUCCESS
+fi
+
+magic="%%%MAGIC variable%%%"
+magic_exe="%%%MAGIC EXE variable%%%"
+
+# Global variables.
+# $mode is unset
+nonopt=
+execute_dlfiles=
+preserve_args=
+lo2o="s/\\.lo\$/.${objext}/"
+o2lo="s/\\.${objext}\$/.lo/"
+extracted_archives=
+extracted_serial=0
+
+opt_dry_run=false
+opt_duplicate_deps=false
+opt_silent=false
+opt_debug=:
+
+# If this variable is set in any of the actions, the command in it
+# will be execed at the end. This prevents here-documents from being
+# left over by shells.
+exec_cmd=
+
+# func_fatal_configuration arg...
+# Echo program name prefixed message to standard error, followed by
+# a configuration failure hint, and exit.
+func_fatal_configuration ()
+{
+ func_error ${1+"$@"}
+ func_error "See the $PACKAGE documentation for more information."
+ func_fatal_error "Fatal configuration error."
+}
+
+
+# func_config
+# Display the configuration for all the tags in this script.
+func_config ()
+{
+ re_begincf='^# ### BEGIN LIBTOOL'
+ re_endcf='^# ### END LIBTOOL'
+
+ # Default configuration.
+ $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath"
+
+ # Now print the configurations for the tags.
+ for tagname in $taglist; do
+ $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath"
+ done
+
+ exit $?
+}
+
+# func_features
+# Display the features supported by this script.
+func_features ()
+{
+ $ECHO "host: $host"
+ if test "$build_libtool_libs" = yes; then
+ $ECHO "enable shared libraries"
+ else
+ $ECHO "disable shared libraries"
+ fi
+ if test "$build_old_libs" = yes; then
+ $ECHO "enable static libraries"
+ else
+ $ECHO "disable static libraries"
+ fi
+
+ exit $?
+}
+
+# func_enable_tag tagname
+# Verify that TAGNAME is valid, and either flag an error and exit, or
+# enable the TAGNAME tag. We also add TAGNAME to the global $taglist
+# variable here.
+func_enable_tag ()
+{
+ # Global variable:
+ tagname="$1"
+
+ re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$"
+ re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$"
+ sed_extractcf="/$re_begincf/,/$re_endcf/p"
+
+ # Validate tagname.
+ case $tagname in
+ *[!-_A-Za-z0-9,/]*)
+ func_fatal_error "invalid tag name: $tagname"
+ ;;
+ esac
+
+ # Don't test for the "default" C tag, as we know it's
+ # there but not specially marked.
+ case $tagname in
+ CC) ;;
+ *)
+ if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then
+ taglist="$taglist $tagname"
+
+ # Evaluate the configuration. Be careful to quote the path
+ # and the sed script, to avoid splitting on whitespace, but
+ # also don't use non-portable quotes within backquotes within
+ # quotes we have to do it in 2 steps:
+ extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"`
+ eval "$extractedcf"
+ else
+ func_error "ignoring unknown tag $tagname"
+ fi
+ ;;
+ esac
+}
+
+# Parse options once, thoroughly. This comes as soon as possible in
+# the script to make things like `libtool --version' happen quickly.
+{
+
+ # Shorthand for --mode=foo, only valid as the first argument
+ case $1 in
+ clean|clea|cle|cl)
+ shift; set dummy --mode clean ${1+"$@"}; shift
+ ;;
+ compile|compil|compi|comp|com|co|c)
+ shift; set dummy --mode compile ${1+"$@"}; shift
+ ;;
+ execute|execut|execu|exec|exe|ex|e)
+ shift; set dummy --mode execute ${1+"$@"}; shift
+ ;;
+ finish|finis|fini|fin|fi|f)
+ shift; set dummy --mode finish ${1+"$@"}; shift
+ ;;
+ install|instal|insta|inst|ins|in|i)
+ shift; set dummy --mode install ${1+"$@"}; shift
+ ;;
+ link|lin|li|l)
+ shift; set dummy --mode link ${1+"$@"}; shift
+ ;;
+ uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u)
+ shift; set dummy --mode uninstall ${1+"$@"}; shift
+ ;;
+ esac
+
+ # Parse non-mode specific arguments:
+ while test "$#" -gt 0; do
+ opt="$1"
+ shift
+
+ case $opt in
+ --config) func_config ;;
+
+ --debug) preserve_args="$preserve_args $opt"
+ func_echo "enabling shell trace mode"
+ opt_debug='set -x'
+ $opt_debug
+ ;;
+
+ -dlopen) test "$#" -eq 0 && func_missing_arg "$opt" && break
+ execute_dlfiles="$execute_dlfiles $1"
+ shift
+ ;;
+
+ --dry-run | -n) opt_dry_run=: ;;
+ --features) func_features ;;
+ --finish) mode="finish" ;;
+
+ --mode) test "$#" -eq 0 && func_missing_arg "$opt" && break
+ case $1 in
+ # Valid mode arguments:
+ clean) ;;
+ compile) ;;
+ execute) ;;
+ finish) ;;
+ install) ;;
+ link) ;;
+ relink) ;;
+ uninstall) ;;
+
+ # Catch anything else as an error
+ *) func_error "invalid argument for $opt"
+ exit_cmd=exit
+ break
+ ;;
+ esac
+
+ mode="$1"
+ shift
+ ;;
+
+ --preserve-dup-deps)
+ opt_duplicate_deps=: ;;
+
+ --quiet|--silent) preserve_args="$preserve_args $opt"
+ opt_silent=:
+ ;;
+
+ --verbose| -v) preserve_args="$preserve_args $opt"
+ opt_silent=false
+ ;;
+
+ --tag) test "$#" -eq 0 && func_missing_arg "$opt" && break
+ preserve_args="$preserve_args $opt $1"
+ func_enable_tag "$1" # tagname is set here
+ shift
+ ;;
+
+ # Separate optargs to long options:
+ -dlopen=*|--mode=*|--tag=*)
+ func_opt_split "$opt"
+ set dummy "$func_opt_split_opt" "$func_opt_split_arg" ${1+"$@"}
+ shift
+ ;;
+
+ -\?|-h) func_usage ;;
+ --help) opt_help=: ;;
+ --version) func_version ;;
+
+ -*) func_fatal_help "unrecognized option \`$opt'" ;;
+
+ *) nonopt="$opt"
+ break
+ ;;
+ esac
+ done
+
+
+ case $host in
+ *cygwin* | *mingw* | *pw32* | *cegcc*)
+ # don't eliminate duplications in $postdeps and $predeps
+ opt_duplicate_compiler_generated_deps=:
+ ;;
+ *)
+ opt_duplicate_compiler_generated_deps=$opt_duplicate_deps
+ ;;
+ esac
+
+ # Having warned about all mis-specified options, bail out if
+ # anything was wrong.
+ $exit_cmd $EXIT_FAILURE
+}
+
+# func_check_version_match
+# Ensure that we are using m4 macros, and libtool script from the same
+# release of libtool.
+func_check_version_match ()
+{
+ if test "$package_revision" != "$macro_revision"; then
+ if test "$VERSION" != "$macro_version"; then
+ if test -z "$macro_version"; then
+ cat >&2 <<_LT_EOF
+$progname: Version mismatch error. This is $PACKAGE $VERSION, but the
+$progname: definition of this LT_INIT comes from an older release.
+$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION
+$progname: and run autoconf again.
+_LT_EOF
+ else
+ cat >&2 <<_LT_EOF
+$progname: Version mismatch error. This is $PACKAGE $VERSION, but the
+$progname: definition of this LT_INIT comes from $PACKAGE $macro_version.
+$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION
+$progname: and run autoconf again.
+_LT_EOF
+ fi
+ else
+ cat >&2 <<_LT_EOF
+$progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision,
+$progname: but the definition of this LT_INIT comes from revision $macro_revision.
+$progname: You should recreate aclocal.m4 with macros from revision $package_revision
+$progname: of $PACKAGE $VERSION and run autoconf again.
+_LT_EOF
+ fi
+
+ exit $EXIT_MISMATCH
+ fi
+}
+
+
+## ----------- ##
+## Main. ##
+## ----------- ##
+
+$opt_help || {
+ # Sanity checks first:
+ func_check_version_match
+
+ if test "$build_libtool_libs" != yes && test "$build_old_libs" != yes; then
+ func_fatal_configuration "not configured to build any kind of library"
+ fi
+
+ test -z "$mode" && func_fatal_error "error: you must specify a MODE."
+
+
+ # Darwin sucks
+ eval std_shrext=\"$shrext_cmds\"
+
+
+ # Only execute mode is allowed to have -dlopen flags.
+ if test -n "$execute_dlfiles" && test "$mode" != execute; then
+ func_error "unrecognized option \`-dlopen'"
+ $ECHO "$help" 1>&2
+ exit $EXIT_FAILURE
+ fi
+
+ # Change the help message to a mode-specific one.
+ generic_help="$help"
+ help="Try \`$progname --help --mode=$mode' for more information."
+}
+
+
+# func_lalib_p file
+# True iff FILE is a libtool `.la' library or `.lo' object file.
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_lalib_p ()
+{
+ test -f "$1" &&
+ $SED -e 4q "$1" 2>/dev/null \
+ | $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1
+}
+
+# func_lalib_unsafe_p file
+# True iff FILE is a libtool `.la' library or `.lo' object file.
+# This function implements the same check as func_lalib_p without
+# resorting to external programs. To this end, it redirects stdin and
+# closes it afterwards, without saving the original file descriptor.
+# As a safety measure, use it only where a negative result would be
+# fatal anyway. Works if `file' does not exist.
+func_lalib_unsafe_p ()
+{
+ lalib_p=no
+ if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then
+ for lalib_p_l in 1 2 3 4
+ do
+ read lalib_p_line
+ case "$lalib_p_line" in
+ \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;;
+ esac
+ done
+ exec 0<&5 5<&-
+ fi
+ test "$lalib_p" = yes
+}
+
+# func_ltwrapper_script_p file
+# True iff FILE is a libtool wrapper script
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_ltwrapper_script_p ()
+{
+ func_lalib_p "$1"
+}
+
+# func_ltwrapper_executable_p file
+# True iff FILE is a libtool wrapper executable
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_ltwrapper_executable_p ()
+{
+ func_ltwrapper_exec_suffix=
+ case $1 in
+ *.exe) ;;
+ *) func_ltwrapper_exec_suffix=.exe ;;
+ esac
+ $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1
+}
+
+# func_ltwrapper_scriptname file
+# Assumes file is an ltwrapper_executable
+# uses $file to determine the appropriate filename for a
+# temporary ltwrapper_script.
+func_ltwrapper_scriptname ()
+{
+ func_ltwrapper_scriptname_result=""
+ if func_ltwrapper_executable_p "$1"; then
+ func_dirname_and_basename "$1" "" "."
+ func_stripname '' '.exe' "$func_basename_result"
+ func_ltwrapper_scriptname_result="$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper"
+ fi
+}
+
+# func_ltwrapper_p file
+# True iff FILE is a libtool wrapper script or wrapper executable
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_ltwrapper_p ()
+{
+ func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1"
+}
+
+
+# func_execute_cmds commands fail_cmd
+# Execute tilde-delimited COMMANDS.
+# If FAIL_CMD is given, eval that upon failure.
+# FAIL_CMD may read-access the current command in variable CMD!
+func_execute_cmds ()
+{
+ $opt_debug
+ save_ifs=$IFS; IFS='~'
+ for cmd in $1; do
+ IFS=$save_ifs
+ eval cmd=\"$cmd\"
+ func_show_eval "$cmd" "${2-:}"
+ done
+ IFS=$save_ifs
+}
+
+
+# func_source file
+# Source FILE, adding directory component if necessary.
+# Note that it is not necessary on cygwin/mingw to append a dot to
+# FILE even if both FILE and FILE.exe exist: automatic-append-.exe
+# behavior happens only for exec(3), not for open(2)! Also, sourcing
+# `FILE.' does not work on cygwin managed mounts.
+func_source ()
+{
+ $opt_debug
+ case $1 in
+ */* | *\\*) . "$1" ;;
+ *) . "./$1" ;;
+ esac
+}
+
+
+# func_infer_tag arg
+# Infer tagged configuration to use if any are available and
+# if one wasn't chosen via the "--tag" command line option.
+# Only attempt this if the compiler in the base compile
+# command doesn't match the default compiler.
+# arg is usually of the form 'gcc ...'
+func_infer_tag ()
+{
+ $opt_debug
+ if test -n "$available_tags" && test -z "$tagname"; then
+ CC_quoted=
+ for arg in $CC; do
+ func_quote_for_eval "$arg"
+ CC_quoted="$CC_quoted $func_quote_for_eval_result"
+ done
+ case $@ in
+ # Blanks in the command may have been stripped by the calling shell,
+ # but not from the CC environment variable when configure was run.
+ " $CC "* | "$CC "* | " `$ECHO $CC` "* | "`$ECHO $CC` "* | " $CC_quoted"* | "$CC_quoted "* | " `$ECHO $CC_quoted` "* | "`$ECHO $CC_quoted` "*) ;;
+ # Blanks at the start of $base_compile will cause this to fail
+ # if we don't check for them as well.
+ *)
+ for z in $available_tags; do
+ if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then
+ # Evaluate the configuration.
+ eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`"
+ CC_quoted=
+ for arg in $CC; do
+ # Double-quote args containing other shell metacharacters.
+ func_quote_for_eval "$arg"
+ CC_quoted="$CC_quoted $func_quote_for_eval_result"
+ done
+ case "$@ " in
+ " $CC "* | "$CC "* | " `$ECHO $CC` "* | "`$ECHO $CC` "* | " $CC_quoted"* | "$CC_quoted "* | " `$ECHO $CC_quoted` "* | "`$ECHO $CC_quoted` "*)
+ # The compiler in the base compile command matches
+ # the one in the tagged configuration.
+ # Assume this is the tagged configuration we want.
+ tagname=$z
+ break
+ ;;
+ esac
+ fi
+ done
+ # If $tagname still isn't set, then no tagged configuration
+ # was found and let the user know that the "--tag" command
+ # line option must be used.
+ if test -z "$tagname"; then
+ func_echo "unable to infer tagged configuration"
+ func_fatal_error "specify a tag with \`--tag'"
+# else
+# func_verbose "using $tagname tagged configuration"
+ fi
+ ;;
+ esac
+ fi
+}
+
+
+
+# func_write_libtool_object output_name pic_name nonpic_name
+# Create a libtool object file (analogous to a ".la" file),
+# but don't create it if we're doing a dry run.
+func_write_libtool_object ()
+{
+ write_libobj=${1}
+ if test "$build_libtool_libs" = yes; then
+ write_lobj=\'${2}\'
+ else
+ write_lobj=none
+ fi
+
+ if test "$build_old_libs" = yes; then
+ write_oldobj=\'${3}\'
+ else
+ write_oldobj=none
+ fi
+
+ $opt_dry_run || {
+ cat >${write_libobj}T <<EOF
+# $write_libobj - a libtool object file
+# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION
+#
+# Please DO NOT delete this file!
+# It is necessary for linking the library.
+
+# Name of the PIC object.
+pic_object=$write_lobj
+
+# Name of the non-PIC object
+non_pic_object=$write_oldobj
+
+EOF
+ $MV "${write_libobj}T" "${write_libobj}"
+ }
+}
+
+# func_mode_compile arg...
+func_mode_compile ()
+{
+ $opt_debug
+ # Get the compilation command and the source file.
+ base_compile=
+ srcfile="$nonopt" # always keep a non-empty value in "srcfile"
+ suppress_opt=yes
+ suppress_output=
+ arg_mode=normal
+ libobj=
+ later=
+ pie_flag=
+
+ for arg
+ do
+ case $arg_mode in
+ arg )
+ # do not "continue". Instead, add this to base_compile
+ lastarg="$arg"
+ arg_mode=normal
+ ;;
+
+ target )
+ libobj="$arg"
+ arg_mode=normal
+ continue
+ ;;
+
+ normal )
+ # Accept any command-line options.
+ case $arg in
+ -o)
+ test -n "$libobj" && \
+ func_fatal_error "you cannot specify \`-o' more than once"
+ arg_mode=target
+ continue
+ ;;
+
+ -pie | -fpie | -fPIE)
+ pie_flag="$pie_flag $arg"
+ continue
+ ;;
+
+ -shared | -static | -prefer-pic | -prefer-non-pic)
+ later="$later $arg"
+ continue
+ ;;
+
+ -no-suppress)
+ suppress_opt=no
+ continue
+ ;;
+
+ -Xcompiler)
+ arg_mode=arg # the next one goes into the "base_compile" arg list
+ continue # The current "srcfile" will either be retained or
+ ;; # replaced later. I would guess that would be a bug.
+
+ -Wc,*)
+ func_stripname '-Wc,' '' "$arg"
+ args=$func_stripname_result
+ lastarg=
+ save_ifs="$IFS"; IFS=','
+ for arg in $args; do
+ IFS="$save_ifs"
+ func_quote_for_eval "$arg"
+ lastarg="$lastarg $func_quote_for_eval_result"
+ done
+ IFS="$save_ifs"
+ func_stripname ' ' '' "$lastarg"
+ lastarg=$func_stripname_result
+
+ # Add the arguments to base_compile.
+ base_compile="$base_compile $lastarg"
+ continue
+ ;;
+
+ *)
+ # Accept the current argument as the source file.
+ # The previous "srcfile" becomes the current argument.
+ #
+ lastarg="$srcfile"
+ srcfile="$arg"
+ ;;
+ esac # case $arg
+ ;;
+ esac # case $arg_mode
+
+ # Aesthetically quote the previous argument.
+ func_quote_for_eval "$lastarg"
+ base_compile="$base_compile $func_quote_for_eval_result"
+ done # for arg
+
+ case $arg_mode in
+ arg)
+ func_fatal_error "you must specify an argument for -Xcompile"
+ ;;
+ target)
+ func_fatal_error "you must specify a target with \`-o'"
+ ;;
+ *)
+ # Get the name of the library object.
+ test -z "$libobj" && {
+ func_basename "$srcfile"
+ libobj="$func_basename_result"
+ }
+ ;;
+ esac
+
+ # Recognize several different file suffixes.
+ # If the user specifies -o file.o, it is replaced with file.lo
+ case $libobj in
+ *.[cCFSifmso] | \
+ *.ada | *.adb | *.ads | *.asm | \
+ *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \
+ *.[fF][09]? | *.for | *.java | *.obj | *.sx)
+ func_xform "$libobj"
+ libobj=$func_xform_result
+ ;;
+ esac
+
+ case $libobj in
+ *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;;
+ *)
+ func_fatal_error "cannot determine name of library object from \`$libobj'"
+ ;;
+ esac
+
+ func_infer_tag $base_compile
+
+ for arg in $later; do
+ case $arg in
+ -shared)
+ test "$build_libtool_libs" != yes && \
+ func_fatal_configuration "can not build a shared library"
+ build_old_libs=no
+ continue
+ ;;
+
+ -static)
+ build_libtool_libs=no
+ build_old_libs=yes
+ continue
+ ;;
+
+ -prefer-pic)
+ pic_mode=yes
+ continue
+ ;;
+
+ -prefer-non-pic)
+ pic_mode=no
+ continue
+ ;;
+ esac
+ done
+
+ func_quote_for_eval "$libobj"
+ test "X$libobj" != "X$func_quote_for_eval_result" \
+ && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"' &()|`$[]' \
+ && func_warning "libobj name \`$libobj' may not contain shell special characters."
+ func_dirname_and_basename "$obj" "/" ""
+ objname="$func_basename_result"
+ xdir="$func_dirname_result"
+ lobj=${xdir}$objdir/$objname
+
+ test -z "$base_compile" && \
+ func_fatal_help "you must specify a compilation command"
+
+ # Delete any leftover library objects.
+ if test "$build_old_libs" = yes; then
+ removelist="$obj $lobj $libobj ${libobj}T"
+ else
+ removelist="$lobj $libobj ${libobj}T"
+ fi
+
+ # On Cygwin there's no "real" PIC flag so we must build both object types
+ case $host_os in
+ cygwin* | mingw* | pw32* | os2* | cegcc*)
+ pic_mode=default
+ ;;
+ esac
+ if test "$pic_mode" = no && test "$deplibs_check_method" != pass_all; then
+ # non-PIC code in shared libraries is not supported
+ pic_mode=default
+ fi
+
+ # Calculate the filename of the output object if compiler does
+ # not support -o with -c
+ if test "$compiler_c_o" = no; then
+ output_obj=`$ECHO "X$srcfile" | $Xsed -e 's%^.*/%%' -e 's%\.[^.]*$%%'`.${objext}
+ lockfile="$output_obj.lock"
+ else
+ output_obj=
+ need_locks=no
+ lockfile=
+ fi
+
+ # Lock this critical section if it is needed
+ # We use this script file to make the link, it avoids creating a new file
+ if test "$need_locks" = yes; then
+ until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do
+ func_echo "Waiting for $lockfile to be removed"
+ sleep 2
+ done
+ elif test "$need_locks" = warn; then
+ if test -f "$lockfile"; then
+ $ECHO "\
+*** ERROR, $lockfile exists and contains:
+`cat $lockfile 2>/dev/null`
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support \`-c' and \`-o' together. If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+ $opt_dry_run || $RM $removelist
+ exit $EXIT_FAILURE
+ fi
+ removelist="$removelist $output_obj"
+ $ECHO "$srcfile" > "$lockfile"
+ fi
+
+ $opt_dry_run || $RM $removelist
+ removelist="$removelist $lockfile"
+ trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15
+
+ if test -n "$fix_srcfile_path"; then
+ eval srcfile=\"$fix_srcfile_path\"
+ fi
+ func_quote_for_eval "$srcfile"
+ qsrcfile=$func_quote_for_eval_result
+
+ # Only build a PIC object if we are building libtool libraries.
+ if test "$build_libtool_libs" = yes; then
+ # Without this assignment, base_compile gets emptied.
+ fbsd_hideous_sh_bug=$base_compile
+
+ if test "$pic_mode" != no; then
+ command="$base_compile $qsrcfile $pic_flag"
+ else
+ # Don't build PIC code
+ command="$base_compile $qsrcfile"
+ fi
+
+ func_mkdir_p "$xdir$objdir"
+
+ if test -z "$output_obj"; then
+ # Place PIC objects in $objdir
+ command="$command -o $lobj"
+ fi
+
+ func_show_eval_locale "$command" \
+ 'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE'
+
+ if test "$need_locks" = warn &&
+ test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then
+ $ECHO "\
+*** ERROR, $lockfile contains:
+`cat $lockfile 2>/dev/null`
+
+but it should contain:
+$srcfile
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support \`-c' and \`-o' together. If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+ $opt_dry_run || $RM $removelist
+ exit $EXIT_FAILURE
+ fi
+
+ # Just move the object if needed, then go on to compile the next one
+ if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then
+ func_show_eval '$MV "$output_obj" "$lobj"' \
+ 'error=$?; $opt_dry_run || $RM $removelist; exit $error'
+ fi
+
+ # Allow error messages only from the first compilation.
+ if test "$suppress_opt" = yes; then
+ suppress_output=' >/dev/null 2>&1'
+ fi
+ fi
+
+ # Only build a position-dependent object if we build old libraries.
+ if test "$build_old_libs" = yes; then
+ if test "$pic_mode" != yes; then
+ # Don't build PIC code
+ command="$base_compile $qsrcfile$pie_flag"
+ else
+ command="$base_compile $qsrcfile $pic_flag"
+ fi
+ if test "$compiler_c_o" = yes; then
+ command="$command -o $obj"
+ fi
+
+ # Suppress compiler output if we already did a PIC compilation.
+ command="$command$suppress_output"
+ func_show_eval_locale "$command" \
+ '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE'
+
+ if test "$need_locks" = warn &&
+ test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then
+ $ECHO "\
+*** ERROR, $lockfile contains:
+`cat $lockfile 2>/dev/null`
+
+but it should contain:
+$srcfile
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support \`-c' and \`-o' together. If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+ $opt_dry_run || $RM $removelist
+ exit $EXIT_FAILURE
+ fi
+
+ # Just move the object if needed
+ if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then
+ func_show_eval '$MV "$output_obj" "$obj"' \
+ 'error=$?; $opt_dry_run || $RM $removelist; exit $error'
+ fi
+ fi
+
+ $opt_dry_run || {
+ func_write_libtool_object "$libobj" "$objdir/$objname" "$objname"
+
+ # Unlock the critical section if it was locked
+ if test "$need_locks" != no; then
+ removelist=$lockfile
+ $RM "$lockfile"
+ fi
+ }
+
+ exit $EXIT_SUCCESS
+}
+
+$opt_help || {
+test "$mode" = compile && func_mode_compile ${1+"$@"}
+}
+
+func_mode_help ()
+{
+ # We need to display help for each of the modes.
+ case $mode in
+ "")
+ # Generic help is extracted from the usage comments
+ # at the start of this file.
+ func_help
+ ;;
+
+ clean)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE...
+
+Remove files from the build directory.
+
+RM is the name of the program to use to delete files associated with each FILE
+(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed
+to RM.
+
+If FILE is a libtool library, object or program, all the files associated
+with it are deleted. Otherwise, only FILE itself is deleted using RM."
+ ;;
+
+ compile)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE
+
+Compile a source file into a libtool library object.
+
+This mode accepts the following additional options:
+
+ -o OUTPUT-FILE set the output file name to OUTPUT-FILE
+ -no-suppress do not suppress compiler output for multiple passes
+ -prefer-pic try to building PIC objects only
+ -prefer-non-pic try to building non-PIC objects only
+ -shared do not build a \`.o' file suitable for static linking
+ -static only build a \`.o' file suitable for static linking
+
+COMPILE-COMMAND is a command to be used in creating a \`standard' object file
+from the given SOURCEFILE.
+
+The output file name is determined by removing the directory component from
+SOURCEFILE, then substituting the C source code suffix \`.c' with the
+library object suffix, \`.lo'."
+ ;;
+
+ execute)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]...
+
+Automatically set library path, then run a program.
+
+This mode accepts the following additional options:
+
+ -dlopen FILE add the directory containing FILE to the library path
+
+This mode sets the library path environment variable according to \`-dlopen'
+flags.
+
+If any of the ARGS are libtool executable wrappers, then they are translated
+into their corresponding uninstalled binary, and any of their required library
+directories are added to the library path.
+
+Then, COMMAND is executed, with ARGS as arguments."
+ ;;
+
+ finish)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=finish [LIBDIR]...
+
+Complete the installation of libtool libraries.
+
+Each LIBDIR is a directory that contains libtool libraries.
+
+The commands that this mode executes may require superuser privileges. Use
+the \`--dry-run' option if you just want to see what would be executed."
+ ;;
+
+ install)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND...
+
+Install executables or libraries.
+
+INSTALL-COMMAND is the installation command. The first component should be
+either the \`install' or \`cp' program.
+
+The following components of INSTALL-COMMAND are treated specially:
+
+ -inst-prefix PREFIX-DIR Use PREFIX-DIR as a staging area for installation
+
+The rest of the components are interpreted as arguments to that command (only
+BSD-compatible install options are recognized)."
+ ;;
+
+ link)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=link LINK-COMMAND...
+
+Link object files or libraries together to form another library, or to
+create an executable program.
+
+LINK-COMMAND is a command using the C compiler that you would use to create
+a program from several object files.
+
+The following components of LINK-COMMAND are treated specially:
+
+ -all-static do not do any dynamic linking at all
+ -avoid-version do not add a version suffix if possible
+ -dlopen FILE \`-dlpreopen' FILE if it cannot be dlopened at runtime
+ -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols
+ -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3)
+ -export-symbols SYMFILE
+ try to export only the symbols listed in SYMFILE
+ -export-symbols-regex REGEX
+ try to export only the symbols matching REGEX
+ -LLIBDIR search LIBDIR for required installed libraries
+ -lNAME OUTPUT-FILE requires the installed library libNAME
+ -module build a library that can dlopened
+ -no-fast-install disable the fast-install mode
+ -no-install link a not-installable executable
+ -no-undefined declare that a library does not refer to external symbols
+ -o OUTPUT-FILE create OUTPUT-FILE from the specified objects
+ -objectlist FILE Use a list of object files found in FILE to specify objects
+ -precious-files-regex REGEX
+ don't remove output files matching REGEX
+ -release RELEASE specify package release information
+ -rpath LIBDIR the created library will eventually be installed in LIBDIR
+ -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries
+ -shared only do dynamic linking of libtool libraries
+ -shrext SUFFIX override the standard shared library file extension
+ -static do not do any dynamic linking of uninstalled libtool libraries
+ -static-libtool-libs
+ do not do any dynamic linking of libtool libraries
+ -version-info CURRENT[:REVISION[:AGE]]
+ specify library version info [each variable defaults to 0]
+ -weak LIBNAME declare that the target provides the LIBNAME interface
+
+All other options (arguments beginning with \`-') are ignored.
+
+Every other argument is treated as a filename. Files ending in \`.la' are
+treated as uninstalled libtool libraries, other files are standard or library
+object files.
+
+If the OUTPUT-FILE ends in \`.la', then a libtool library is created,
+only library objects (\`.lo' files) may be specified, and \`-rpath' is
+required, except when creating a convenience library.
+
+If OUTPUT-FILE ends in \`.a' or \`.lib', then a standard library is created
+using \`ar' and \`ranlib', or on Windows using \`lib'.
+
+If OUTPUT-FILE ends in \`.lo' or \`.${objext}', then a reloadable object file
+is created, otherwise an executable program is created."
+ ;;
+
+ uninstall)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE...
+
+Remove libraries from an installation directory.
+
+RM is the name of the program to use to delete files associated with each FILE
+(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed
+to RM.
+
+If FILE is a libtool library, all the files associated with it are deleted.
+Otherwise, only FILE itself is deleted using RM."
+ ;;
+
+ *)
+ func_fatal_help "invalid operation mode \`$mode'"
+ ;;
+ esac
+
+ $ECHO
+ $ECHO "Try \`$progname --help' for more information about other modes."
+
+ exit $?
+}
+
+ # Now that we've collected a possible --mode arg, show help if necessary
+ $opt_help && func_mode_help
+
+
+# func_mode_execute arg...
+func_mode_execute ()
+{
+ $opt_debug
+ # The first argument is the command name.
+ cmd="$nonopt"
+ test -z "$cmd" && \
+ func_fatal_help "you must specify a COMMAND"
+
+ # Handle -dlopen flags immediately.
+ for file in $execute_dlfiles; do
+ test -f "$file" \
+ || func_fatal_help "\`$file' is not a file"
+
+ dir=
+ case $file in
+ *.la)
+ # Check to see that this really is a libtool archive.
+ func_lalib_unsafe_p "$file" \
+ || func_fatal_help "\`$lib' is not a valid libtool archive"
+
+ # Read the libtool library.
+ dlname=
+ library_names=
+ func_source "$file"
+
+ # Skip this library if it cannot be dlopened.
+ if test -z "$dlname"; then
+ # Warn if it was a shared library.
+ test -n "$library_names" && \
+ func_warning "\`$file' was not linked with \`-export-dynamic'"
+ continue
+ fi
+
+ func_dirname "$file" "" "."
+ dir="$func_dirname_result"
+
+ if test -f "$dir/$objdir/$dlname"; then
+ dir="$dir/$objdir"
+ else
+ if test ! -f "$dir/$dlname"; then
+ func_fatal_error "cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'"
+ fi
+ fi
+ ;;
+
+ *.lo)
+ # Just add the directory containing the .lo file.
+ func_dirname "$file" "" "."
+ dir="$func_dirname_result"
+ ;;
+
+ *)
+ func_warning "\`-dlopen' is ignored for non-libtool libraries and objects"
+ continue
+ ;;
+ esac
+
+ # Get the absolute pathname.
+ absdir=`cd "$dir" && pwd`
+ test -n "$absdir" && dir="$absdir"
+
+ # Now add the directory to shlibpath_var.
+ if eval "test -z \"\$$shlibpath_var\""; then
+ eval "$shlibpath_var=\"\$dir\""
+ else
+ eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\""
+ fi
+ done
+
+ # This variable tells wrapper scripts just to set shlibpath_var
+ # rather than running their programs.
+ libtool_execute_magic="$magic"
+
+ # Check if any of the arguments is a wrapper script.
+ args=
+ for file
+ do
+ case $file in
+ -*) ;;
+ *)
+ # Do a test to see if this is really a libtool program.
+ if func_ltwrapper_script_p "$file"; then
+ func_source "$file"
+ # Transform arg to wrapped name.
+ file="$progdir/$program"
+ elif func_ltwrapper_executable_p "$file"; then
+ func_ltwrapper_scriptname "$file"
+ func_source "$func_ltwrapper_scriptname_result"
+ # Transform arg to wrapped name.
+ file="$progdir/$program"
+ fi
+ ;;
+ esac
+ # Quote arguments (to preserve shell metacharacters).
+ func_quote_for_eval "$file"
+ args="$args $func_quote_for_eval_result"
+ done
+
+ if test "X$opt_dry_run" = Xfalse; then
+ if test -n "$shlibpath_var"; then
+ # Export the shlibpath_var.
+ eval "export $shlibpath_var"
+ fi
+
+ # Restore saved environment variables
+ for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES
+ do
+ eval "if test \"\${save_$lt_var+set}\" = set; then
+ $lt_var=\$save_$lt_var; export $lt_var
+ else
+ $lt_unset $lt_var
+ fi"
+ done
+
+ # Now prepare to actually exec the command.
+ exec_cmd="\$cmd$args"
+ else
+ # Display what would be done.
+ if test -n "$shlibpath_var"; then
+ eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\""
+ $ECHO "export $shlibpath_var"
+ fi
+ $ECHO "$cmd$args"
+ exit $EXIT_SUCCESS
+ fi
+}
+
+test "$mode" = execute && func_mode_execute ${1+"$@"}
+
+
+# func_mode_finish arg...
+func_mode_finish ()
+{
+ $opt_debug
+ libdirs="$nonopt"
+ admincmds=
+
+ if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then
+ for dir
+ do
+ libdirs="$libdirs $dir"
+ done
+
+ for libdir in $libdirs; do
+ if test -n "$finish_cmds"; then
+ # Do each command in the finish commands.
+ func_execute_cmds "$finish_cmds" 'admincmds="$admincmds
+'"$cmd"'"'
+ fi
+ if test -n "$finish_eval"; then
+ # Do the single finish_eval.
+ eval cmds=\"$finish_eval\"
+ $opt_dry_run || eval "$cmds" || admincmds="$admincmds
+ $cmds"
+ fi
+ done
+ fi
+
+ # Exit here if they wanted silent mode.
+ $opt_silent && exit $EXIT_SUCCESS
+
+ $ECHO "X----------------------------------------------------------------------" | $Xsed
+ $ECHO "Libraries have been installed in:"
+ for libdir in $libdirs; do
+ $ECHO " $libdir"
+ done
+ $ECHO
+ $ECHO "If you ever happen to want to link against installed libraries"
+ $ECHO "in a given directory, LIBDIR, you must either use libtool, and"
+ $ECHO "specify the full pathname of the library, or use the \`-LLIBDIR'"
+ $ECHO "flag during linking and do at least one of the following:"
+ if test -n "$shlibpath_var"; then
+ $ECHO " - add LIBDIR to the \`$shlibpath_var' environment variable"
+ $ECHO " during execution"
+ fi
+ if test -n "$runpath_var"; then
+ $ECHO " - add LIBDIR to the \`$runpath_var' environment variable"
+ $ECHO " during linking"
+ fi
+ if test -n "$hardcode_libdir_flag_spec"; then
+ libdir=LIBDIR
+ eval flag=\"$hardcode_libdir_flag_spec\"
+
+ $ECHO " - use the \`$flag' linker flag"
+ fi
+ if test -n "$admincmds"; then
+ $ECHO " - have your system administrator run these commands:$admincmds"
+ fi
+ if test -f /etc/ld.so.conf; then
+ $ECHO " - have your system administrator add LIBDIR to \`/etc/ld.so.conf'"
+ fi
+ $ECHO
+
+ $ECHO "See any operating system documentation about shared libraries for"
+ case $host in
+ solaris2.[6789]|solaris2.1[0-9])
+ $ECHO "more information, such as the ld(1), crle(1) and ld.so(8) manual"
+ $ECHO "pages."
+ ;;
+ *)
+ $ECHO "more information, such as the ld(1) and ld.so(8) manual pages."
+ ;;
+ esac
+ $ECHO "X----------------------------------------------------------------------" | $Xsed
+ exit $EXIT_SUCCESS
+}
+
+test "$mode" = finish && func_mode_finish ${1+"$@"}
+
+
+# func_mode_install arg...
+func_mode_install ()
+{
+ $opt_debug
+ # There may be an optional sh(1) argument at the beginning of
+ # install_prog (especially on Windows NT).
+ if test "$nonopt" = "$SHELL" || test "$nonopt" = /bin/sh ||
+ # Allow the use of GNU shtool's install command.
+ $ECHO "X$nonopt" | $GREP shtool >/dev/null; then
+ # Aesthetically quote it.
+ func_quote_for_eval "$nonopt"
+ install_prog="$func_quote_for_eval_result "
+ arg=$1
+ shift
+ else
+ install_prog=
+ arg=$nonopt
+ fi
+
+ # The real first argument should be the name of the installation program.
+ # Aesthetically quote it.
+ func_quote_for_eval "$arg"
+ install_prog="$install_prog$func_quote_for_eval_result"
+
+ # We need to accept at least all the BSD install flags.
+ dest=
+ files=
+ opts=
+ prev=
+ install_type=
+ isdir=no
+ stripme=
+ for arg
+ do
+ if test -n "$dest"; then
+ files="$files $dest"
+ dest=$arg
+ continue
+ fi
+
+ case $arg in
+ -d) isdir=yes ;;
+ -f)
+ case " $install_prog " in
+ *[\\\ /]cp\ *) ;;
+ *) prev=$arg ;;
+ esac
+ ;;
+ -g | -m | -o)
+ prev=$arg
+ ;;
+ -s)
+ stripme=" -s"
+ continue
+ ;;
+ -*)
+ ;;
+ *)
+ # If the previous option needed an argument, then skip it.
+ if test -n "$prev"; then
+ prev=
+ else
+ dest=$arg
+ continue
+ fi
+ ;;
+ esac
+
+ # Aesthetically quote the argument.
+ func_quote_for_eval "$arg"
+ install_prog="$install_prog $func_quote_for_eval_result"
+ done
+
+ test -z "$install_prog" && \
+ func_fatal_help "you must specify an install program"
+
+ test -n "$prev" && \
+ func_fatal_help "the \`$prev' option requires an argument"
+
+ if test -z "$files"; then
+ if test -z "$dest"; then
+ func_fatal_help "no file or destination specified"
+ else
+ func_fatal_help "you must specify a destination"
+ fi
+ fi
+
+ # Strip any trailing slash from the destination.
+ func_stripname '' '/' "$dest"
+ dest=$func_stripname_result
+
+ # Check to see that the destination is a directory.
+ test -d "$dest" && isdir=yes
+ if test "$isdir" = yes; then
+ destdir="$dest"
+ destname=
+ else
+ func_dirname_and_basename "$dest" "" "."
+ destdir="$func_dirname_result"
+ destname="$func_basename_result"
+
+ # Not a directory, so check to see that there is only one file specified.
+ set dummy $files; shift
+ test "$#" -gt 1 && \
+ func_fatal_help "\`$dest' is not a directory"
+ fi
+ case $destdir in
+ [\\/]* | [A-Za-z]:[\\/]*) ;;
+ *)
+ for file in $files; do
+ case $file in
+ *.lo) ;;
+ *)
+ func_fatal_help "\`$destdir' must be an absolute directory name"
+ ;;
+ esac
+ done
+ ;;
+ esac
+
+ # This variable tells wrapper scripts just to set variables rather
+ # than running their programs.
+ libtool_install_magic="$magic"
+
+ staticlibs=
+ future_libdirs=
+ current_libdirs=
+ for file in $files; do
+
+ # Do each installation.
+ case $file in
+ *.$libext)
+ # Do the static libraries later.
+ staticlibs="$staticlibs $file"
+ ;;
+
+ *.la)
+ # Check to see that this really is a libtool archive.
+ func_lalib_unsafe_p "$file" \
+ || func_fatal_help "\`$file' is not a valid libtool archive"
+
+ library_names=
+ old_library=
+ relink_command=
+ func_source "$file"
+
+ # Add the libdir to current_libdirs if it is the destination.
+ if test "X$destdir" = "X$libdir"; then
+ case "$current_libdirs " in
+ *" $libdir "*) ;;
+ *) current_libdirs="$current_libdirs $libdir" ;;
+ esac
+ else
+ # Note the libdir as a future libdir.
+ case "$future_libdirs " in
+ *" $libdir "*) ;;
+ *) future_libdirs="$future_libdirs $libdir" ;;
+ esac
+ fi
+
+ func_dirname "$file" "/" ""
+ dir="$func_dirname_result"
+ dir="$dir$objdir"
+
+ if test -n "$relink_command"; then
+ # Determine the prefix the user has applied to our future dir.
+ inst_prefix_dir=`$ECHO "X$destdir" | $Xsed -e "s%$libdir\$%%"`
+
+ # Don't allow the user to place us outside of our expected
+ # location b/c this prevents finding dependent libraries that
+ # are installed to the same prefix.
+ # At present, this check doesn't affect windows .dll's that
+ # are installed into $libdir/../bin (currently, that works fine)
+ # but it's something to keep an eye on.
+ test "$inst_prefix_dir" = "$destdir" && \
+ func_fatal_error "error: cannot install \`$file' to a directory not ending in $libdir"
+
+ if test -n "$inst_prefix_dir"; then
+ # Stick the inst_prefix_dir data into the link command.
+ relink_command=`$ECHO "X$relink_command" | $Xsed -e "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"`
+ else
+ relink_command=`$ECHO "X$relink_command" | $Xsed -e "s%@inst_prefix_dir@%%"`
+ fi
+
+ func_warning "relinking \`$file'"
+ func_show_eval "$relink_command" \
+ 'func_fatal_error "error: relink \`$file'\'' with the above command before installing it"'
+ fi
+
+ # See the names of the shared library.
+ set dummy $library_names; shift
+ if test -n "$1"; then
+ realname="$1"
+ shift
+
+ srcname="$realname"
+ test -n "$relink_command" && srcname="$realname"T
+
+ # Install the shared library and build the symlinks.
+ func_show_eval "$install_prog $dir/$srcname $destdir/$realname" \
+ 'exit $?'
+ tstripme="$stripme"
+ case $host_os in
+ cygwin* | mingw* | pw32* | cegcc*)
+ case $realname in
+ *.dll.a)
+ tstripme=""
+ ;;
+ esac
+ ;;
+ esac
+ if test -n "$tstripme" && test -n "$striplib"; then
+ func_show_eval "$striplib $destdir/$realname" 'exit $?'
+ fi
+
+ if test "$#" -gt 0; then
+ # Delete the old symlinks, and create new ones.
+ # Try `ln -sf' first, because the `ln' binary might depend on
+ # the symlink we replace! Solaris /bin/ln does not understand -f,
+ # so we also need to try rm && ln -s.
+ for linkname
+ do
+ test "$linkname" != "$realname" \
+ && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })"
+ done
+ fi
+
+ # Do each command in the postinstall commands.
+ lib="$destdir/$realname"
+ func_execute_cmds "$postinstall_cmds" 'exit $?'
+ fi
+
+ # Install the pseudo-library for information purposes.
+ func_basename "$file"
+ name="$func_basename_result"
+ instname="$dir/$name"i
+ func_show_eval "$install_prog $instname $destdir/$name" 'exit $?'
+
+ # Maybe install the static library, too.
+ test -n "$old_library" && staticlibs="$staticlibs $dir/$old_library"
+ ;;
+
+ *.lo)
+ # Install (i.e. copy) a libtool object.
+
+ # Figure out destination file name, if it wasn't already specified.
+ if test -n "$destname"; then
+ destfile="$destdir/$destname"
+ else
+ func_basename "$file"
+ destfile="$func_basename_result"
+ destfile="$destdir/$destfile"
+ fi
+
+ # Deduce the name of the destination old-style object file.
+ case $destfile in
+ *.lo)
+ func_lo2o "$destfile"
+ staticdest=$func_lo2o_result
+ ;;
+ *.$objext)
+ staticdest="$destfile"
+ destfile=
+ ;;
+ *)
+ func_fatal_help "cannot copy a libtool object to \`$destfile'"
+ ;;
+ esac
+
+ # Install the libtool object if requested.
+ test -n "$destfile" && \
+ func_show_eval "$install_prog $file $destfile" 'exit $?'
+
+ # Install the old object if enabled.
+ if test "$build_old_libs" = yes; then
+ # Deduce the name of the old-style object file.
+ func_lo2o "$file"
+ staticobj=$func_lo2o_result
+ func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?'
+ fi
+ exit $EXIT_SUCCESS
+ ;;
+
+ *)
+ # Figure out destination file name, if it wasn't already specified.
+ if test -n "$destname"; then
+ destfile="$destdir/$destname"
+ else
+ func_basename "$file"
+ destfile="$func_basename_result"
+ destfile="$destdir/$destfile"
+ fi
+
+ # If the file is missing, and there is a .exe on the end, strip it
+ # because it is most likely a libtool script we actually want to
+ # install
+ stripped_ext=""
+ case $file in
+ *.exe)
+ if test ! -f "$file"; then
+ func_stripname '' '.exe' "$file"
+ file=$func_stripname_result
+ stripped_ext=".exe"
+ fi
+ ;;
+ esac
+
+ # Do a test to see if this is really a libtool program.
+ case $host in
+ *cygwin* | *mingw*)
+ if func_ltwrapper_executable_p "$file"; then
+ func_ltwrapper_scriptname "$file"
+ wrapper=$func_ltwrapper_scriptname_result
+ else
+ func_stripname '' '.exe' "$file"
+ wrapper=$func_stripname_result
+ fi
+ ;;
+ *)
+ wrapper=$file
+ ;;
+ esac
+ if func_ltwrapper_script_p "$wrapper"; then
+ notinst_deplibs=
+ relink_command=
+
+ func_source "$wrapper"
+
+ # Check the variables that should have been set.
+ test -z "$generated_by_libtool_version" && \
+ func_fatal_error "invalid libtool wrapper script \`$wrapper'"
+
+ finalize=yes
+ for lib in $notinst_deplibs; do
+ # Check to see that each library is installed.
+ libdir=
+ if test -f "$lib"; then
+ func_source "$lib"
+ fi
+ libfile="$libdir/"`$ECHO "X$lib" | $Xsed -e 's%^.*/%%g'` ### testsuite: skip nested quoting test
+ if test -n "$libdir" && test ! -f "$libfile"; then
+ func_warning "\`$lib' has not been installed in \`$libdir'"
+ finalize=no
+ fi
+ done
+
+ relink_command=
+ func_source "$wrapper"
+
+ outputname=
+ if test "$fast_install" = no && test -n "$relink_command"; then
+ $opt_dry_run || {
+ if test "$finalize" = yes; then
+ tmpdir=`func_mktempdir`
+ func_basename "$file$stripped_ext"
+ file="$func_basename_result"
+ outputname="$tmpdir/$file"
+ # Replace the output file specification.
+ relink_command=`$ECHO "X$relink_command" | $Xsed -e 's%@OUTPUT@%'"$outputname"'%g'`
+
+ $opt_silent || {
+ func_quote_for_expand "$relink_command"
+ eval "func_echo $func_quote_for_expand_result"
+ }
+ if eval "$relink_command"; then :
+ else
+ func_error "error: relink \`$file' with the above command before installing it"
+ $opt_dry_run || ${RM}r "$tmpdir"
+ continue
+ fi
+ file="$outputname"
+ else
+ func_warning "cannot relink \`$file'"
+ fi
+ }
+ else
+ # Install the binary that we compiled earlier.
+ file=`$ECHO "X$file$stripped_ext" | $Xsed -e "s%\([^/]*\)$%$objdir/\1%"`
+ fi
+ fi
+
+ # remove .exe since cygwin /usr/bin/install will append another
+ # one anyway
+ case $install_prog,$host in
+ */usr/bin/install*,*cygwin*)
+ case $file:$destfile in
+ *.exe:*.exe)
+ # this is ok
+ ;;
+ *.exe:*)
+ destfile=$destfile.exe
+ ;;
+ *:*.exe)
+ func_stripname '' '.exe' "$destfile"
+ destfile=$func_stripname_result
+ ;;
+ esac
+ ;;
+ esac
+ func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?'
+ $opt_dry_run || if test -n "$outputname"; then
+ ${RM}r "$tmpdir"
+ fi
+ ;;
+ esac
+ done
+
+ for file in $staticlibs; do
+ func_basename "$file"
+ name="$func_basename_result"
+
+ # Set up the ranlib parameters.
+ oldlib="$destdir/$name"
+
+ func_show_eval "$install_prog \$file \$oldlib" 'exit $?'
+
+ if test -n "$stripme" && test -n "$old_striplib"; then
+ func_show_eval "$old_striplib $oldlib" 'exit $?'
+ fi
+
+ # Do each command in the postinstall commands.
+ func_execute_cmds "$old_postinstall_cmds" 'exit $?'
+ done
+
+ test -n "$future_libdirs" && \
+ func_warning "remember to run \`$progname --finish$future_libdirs'"
+
+ if test -n "$current_libdirs"; then
+ # Maybe just do a dry run.
+ $opt_dry_run && current_libdirs=" -n$current_libdirs"
+ exec_cmd='$SHELL $progpath $preserve_args --finish$current_libdirs'
+ else
+ exit $EXIT_SUCCESS
+ fi
+}
+
+test "$mode" = install && func_mode_install ${1+"$@"}
+
+
+# func_generate_dlsyms outputname originator pic_p
+# Extract symbols from dlprefiles and create ${outputname}S.o with
+# a dlpreopen symbol table.
+func_generate_dlsyms ()
+{
+ $opt_debug
+ my_outputname="$1"
+ my_originator="$2"
+ my_pic_p="${3-no}"
+ my_prefix=`$ECHO "$my_originator" | sed 's%[^a-zA-Z0-9]%_%g'`
+ my_dlsyms=
+
+ if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then
+ if test -n "$NM" && test -n "$global_symbol_pipe"; then
+ my_dlsyms="${my_outputname}S.c"
+ else
+ func_error "not configured to extract global symbols from dlpreopened files"
+ fi
+ fi
+
+ if test -n "$my_dlsyms"; then
+ case $my_dlsyms in
+ "") ;;
+ *.c)
+ # Discover the nlist of each of the dlfiles.
+ nlist="$output_objdir/${my_outputname}.nm"
+
+ func_show_eval "$RM $nlist ${nlist}S ${nlist}T"
+
+ # Parse the name list into a source file.
+ func_verbose "creating $output_objdir/$my_dlsyms"
+
+ $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\
+/* $my_dlsyms - symbol resolution table for \`$my_outputname' dlsym emulation. */
+/* Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION */
+
+#ifdef __cplusplus
+extern \"C\" {
+#endif
+
+/* External symbol declarations for the compiler. */\
+"
+
+ if test "$dlself" = yes; then
+ func_verbose "generating symbol list for \`$output'"
+
+ $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist"
+
+ # Add our own program objects to the symbol list.
+ progfiles=`$ECHO "X$objs$old_deplibs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP`
+ for progfile in $progfiles; do
+ func_verbose "extracting global C symbols from \`$progfile'"
+ $opt_dry_run || eval "$NM $progfile | $global_symbol_pipe >> '$nlist'"
+ done
+
+ if test -n "$exclude_expsyms"; then
+ $opt_dry_run || {
+ eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T'
+ eval '$MV "$nlist"T "$nlist"'
+ }
+ fi
+
+ if test -n "$export_symbols_regex"; then
+ $opt_dry_run || {
+ eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T'
+ eval '$MV "$nlist"T "$nlist"'
+ }
+ fi
+
+ # Prepare the list of exported symbols
+ if test -z "$export_symbols"; then
+ export_symbols="$output_objdir/$outputname.exp"
+ $opt_dry_run || {
+ $RM $export_symbols
+ eval "${SED} -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"'
+ case $host in
+ *cygwin* | *mingw* | *cegcc* )
+ eval "echo EXPORTS "'> "$output_objdir/$outputname.def"'
+ eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"'
+ ;;
+ esac
+ }
+ else
+ $opt_dry_run || {
+ eval "${SED} -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"'
+ eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T'
+ eval '$MV "$nlist"T "$nlist"'
+ case $host in
+ *cygwin | *mingw* | *cegcc* )
+ eval "echo EXPORTS "'> "$output_objdir/$outputname.def"'
+ eval 'cat "$nlist" >> "$output_objdir/$outputname.def"'
+ ;;
+ esac
+ }
+ fi
+ fi
+
+ for dlprefile in $dlprefiles; do
+ func_verbose "extracting global C symbols from \`$dlprefile'"
+ func_basename "$dlprefile"
+ name="$func_basename_result"
+ $opt_dry_run || {
+ eval '$ECHO ": $name " >> "$nlist"'
+ eval "$NM $dlprefile 2>/dev/null | $global_symbol_pipe >> '$nlist'"
+ }
+ done
+
+ $opt_dry_run || {
+ # Make sure we have at least an empty file.
+ test -f "$nlist" || : > "$nlist"
+
+ if test -n "$exclude_expsyms"; then
+ $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T
+ $MV "$nlist"T "$nlist"
+ fi
+
+ # Try sorting and uniquifying the output.
+ if $GREP -v "^: " < "$nlist" |
+ if sort -k 3 </dev/null >/dev/null 2>&1; then
+ sort -k 3
+ else
+ sort +2
+ fi |
+ uniq > "$nlist"S; then
+ :
+ else
+ $GREP -v "^: " < "$nlist" > "$nlist"S
+ fi
+
+ if test -f "$nlist"S; then
+ eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"'
+ else
+ $ECHO '/* NONE */' >> "$output_objdir/$my_dlsyms"
+ fi
+
+ $ECHO >> "$output_objdir/$my_dlsyms" "\
+
+/* The mapping between symbol names and symbols. */
+typedef struct {
+ const char *name;
+ void *address;
+} lt_dlsymlist;
+"
+ case $host in
+ *cygwin* | *mingw* | *cegcc* )
+ $ECHO >> "$output_objdir/$my_dlsyms" "\
+/* DATA imports from DLLs on WIN32 con't be const, because
+ runtime relocations are performed -- see ld's documentation
+ on pseudo-relocs. */"
+ lt_dlsym_const= ;;
+ *osf5*)
+ echo >> "$output_objdir/$my_dlsyms" "\
+/* This system does not cope well with relocations in const data */"
+ lt_dlsym_const= ;;
+ *)
+ lt_dlsym_const=const ;;
+ esac
+
+ $ECHO >> "$output_objdir/$my_dlsyms" "\
+extern $lt_dlsym_const lt_dlsymlist
+lt_${my_prefix}_LTX_preloaded_symbols[];
+$lt_dlsym_const lt_dlsymlist
+lt_${my_prefix}_LTX_preloaded_symbols[] =
+{\
+ { \"$my_originator\", (void *) 0 },"
+
+ case $need_lib_prefix in
+ no)
+ eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms"
+ ;;
+ *)
+ eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms"
+ ;;
+ esac
+ $ECHO >> "$output_objdir/$my_dlsyms" "\
+ {0, (void *) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+ return lt_${my_prefix}_LTX_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif\
+"
+ } # !$opt_dry_run
+
+ pic_flag_for_symtable=
+ case "$compile_command " in
+ *" -static "*) ;;
+ *)
+ case $host in
+ # compiling the symbol table file with pic_flag works around
+ # a FreeBSD bug that causes programs to crash when -lm is
+ # linked before any other PIC object. But we must not use
+ # pic_flag when linking with -static. The problem exists in
+ # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1.
+ *-*-freebsd2*|*-*-freebsd3.0*|*-*-freebsdelf3.0*)
+ pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;;
+ *-*-hpux*)
+ pic_flag_for_symtable=" $pic_flag" ;;
+ *)
+ if test "X$my_pic_p" != Xno; then
+ pic_flag_for_symtable=" $pic_flag"
+ fi
+ ;;
+ esac
+ ;;
+ esac
+ symtab_cflags=
+ for arg in $LTCFLAGS; do
+ case $arg in
+ -pie | -fpie | -fPIE) ;;
+ *) symtab_cflags="$symtab_cflags $arg" ;;
+ esac
+ done
+
+ # Now compile the dynamic symbol file.
+ func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?'
+
+ # Clean up the generated files.
+ func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T"'
+
+ # Transform the symbol file into the correct name.
+ symfileobj="$output_objdir/${my_outputname}S.$objext"
+ case $host in
+ *cygwin* | *mingw* | *cegcc* )
+ if test -f "$output_objdir/$my_outputname.def"; then
+ compile_command=`$ECHO "X$compile_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"`
+ finalize_command=`$ECHO "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"`
+ else
+ compile_command=`$ECHO "X$compile_command" | $Xsed -e "s%@SYMFILE@%$symfileobj%"`
+ finalize_command=`$ECHO "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$symfileobj%"`
+ fi
+ ;;
+ *)
+ compile_command=`$ECHO "X$compile_command" | $Xsed -e "s%@SYMFILE@%$symfileobj%"`
+ finalize_command=`$ECHO "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$symfileobj%"`
+ ;;
+ esac
+ ;;
+ *)
+ func_fatal_error "unknown suffix for \`$my_dlsyms'"
+ ;;
+ esac
+ else
+ # We keep going just in case the user didn't refer to
+ # lt_preloaded_symbols. The linker will fail if global_symbol_pipe
+ # really was required.
+
+ # Nullify the symbol file.
+ compile_command=`$ECHO "X$compile_command" | $Xsed -e "s% @SYMFILE@%%"`
+ finalize_command=`$ECHO "X$finalize_command" | $Xsed -e "s% @SYMFILE@%%"`
+ fi
+}
+
+# func_win32_libid arg
+# return the library type of file 'arg'
+#
+# Need a lot of goo to handle *both* DLLs and import libs
+# Has to be a shell function in order to 'eat' the argument
+# that is supplied when $file_magic_command is called.
+func_win32_libid ()
+{
+ $opt_debug
+ win32_libid_type="unknown"
+ win32_fileres=`file -L $1 2>/dev/null`
+ case $win32_fileres in
+ *ar\ archive\ import\ library*) # definitely import
+ win32_libid_type="x86 archive import"
+ ;;
+ *ar\ archive*) # could be an import, or static
+ if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null |
+ $EGREP 'file format pe-i386(.*architecture: i386)?' >/dev/null ; then
+ win32_nmres=`eval $NM -f posix -A $1 |
+ $SED -n -e '
+ 1,100{
+ / I /{
+ s,.*,import,
+ p
+ q
+ }
+ }'`
+ case $win32_nmres in
+ import*) win32_libid_type="x86 archive import";;
+ *) win32_libid_type="x86 archive static";;
+ esac
+ fi
+ ;;
+ *DLL*)
+ win32_libid_type="x86 DLL"
+ ;;
+ *executable*) # but shell scripts are "executable" too...
+ case $win32_fileres in
+ *MS\ Windows\ PE\ Intel*)
+ win32_libid_type="x86 DLL"
+ ;;
+ esac
+ ;;
+ esac
+ $ECHO "$win32_libid_type"
+}
+
+
+
+# func_extract_an_archive dir oldlib
+func_extract_an_archive ()
+{
+ $opt_debug
+ f_ex_an_ar_dir="$1"; shift
+ f_ex_an_ar_oldlib="$1"
+ func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" 'exit $?'
+ if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then
+ :
+ else
+ func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib"
+ fi
+}
+
+
+# func_extract_archives gentop oldlib ...
+func_extract_archives ()
+{
+ $opt_debug
+ my_gentop="$1"; shift
+ my_oldlibs=${1+"$@"}
+ my_oldobjs=""
+ my_xlib=""
+ my_xabs=""
+ my_xdir=""
+
+ for my_xlib in $my_oldlibs; do
+ # Extract the objects.
+ case $my_xlib in
+ [\\/]* | [A-Za-z]:[\\/]*) my_xabs="$my_xlib" ;;
+ *) my_xabs=`pwd`"/$my_xlib" ;;
+ esac
+ func_basename "$my_xlib"
+ my_xlib="$func_basename_result"
+ my_xlib_u=$my_xlib
+ while :; do
+ case " $extracted_archives " in
+ *" $my_xlib_u "*)
+ func_arith $extracted_serial + 1
+ extracted_serial=$func_arith_result
+ my_xlib_u=lt$extracted_serial-$my_xlib ;;
+ *) break ;;
+ esac
+ done
+ extracted_archives="$extracted_archives $my_xlib_u"
+ my_xdir="$my_gentop/$my_xlib_u"
+
+ func_mkdir_p "$my_xdir"
+
+ case $host in
+ *-darwin*)
+ func_verbose "Extracting $my_xabs"
+ # Do not bother doing anything if just a dry run
+ $opt_dry_run || {
+ darwin_orig_dir=`pwd`
+ cd $my_xdir || exit $?
+ darwin_archive=$my_xabs
+ darwin_curdir=`pwd`
+ darwin_base_archive=`basename "$darwin_archive"`
+ darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true`
+ if test -n "$darwin_arches"; then
+ darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'`
+ darwin_arch=
+ func_verbose "$darwin_base_archive has multiple architectures $darwin_arches"
+ for darwin_arch in $darwin_arches ; do
+ func_mkdir_p "unfat-$$/${darwin_base_archive}-${darwin_arch}"
+ $LIPO -thin $darwin_arch -output "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" "${darwin_archive}"
+ cd "unfat-$$/${darwin_base_archive}-${darwin_arch}"
+ func_extract_an_archive "`pwd`" "${darwin_base_archive}"
+ cd "$darwin_curdir"
+ $RM "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}"
+ done # $darwin_arches
+ ## Okay now we've a bunch of thin objects, gotta fatten them up :)
+ darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$basename" | sort -u`
+ darwin_file=
+ darwin_files=
+ for darwin_file in $darwin_filelist; do
+ darwin_files=`find unfat-$$ -name $darwin_file -print | $NL2SP`
+ $LIPO -create -output "$darwin_file" $darwin_files
+ done # $darwin_filelist
+ $RM -rf unfat-$$
+ cd "$darwin_orig_dir"
+ else
+ cd $darwin_orig_dir
+ func_extract_an_archive "$my_xdir" "$my_xabs"
+ fi # $darwin_arches
+ } # !$opt_dry_run
+ ;;
+ *)
+ func_extract_an_archive "$my_xdir" "$my_xabs"
+ ;;
+ esac
+ my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | $NL2SP`
+ done
+
+ func_extract_archives_result="$my_oldobjs"
+}
+
+
+
+# func_emit_wrapper_part1 [arg=no]
+#
+# Emit the first part of a libtool wrapper script on stdout.
+# For more information, see the description associated with
+# func_emit_wrapper(), below.
+func_emit_wrapper_part1 ()
+{
+ func_emit_wrapper_part1_arg1=no
+ if test -n "$1" ; then
+ func_emit_wrapper_part1_arg1=$1
+ fi
+
+ $ECHO "\
+#! $SHELL
+
+# $output - temporary wrapper script for $objdir/$outputname
+# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION
+#
+# The $output program cannot be directly executed until all the libtool
+# libraries that it depends on are installed.
+#
+# This wrapper script should never be moved out of the build directory.
+# If it is, it will not operate correctly.
+
+# Sed substitution that helps us do robust quoting. It backslashifies
+# metacharacters that are still active within double-quoted strings.
+Xsed='${SED} -e 1s/^X//'
+sed_quote_subst='$sed_quote_subst'
+
+# Be Bourne compatible
+if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '\${1+\"\$@\"}'='\"\$@\"'
+ setopt NO_GLOB_SUBST
+else
+ case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac
+fi
+BIN_SH=xpg4; export BIN_SH # for Tru64
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+relink_command=\"$relink_command\"
+
+# This environment variable determines our operation mode.
+if test \"\$libtool_install_magic\" = \"$magic\"; then
+ # install mode needs the following variables:
+ generated_by_libtool_version='$macro_version'
+ notinst_deplibs='$notinst_deplibs'
+else
+ # When we are sourced in execute mode, \$file and \$ECHO are already set.
+ if test \"\$libtool_execute_magic\" != \"$magic\"; then
+ ECHO=\"$qecho\"
+ file=\"\$0\"
+ # Make sure echo works.
+ if test \"X\$1\" = X--no-reexec; then
+ # Discard the --no-reexec flag, and continue.
+ shift
+ elif test \"X\`{ \$ECHO '\t'; } 2>/dev/null\`\" = 'X\t'; then
+ # Yippee, \$ECHO works!
+ :
+ else
+ # Restart under the correct shell, and then maybe \$ECHO will work.
+ exec $SHELL \"\$0\" --no-reexec \${1+\"\$@\"}
+ fi
+ fi\
+"
+ $ECHO "\
+
+ # Find the directory that this script lives in.
+ thisdir=\`\$ECHO \"X\$file\" | \$Xsed -e 's%/[^/]*$%%'\`
+ test \"x\$thisdir\" = \"x\$file\" && thisdir=.
+
+ # Follow symbolic links until we get to the real thisdir.
+ file=\`ls -ld \"\$file\" | ${SED} -n 's/.*-> //p'\`
+ while test -n \"\$file\"; do
+ destdir=\`\$ECHO \"X\$file\" | \$Xsed -e 's%/[^/]*\$%%'\`
+
+ # If there was a directory component, then change thisdir.
+ if test \"x\$destdir\" != \"x\$file\"; then
+ case \"\$destdir\" in
+ [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;;
+ *) thisdir=\"\$thisdir/\$destdir\" ;;
+ esac
+ fi
+
+ file=\`\$ECHO \"X\$file\" | \$Xsed -e 's%^.*/%%'\`
+ file=\`ls -ld \"\$thisdir/\$file\" | ${SED} -n 's/.*-> //p'\`
+ done
+"
+}
+# end: func_emit_wrapper_part1
+
+# func_emit_wrapper_part2 [arg=no]
+#
+# Emit the second part of a libtool wrapper script on stdout.
+# For more information, see the description associated with
+# func_emit_wrapper(), below.
+func_emit_wrapper_part2 ()
+{
+ func_emit_wrapper_part2_arg1=no
+ if test -n "$1" ; then
+ func_emit_wrapper_part2_arg1=$1
+ fi
+
+ $ECHO "\
+
+ # Usually 'no', except on cygwin/mingw when embedded into
+ # the cwrapper.
+ WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_part2_arg1
+ if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then
+ # special case for '.'
+ if test \"\$thisdir\" = \".\"; then
+ thisdir=\`pwd\`
+ fi
+ # remove .libs from thisdir
+ case \"\$thisdir\" in
+ *[\\\\/]$objdir ) thisdir=\`\$ECHO \"X\$thisdir\" | \$Xsed -e 's%[\\\\/][^\\\\/]*$%%'\` ;;
+ $objdir ) thisdir=. ;;
+ esac
+ fi
+
+ # Try to get the absolute directory name.
+ absdir=\`cd \"\$thisdir\" && pwd\`
+ test -n \"\$absdir\" && thisdir=\"\$absdir\"
+"
+
+ if test "$fast_install" = yes; then
+ $ECHO "\
+ program=lt-'$outputname'$exeext
+ progdir=\"\$thisdir/$objdir\"
+
+ if test ! -f \"\$progdir/\$program\" ||
+ { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | ${SED} 1q\`; \\
+ test \"X\$file\" != \"X\$progdir/\$program\"; }; then
+
+ file=\"\$\$-\$program\"
+
+ if test ! -d \"\$progdir\"; then
+ $MKDIR \"\$progdir\"
+ else
+ $RM \"\$progdir/\$file\"
+ fi"
+
+ $ECHO "\
+
+ # relink executable if necessary
+ if test -n \"\$relink_command\"; then
+ if relink_command_output=\`eval \$relink_command 2>&1\`; then :
+ else
+ $ECHO \"\$relink_command_output\" >&2
+ $RM \"\$progdir/\$file\"
+ exit 1
+ fi
+ fi
+
+ $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null ||
+ { $RM \"\$progdir/\$program\";
+ $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; }
+ $RM \"\$progdir/\$file\"
+ fi"
+ else
+ $ECHO "\
+ program='$outputname'
+ progdir=\"\$thisdir/$objdir\"
+"
+ fi
+
+ $ECHO "\
+
+ if test -f \"\$progdir/\$program\"; then"
+
+ # Export our shlibpath_var if we have one.
+ if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then
+ $ECHO "\
+ # Add our own library path to $shlibpath_var
+ $shlibpath_var=\"$temp_rpath\$$shlibpath_var\"
+
+ # Some systems cannot cope with colon-terminated $shlibpath_var
+ # The second colon is a workaround for a bug in BeOS R4 sed
+ $shlibpath_var=\`\$ECHO \"X\$$shlibpath_var\" | \$Xsed -e 's/::*\$//'\`
+
+ export $shlibpath_var
+"
+ fi
+
+ # fixup the dll searchpath if we need to.
+ if test -n "$dllsearchpath"; then
+ $ECHO "\
+ # Add the dll search path components to the executable PATH
+ PATH=$dllsearchpath:\$PATH
+"
+ fi
+
+ $ECHO "\
+ if test \"\$libtool_execute_magic\" != \"$magic\"; then
+ # Run the actual program with our arguments.
+"
+ case $host in
+ # Backslashes separate directories on plain windows
+ *-*-mingw | *-*-os2* | *-cegcc*)
+ $ECHO "\
+ exec \"\$progdir\\\\\$program\" \${1+\"\$@\"}
+"
+ ;;
+
+ *)
+ $ECHO "\
+ exec \"\$progdir/\$program\" \${1+\"\$@\"}
+"
+ ;;
+ esac
+ $ECHO "\
+ \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2
+ exit 1
+ fi
+ else
+ # The program doesn't exist.
+ \$ECHO \"\$0: error: \\\`\$progdir/\$program' does not exist\" 1>&2
+ \$ECHO \"This script is just a wrapper for \$program.\" 1>&2
+ $ECHO \"See the $PACKAGE documentation for more information.\" 1>&2
+ exit 1
+ fi
+fi\
+"
+}
+# end: func_emit_wrapper_part2
+
+
+# func_emit_wrapper [arg=no]
+#
+# Emit a libtool wrapper script on stdout.
+# Don't directly open a file because we may want to
+# incorporate the script contents within a cygwin/mingw
+# wrapper executable. Must ONLY be called from within
+# func_mode_link because it depends on a number of variables
+# set therein.
+#
+# ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR
+# variable will take. If 'yes', then the emitted script
+# will assume that the directory in which it is stored is
+# the $objdir directory. This is a cygwin/mingw-specific
+# behavior.
+func_emit_wrapper ()
+{
+ func_emit_wrapper_arg1=no
+ if test -n "$1" ; then
+ func_emit_wrapper_arg1=$1
+ fi
+
+ # split this up so that func_emit_cwrapperexe_src
+ # can call each part independently.
+ func_emit_wrapper_part1 "${func_emit_wrapper_arg1}"
+ func_emit_wrapper_part2 "${func_emit_wrapper_arg1}"
+}
+
+
+# func_to_host_path arg
+#
+# Convert paths to host format when used with build tools.
+# Intended for use with "native" mingw (where libtool itself
+# is running under the msys shell), or in the following cross-
+# build environments:
+# $build $host
+# mingw (msys) mingw [e.g. native]
+# cygwin mingw
+# *nix + wine mingw
+# where wine is equipped with the `winepath' executable.
+# In the native mingw case, the (msys) shell automatically
+# converts paths for any non-msys applications it launches,
+# but that facility isn't available from inside the cwrapper.
+# Similar accommodations are necessary for $host mingw and
+# $build cygwin. Calling this function does no harm for other
+# $host/$build combinations not listed above.
+#
+# ARG is the path (on $build) that should be converted to
+# the proper representation for $host. The result is stored
+# in $func_to_host_path_result.
+func_to_host_path ()
+{
+ func_to_host_path_result="$1"
+ if test -n "$1" ; then
+ case $host in
+ *mingw* )
+ lt_sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g'
+ case $build in
+ *mingw* ) # actually, msys
+ # awkward: cmd appends spaces to result
+ lt_sed_strip_trailing_spaces="s/[ ]*\$//"
+ func_to_host_path_tmp1=`( cmd //c echo "$1" |\
+ $SED -e "$lt_sed_strip_trailing_spaces" ) 2>/dev/null || echo ""`
+ func_to_host_path_result=`echo "$func_to_host_path_tmp1" |\
+ $SED -e "$lt_sed_naive_backslashify"`
+ ;;
+ *cygwin* )
+ func_to_host_path_tmp1=`cygpath -w "$1"`
+ func_to_host_path_result=`echo "$func_to_host_path_tmp1" |\
+ $SED -e "$lt_sed_naive_backslashify"`
+ ;;
+ * )
+ # Unfortunately, winepath does not exit with a non-zero
+ # error code, so we are forced to check the contents of
+ # stdout. On the other hand, if the command is not
+ # found, the shell will set an exit code of 127 and print
+ # *an error message* to stdout. So we must check for both
+ # error code of zero AND non-empty stdout, which explains
+ # the odd construction:
+ func_to_host_path_tmp1=`winepath -w "$1" 2>/dev/null`
+ if test "$?" -eq 0 && test -n "${func_to_host_path_tmp1}"; then
+ func_to_host_path_result=`echo "$func_to_host_path_tmp1" |\
+ $SED -e "$lt_sed_naive_backslashify"`
+ else
+ # Allow warning below.
+ func_to_host_path_result=""
+ fi
+ ;;
+ esac
+ if test -z "$func_to_host_path_result" ; then
+ func_error "Could not determine host path corresponding to"
+ func_error " '$1'"
+ func_error "Continuing, but uninstalled executables may not work."
+ # Fallback:
+ func_to_host_path_result="$1"
+ fi
+ ;;
+ esac
+ fi
+}
+# end: func_to_host_path
+
+# func_to_host_pathlist arg
+#
+# Convert pathlists to host format when used with build tools.
+# See func_to_host_path(), above. This function supports the
+# following $build/$host combinations (but does no harm for
+# combinations not listed here):
+# $build $host
+# mingw (msys) mingw [e.g. native]
+# cygwin mingw
+# *nix + wine mingw
+#
+# Path separators are also converted from $build format to
+# $host format. If ARG begins or ends with a path separator
+# character, it is preserved (but converted to $host format)
+# on output.
+#
+# ARG is a pathlist (on $build) that should be converted to
+# the proper representation on $host. The result is stored
+# in $func_to_host_pathlist_result.
+func_to_host_pathlist ()
+{
+ func_to_host_pathlist_result="$1"
+ if test -n "$1" ; then
+ case $host in
+ *mingw* )
+ lt_sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g'
+ # Remove leading and trailing path separator characters from
+ # ARG. msys behavior is inconsistent here, cygpath turns them
+ # into '.;' and ';.', and winepath ignores them completely.
+ func_to_host_pathlist_tmp2="$1"
+ # Once set for this call, this variable should not be
+ # reassigned. It is used in tha fallback case.
+ func_to_host_pathlist_tmp1=`echo "$func_to_host_pathlist_tmp2" |\
+ $SED -e 's|^:*||' -e 's|:*$||'`
+ case $build in
+ *mingw* ) # Actually, msys.
+ # Awkward: cmd appends spaces to result.
+ lt_sed_strip_trailing_spaces="s/[ ]*\$//"
+ func_to_host_pathlist_tmp2=`( cmd //c echo "$func_to_host_pathlist_tmp1" |\
+ $SED -e "$lt_sed_strip_trailing_spaces" ) 2>/dev/null || echo ""`
+ func_to_host_pathlist_result=`echo "$func_to_host_pathlist_tmp2" |\
+ $SED -e "$lt_sed_naive_backslashify"`
+ ;;
+ *cygwin* )
+ func_to_host_pathlist_tmp2=`cygpath -w -p "$func_to_host_pathlist_tmp1"`
+ func_to_host_pathlist_result=`echo "$func_to_host_pathlist_tmp2" |\
+ $SED -e "$lt_sed_naive_backslashify"`
+ ;;
+ * )
+ # unfortunately, winepath doesn't convert pathlists
+ func_to_host_pathlist_result=""
+ func_to_host_pathlist_oldIFS=$IFS
+ IFS=:
+ for func_to_host_pathlist_f in $func_to_host_pathlist_tmp1 ; do
+ IFS=$func_to_host_pathlist_oldIFS
+ if test -n "$func_to_host_pathlist_f" ; then
+ func_to_host_path "$func_to_host_pathlist_f"
+ if test -n "$func_to_host_path_result" ; then
+ if test -z "$func_to_host_pathlist_result" ; then
+ func_to_host_pathlist_result="$func_to_host_path_result"
+ else
+ func_to_host_pathlist_result="$func_to_host_pathlist_result;$func_to_host_path_result"
+ fi
+ fi
+ fi
+ IFS=:
+ done
+ IFS=$func_to_host_pathlist_oldIFS
+ ;;
+ esac
+ if test -z "$func_to_host_pathlist_result" ; then
+ func_error "Could not determine the host path(s) corresponding to"
+ func_error " '$1'"
+ func_error "Continuing, but uninstalled executables may not work."
+ # Fallback. This may break if $1 contains DOS-style drive
+ # specifications. The fix is not to complicate the expression
+ # below, but for the user to provide a working wine installation
+ # with winepath so that path translation in the cross-to-mingw
+ # case works properly.
+ lt_replace_pathsep_nix_to_dos="s|:|;|g"
+ func_to_host_pathlist_result=`echo "$func_to_host_pathlist_tmp1" |\
+ $SED -e "$lt_replace_pathsep_nix_to_dos"`
+ fi
+ # Now, add the leading and trailing path separators back
+ case "$1" in
+ :* ) func_to_host_pathlist_result=";$func_to_host_pathlist_result"
+ ;;
+ esac
+ case "$1" in
+ *: ) func_to_host_pathlist_result="$func_to_host_pathlist_result;"
+ ;;
+ esac
+ ;;
+ esac
+ fi
+}
+# end: func_to_host_pathlist
+
+# func_emit_cwrapperexe_src
+# emit the source code for a wrapper executable on stdout
+# Must ONLY be called from within func_mode_link because
+# it depends on a number of variable set therein.
+func_emit_cwrapperexe_src ()
+{
+ cat <<EOF
+
+/* $cwrappersource - temporary wrapper executable for $objdir/$outputname
+ Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION
+
+ The $output program cannot be directly executed until all the libtool
+ libraries that it depends on are installed.
+
+ This wrapper executable should never be moved out of the build directory.
+ If it is, it will not operate correctly.
+
+ Currently, it simply execs the wrapper *script* "$SHELL $output",
+ but could eventually absorb all of the scripts functionality and
+ exec $objdir/$outputname directly.
+*/
+EOF
+ cat <<"EOF"
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef _MSC_VER
+# include <direct.h>
+# include <process.h>
+# include <io.h>
+# define setmode _setmode
+#else
+# include <unistd.h>
+# include <stdint.h>
+# ifdef __CYGWIN__
+# include <io.h>
+# define HAVE_SETENV
+# ifdef __STRICT_ANSI__
+char *realpath (const char *, char *);
+int putenv (char *);
+int setenv (const char *, const char *, int);
+# endif
+# endif
+#endif
+#include <malloc.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#if defined(PATH_MAX)
+# define LT_PATHMAX PATH_MAX
+#elif defined(MAXPATHLEN)
+# define LT_PATHMAX MAXPATHLEN
+#else
+# define LT_PATHMAX 1024
+#endif
+
+#ifndef S_IXOTH
+# define S_IXOTH 0
+#endif
+#ifndef S_IXGRP
+# define S_IXGRP 0
+#endif
+
+#ifdef _MSC_VER
+# define S_IXUSR _S_IEXEC
+# define stat _stat
+# ifndef _INTPTR_T_DEFINED
+# define intptr_t int
+# endif
+#endif
+
+#ifndef DIR_SEPARATOR
+# define DIR_SEPARATOR '/'
+# define PATH_SEPARATOR ':'
+#endif
+
+#if defined (_WIN32) || defined (__MSDOS__) || defined (__DJGPP__) || \
+ defined (__OS2__)
+# define HAVE_DOS_BASED_FILE_SYSTEM
+# define FOPEN_WB "wb"
+# ifndef DIR_SEPARATOR_2
+# define DIR_SEPARATOR_2 '\\'
+# endif
+# ifndef PATH_SEPARATOR_2
+# define PATH_SEPARATOR_2 ';'
+# endif
+#endif
+
+#ifndef DIR_SEPARATOR_2
+# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR)
+#else /* DIR_SEPARATOR_2 */
+# define IS_DIR_SEPARATOR(ch) \
+ (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2))
+#endif /* DIR_SEPARATOR_2 */
+
+#ifndef PATH_SEPARATOR_2
+# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR)
+#else /* PATH_SEPARATOR_2 */
+# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2)
+#endif /* PATH_SEPARATOR_2 */
+
+#ifdef __CYGWIN__
+# define FOPEN_WB "wb"
+#endif
+
+#ifndef FOPEN_WB
+# define FOPEN_WB "w"
+#endif
+#ifndef _O_BINARY
+# define _O_BINARY 0
+#endif
+
+#define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type)))
+#define XFREE(stale) do { \
+ if (stale) { free ((void *) stale); stale = 0; } \
+} while (0)
+
+#undef LTWRAPPER_DEBUGPRINTF
+#if defined DEBUGWRAPPER
+# define LTWRAPPER_DEBUGPRINTF(args) ltwrapper_debugprintf args
+static void
+ltwrapper_debugprintf (const char *fmt, ...)
+{
+ va_list args;
+ va_start (args, fmt);
+ (void) vfprintf (stderr, fmt, args);
+ va_end (args);
+}
+#else
+# define LTWRAPPER_DEBUGPRINTF(args)
+#endif
+
+const char *program_name = NULL;
+
+void *xmalloc (size_t num);
+char *xstrdup (const char *string);
+const char *base_name (const char *name);
+char *find_executable (const char *wrapper);
+char *chase_symlinks (const char *pathspec);
+int make_executable (const char *path);
+int check_executable (const char *path);
+char *strendzap (char *str, const char *pat);
+void lt_fatal (const char *message, ...);
+void lt_setenv (const char *name, const char *value);
+char *lt_extend_str (const char *orig_value, const char *add, int to_end);
+void lt_opt_process_env_set (const char *arg);
+void lt_opt_process_env_prepend (const char *arg);
+void lt_opt_process_env_append (const char *arg);
+int lt_split_name_value (const char *arg, char** name, char** value);
+void lt_update_exe_path (const char *name, const char *value);
+void lt_update_lib_path (const char *name, const char *value);
+
+static const char *script_text_part1 =
+EOF
+
+ func_emit_wrapper_part1 yes |
+ $SED -e 's/\([\\"]\)/\\\1/g' \
+ -e 's/^/ "/' -e 's/$/\\n"/'
+ echo ";"
+ cat <<EOF
+
+static const char *script_text_part2 =
+EOF
+ func_emit_wrapper_part2 yes |
+ $SED -e 's/\([\\"]\)/\\\1/g' \
+ -e 's/^/ "/' -e 's/$/\\n"/'
+ echo ";"
+
+ cat <<EOF
+const char * MAGIC_EXE = "$magic_exe";
+const char * LIB_PATH_VARNAME = "$shlibpath_var";
+EOF
+
+ if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then
+ func_to_host_pathlist "$temp_rpath"
+ cat <<EOF
+const char * LIB_PATH_VALUE = "$func_to_host_pathlist_result";
+EOF
+ else
+ cat <<"EOF"
+const char * LIB_PATH_VALUE = "";
+EOF
+ fi
+
+ if test -n "$dllsearchpath"; then
+ func_to_host_pathlist "$dllsearchpath:"
+ cat <<EOF
+const char * EXE_PATH_VARNAME = "PATH";
+const char * EXE_PATH_VALUE = "$func_to_host_pathlist_result";
+EOF
+ else
+ cat <<"EOF"
+const char * EXE_PATH_VARNAME = "";
+const char * EXE_PATH_VALUE = "";
+EOF
+ fi
+
+ if test "$fast_install" = yes; then
+ cat <<EOF
+const char * TARGET_PROGRAM_NAME = "lt-$outputname"; /* hopefully, no .exe */
+EOF
+ else
+ cat <<EOF
+const char * TARGET_PROGRAM_NAME = "$outputname"; /* hopefully, no .exe */
+EOF
+ fi
+
+
+ cat <<"EOF"
+
+#define LTWRAPPER_OPTION_PREFIX "--lt-"
+#define LTWRAPPER_OPTION_PREFIX_LENGTH 5
+
+static const size_t opt_prefix_len = LTWRAPPER_OPTION_PREFIX_LENGTH;
+static const char *ltwrapper_option_prefix = LTWRAPPER_OPTION_PREFIX;
+
+static const char *dumpscript_opt = LTWRAPPER_OPTION_PREFIX "dump-script";
+
+static const size_t env_set_opt_len = LTWRAPPER_OPTION_PREFIX_LENGTH + 7;
+static const char *env_set_opt = LTWRAPPER_OPTION_PREFIX "env-set";
+ /* argument is putenv-style "foo=bar", value of foo is set to bar */
+
+static const size_t env_prepend_opt_len = LTWRAPPER_OPTION_PREFIX_LENGTH + 11;
+static const char *env_prepend_opt = LTWRAPPER_OPTION_PREFIX "env-prepend";
+ /* argument is putenv-style "foo=bar", new value of foo is bar${foo} */
+
+static const size_t env_append_opt_len = LTWRAPPER_OPTION_PREFIX_LENGTH + 10;
+static const char *env_append_opt = LTWRAPPER_OPTION_PREFIX "env-append";
+ /* argument is putenv-style "foo=bar", new value of foo is ${foo}bar */
+
+int
+main (int argc, char *argv[])
+{
+ char **newargz;
+ int newargc;
+ char *tmp_pathspec;
+ char *actual_cwrapper_path;
+ char *actual_cwrapper_name;
+ char *target_name;
+ char *lt_argv_zero;
+ intptr_t rval = 127;
+
+ int i;
+
+ program_name = (char *) xstrdup (base_name (argv[0]));
+ LTWRAPPER_DEBUGPRINTF (("(main) argv[0] : %s\n", argv[0]));
+ LTWRAPPER_DEBUGPRINTF (("(main) program_name : %s\n", program_name));
+
+ /* very simple arg parsing; don't want to rely on getopt */
+ for (i = 1; i < argc; i++)
+ {
+ if (strcmp (argv[i], dumpscript_opt) == 0)
+ {
+EOF
+ case "$host" in
+ *mingw* | *cygwin* )
+ # make stdout use "unix" line endings
+ echo " setmode(1,_O_BINARY);"
+ ;;
+ esac
+
+ cat <<"EOF"
+ printf ("%s", script_text_part1);
+ printf ("%s", script_text_part2);
+ return 0;
+ }
+ }
+
+ newargz = XMALLOC (char *, argc + 1);
+ tmp_pathspec = find_executable (argv[0]);
+ if (tmp_pathspec == NULL)
+ lt_fatal ("Couldn't find %s", argv[0]);
+ LTWRAPPER_DEBUGPRINTF (("(main) found exe (before symlink chase) at : %s\n",
+ tmp_pathspec));
+
+ actual_cwrapper_path = chase_symlinks (tmp_pathspec);
+ LTWRAPPER_DEBUGPRINTF (("(main) found exe (after symlink chase) at : %s\n",
+ actual_cwrapper_path));
+ XFREE (tmp_pathspec);
+
+ actual_cwrapper_name = xstrdup( base_name (actual_cwrapper_path));
+ strendzap (actual_cwrapper_path, actual_cwrapper_name);
+
+ /* wrapper name transforms */
+ strendzap (actual_cwrapper_name, ".exe");
+ tmp_pathspec = lt_extend_str (actual_cwrapper_name, ".exe", 1);
+ XFREE (actual_cwrapper_name);
+ actual_cwrapper_name = tmp_pathspec;
+ tmp_pathspec = 0;
+
+ /* target_name transforms -- use actual target program name; might have lt- prefix */
+ target_name = xstrdup (base_name (TARGET_PROGRAM_NAME));
+ strendzap (target_name, ".exe");
+ tmp_pathspec = lt_extend_str (target_name, ".exe", 1);
+ XFREE (target_name);
+ target_name = tmp_pathspec;
+ tmp_pathspec = 0;
+
+ LTWRAPPER_DEBUGPRINTF (("(main) libtool target name: %s\n",
+ target_name));
+EOF
+
+ cat <<EOF
+ newargz[0] =
+ XMALLOC (char, (strlen (actual_cwrapper_path) +
+ strlen ("$objdir") + 1 + strlen (actual_cwrapper_name) + 1));
+ strcpy (newargz[0], actual_cwrapper_path);
+ strcat (newargz[0], "$objdir");
+ strcat (newargz[0], "/");
+EOF
+
+ cat <<"EOF"
+ /* stop here, and copy so we don't have to do this twice */
+ tmp_pathspec = xstrdup (newargz[0]);
+
+ /* do NOT want the lt- prefix here, so use actual_cwrapper_name */
+ strcat (newargz[0], actual_cwrapper_name);
+
+ /* DO want the lt- prefix here if it exists, so use target_name */
+ lt_argv_zero = lt_extend_str (tmp_pathspec, target_name, 1);
+ XFREE (tmp_pathspec);
+ tmp_pathspec = NULL;
+EOF
+
+ case $host_os in
+ mingw*)
+ cat <<"EOF"
+ {
+ char* p;
+ while ((p = strchr (newargz[0], '\\')) != NULL)
+ {
+ *p = '/';
+ }
+ while ((p = strchr (lt_argv_zero, '\\')) != NULL)
+ {
+ *p = '/';
+ }
+ }
+EOF
+ ;;
+ esac
+
+ cat <<"EOF"
+ XFREE (target_name);
+ XFREE (actual_cwrapper_path);
+ XFREE (actual_cwrapper_name);
+
+ lt_setenv ("BIN_SH", "xpg4"); /* for Tru64 */
+ lt_setenv ("DUALCASE", "1"); /* for MSK sh */
+ lt_update_lib_path (LIB_PATH_VARNAME, LIB_PATH_VALUE);
+ lt_update_exe_path (EXE_PATH_VARNAME, EXE_PATH_VALUE);
+
+ newargc=0;
+ for (i = 1; i < argc; i++)
+ {
+ if (strncmp (argv[i], env_set_opt, env_set_opt_len) == 0)
+ {
+ if (argv[i][env_set_opt_len] == '=')
+ {
+ const char *p = argv[i] + env_set_opt_len + 1;
+ lt_opt_process_env_set (p);
+ }
+ else if (argv[i][env_set_opt_len] == '\0' && i + 1 < argc)
+ {
+ lt_opt_process_env_set (argv[++i]); /* don't copy */
+ }
+ else
+ lt_fatal ("%s missing required argument", env_set_opt);
+ continue;
+ }
+ if (strncmp (argv[i], env_prepend_opt, env_prepend_opt_len) == 0)
+ {
+ if (argv[i][env_prepend_opt_len] == '=')
+ {
+ const char *p = argv[i] + env_prepend_opt_len + 1;
+ lt_opt_process_env_prepend (p);
+ }
+ else if (argv[i][env_prepend_opt_len] == '\0' && i + 1 < argc)
+ {
+ lt_opt_process_env_prepend (argv[++i]); /* don't copy */
+ }
+ else
+ lt_fatal ("%s missing required argument", env_prepend_opt);
+ continue;
+ }
+ if (strncmp (argv[i], env_append_opt, env_append_opt_len) == 0)
+ {
+ if (argv[i][env_append_opt_len] == '=')
+ {
+ const char *p = argv[i] + env_append_opt_len + 1;
+ lt_opt_process_env_append (p);
+ }
+ else if (argv[i][env_append_opt_len] == '\0' && i + 1 < argc)
+ {
+ lt_opt_process_env_append (argv[++i]); /* don't copy */
+ }
+ else
+ lt_fatal ("%s missing required argument", env_append_opt);
+ continue;
+ }
+ if (strncmp (argv[i], ltwrapper_option_prefix, opt_prefix_len) == 0)
+ {
+ /* however, if there is an option in the LTWRAPPER_OPTION_PREFIX
+ namespace, but it is not one of the ones we know about and
+ have already dealt with, above (inluding dump-script), then
+ report an error. Otherwise, targets might begin to believe
+ they are allowed to use options in the LTWRAPPER_OPTION_PREFIX
+ namespace. The first time any user complains about this, we'll
+ need to make LTWRAPPER_OPTION_PREFIX a configure-time option
+ or a configure.ac-settable value.
+ */
+ lt_fatal ("Unrecognized option in %s namespace: '%s'",
+ ltwrapper_option_prefix, argv[i]);
+ }
+ /* otherwise ... */
+ newargz[++newargc] = xstrdup (argv[i]);
+ }
+ newargz[++newargc] = NULL;
+
+ LTWRAPPER_DEBUGPRINTF (("(main) lt_argv_zero : %s\n", (lt_argv_zero ? lt_argv_zero : "<NULL>")));
+ for (i = 0; i < newargc; i++)
+ {
+ LTWRAPPER_DEBUGPRINTF (("(main) newargz[%d] : %s\n", i, (newargz[i] ? newargz[i] : "<NULL>")));
+ }
+
+EOF
+
+ case $host_os in
+ mingw*)
+ cat <<"EOF"
+ /* execv doesn't actually work on mingw as expected on unix */
+ rval = _spawnv (_P_WAIT, lt_argv_zero, (const char * const *) newargz);
+ if (rval == -1)
+ {
+ /* failed to start process */
+ LTWRAPPER_DEBUGPRINTF (("(main) failed to launch target \"%s\": errno = %d\n", lt_argv_zero, errno));
+ return 127;
+ }
+ return rval;
+EOF
+ ;;
+ *)
+ cat <<"EOF"
+ execv (lt_argv_zero, newargz);
+ return rval; /* =127, but avoids unused variable warning */
+EOF
+ ;;
+ esac
+
+ cat <<"EOF"
+}
+
+void *
+xmalloc (size_t num)
+{
+ void *p = (void *) malloc (num);
+ if (!p)
+ lt_fatal ("Memory exhausted");
+
+ return p;
+}
+
+char *
+xstrdup (const char *string)
+{
+ return string ? strcpy ((char *) xmalloc (strlen (string) + 1),
+ string) : NULL;
+}
+
+const char *
+base_name (const char *name)
+{
+ const char *base;
+
+#if defined (HAVE_DOS_BASED_FILE_SYSTEM)
+ /* Skip over the disk name in MSDOS pathnames. */
+ if (isalpha ((unsigned char) name[0]) && name[1] == ':')
+ name += 2;
+#endif
+
+ for (base = name; *name; name++)
+ if (IS_DIR_SEPARATOR (*name))
+ base = name + 1;
+ return base;
+}
+
+int
+check_executable (const char *path)
+{
+ struct stat st;
+
+ LTWRAPPER_DEBUGPRINTF (("(check_executable) : %s\n",
+ path ? (*path ? path : "EMPTY!") : "NULL!"));
+ if ((!path) || (!*path))
+ return 0;
+
+ if ((stat (path, &st) >= 0)
+ && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
+ return 1;
+ else
+ return 0;
+}
+
+int
+make_executable (const char *path)
+{
+ int rval = 0;
+ struct stat st;
+
+ LTWRAPPER_DEBUGPRINTF (("(make_executable) : %s\n",
+ path ? (*path ? path : "EMPTY!") : "NULL!"));
+ if ((!path) || (!*path))
+ return 0;
+
+ if (stat (path, &st) >= 0)
+ {
+ rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR);
+ }
+ return rval;
+}
+
+/* Searches for the full path of the wrapper. Returns
+ newly allocated full path name if found, NULL otherwise
+ Does not chase symlinks, even on platforms that support them.
+*/
+char *
+find_executable (const char *wrapper)
+{
+ int has_slash = 0;
+ const char *p;
+ const char *p_next;
+ /* static buffer for getcwd */
+ char tmp[LT_PATHMAX + 1];
+ int tmp_len;
+ char *concat_name;
+
+ LTWRAPPER_DEBUGPRINTF (("(find_executable) : %s\n",
+ wrapper ? (*wrapper ? wrapper : "EMPTY!") : "NULL!"));
+
+ if ((wrapper == NULL) || (*wrapper == '\0'))
+ return NULL;
+
+ /* Absolute path? */
+#if defined (HAVE_DOS_BASED_FILE_SYSTEM)
+ if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':')
+ {
+ concat_name = xstrdup (wrapper);
+ if (check_executable (concat_name))
+ return concat_name;
+ XFREE (concat_name);
+ }
+ else
+ {
+#endif
+ if (IS_DIR_SEPARATOR (wrapper[0]))
+ {
+ concat_name = xstrdup (wrapper);
+ if (check_executable (concat_name))
+ return concat_name;
+ XFREE (concat_name);
+ }
+#if defined (HAVE_DOS_BASED_FILE_SYSTEM)
+ }
+#endif
+
+ for (p = wrapper; *p; p++)
+ if (*p == '/')
+ {
+ has_slash = 1;
+ break;
+ }
+ if (!has_slash)
+ {
+ /* no slashes; search PATH */
+ const char *path = getenv ("PATH");
+ if (path != NULL)
+ {
+ for (p = path; *p; p = p_next)
+ {
+ const char *q;
+ size_t p_len;
+ for (q = p; *q; q++)
+ if (IS_PATH_SEPARATOR (*q))
+ break;
+ p_len = q - p;
+ p_next = (*q == '\0' ? q : q + 1);
+ if (p_len == 0)
+ {
+ /* empty path: current directory */
+ if (getcwd (tmp, LT_PATHMAX) == NULL)
+ lt_fatal ("getcwd failed");
+ tmp_len = strlen (tmp);
+ concat_name =
+ XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1);
+ memcpy (concat_name, tmp, tmp_len);
+ concat_name[tmp_len] = '/';
+ strcpy (concat_name + tmp_len + 1, wrapper);
+ }
+ else
+ {
+ concat_name =
+ XMALLOC (char, p_len + 1 + strlen (wrapper) + 1);
+ memcpy (concat_name, p, p_len);
+ concat_name[p_len] = '/';
+ strcpy (concat_name + p_len + 1, wrapper);
+ }
+ if (check_executable (concat_name))
+ return concat_name;
+ XFREE (concat_name);
+ }
+ }
+ /* not found in PATH; assume curdir */
+ }
+ /* Relative path | not found in path: prepend cwd */
+ if (getcwd (tmp, LT_PATHMAX) == NULL)
+ lt_fatal ("getcwd failed");
+ tmp_len = strlen (tmp);
+ concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1);
+ memcpy (concat_name, tmp, tmp_len);
+ concat_name[tmp_len] = '/';
+ strcpy (concat_name + tmp_len + 1, wrapper);
+
+ if (check_executable (concat_name))
+ return concat_name;
+ XFREE (concat_name);
+ return NULL;
+}
+
+char *
+chase_symlinks (const char *pathspec)
+{
+#ifndef S_ISLNK
+ return xstrdup (pathspec);
+#else
+ char buf[LT_PATHMAX];
+ struct stat s;
+ char *tmp_pathspec = xstrdup (pathspec);
+ char *p;
+ int has_symlinks = 0;
+ while (strlen (tmp_pathspec) && !has_symlinks)
+ {
+ LTWRAPPER_DEBUGPRINTF (("checking path component for symlinks: %s\n",
+ tmp_pathspec));
+ if (lstat (tmp_pathspec, &s) == 0)
+ {
+ if (S_ISLNK (s.st_mode) != 0)
+ {
+ has_symlinks = 1;
+ break;
+ }
+
+ /* search backwards for last DIR_SEPARATOR */
+ p = tmp_pathspec + strlen (tmp_pathspec) - 1;
+ while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p)))
+ p--;
+ if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p)))
+ {
+ /* no more DIR_SEPARATORS left */
+ break;
+ }
+ *p = '\0';
+ }
+ else
+ {
+ char *errstr = strerror (errno);
+ lt_fatal ("Error accessing file %s (%s)", tmp_pathspec, errstr);
+ }
+ }
+ XFREE (tmp_pathspec);
+
+ if (!has_symlinks)
+ {
+ return xstrdup (pathspec);
+ }
+
+ tmp_pathspec = realpath (pathspec, buf);
+ if (tmp_pathspec == 0)
+ {
+ lt_fatal ("Could not follow symlinks for %s", pathspec);
+ }
+ return xstrdup (tmp_pathspec);
+#endif
+}
+
+char *
+strendzap (char *str, const char *pat)
+{
+ size_t len, patlen;
+
+ assert (str != NULL);
+ assert (pat != NULL);
+
+ len = strlen (str);
+ patlen = strlen (pat);
+
+ if (patlen <= len)
+ {
+ str += len - patlen;
+ if (strcmp (str, pat) == 0)
+ *str = '\0';
+ }
+ return str;
+}
+
+static void
+lt_error_core (int exit_status, const char *mode,
+ const char *message, va_list ap)
+{
+ fprintf (stderr, "%s: %s: ", program_name, mode);
+ vfprintf (stderr, message, ap);
+ fprintf (stderr, ".\n");
+
+ if (exit_status >= 0)
+ exit (exit_status);
+}
+
+void
+lt_fatal (const char *message, ...)
+{
+ va_list ap;
+ va_start (ap, message);
+ lt_error_core (EXIT_FAILURE, "FATAL", message, ap);
+ va_end (ap);
+}
+
+void
+lt_setenv (const char *name, const char *value)
+{
+ LTWRAPPER_DEBUGPRINTF (("(lt_setenv) setting '%s' to '%s'\n",
+ (name ? name : "<NULL>"),
+ (value ? value : "<NULL>")));
+ {
+#ifdef HAVE_SETENV
+ /* always make a copy, for consistency with !HAVE_SETENV */
+ char *str = xstrdup (value);
+ setenv (name, str, 1);
+#else
+ int len = strlen (name) + 1 + strlen (value) + 1;
+ char *str = XMALLOC (char, len);
+ sprintf (str, "%s=%s", name, value);
+ if (putenv (str) != EXIT_SUCCESS)
+ {
+ XFREE (str);
+ }
+#endif
+ }
+}
+
+char *
+lt_extend_str (const char *orig_value, const char *add, int to_end)
+{
+ char *new_value;
+ if (orig_value && *orig_value)
+ {
+ int orig_value_len = strlen (orig_value);
+ int add_len = strlen (add);
+ new_value = XMALLOC (char, add_len + orig_value_len + 1);
+ if (to_end)
+ {
+ strcpy (new_value, orig_value);
+ strcpy (new_value + orig_value_len, add);
+ }
+ else
+ {
+ strcpy (new_value, add);
+ strcpy (new_value + add_len, orig_value);
+ }
+ }
+ else
+ {
+ new_value = xstrdup (add);
+ }
+ return new_value;
+}
+
+int
+lt_split_name_value (const char *arg, char** name, char** value)
+{
+ const char *p;
+ int len;
+ if (!arg || !*arg)
+ return 1;
+
+ p = strchr (arg, (int)'=');
+
+ if (!p)
+ return 1;
+
+ *value = xstrdup (++p);
+
+ len = strlen (arg) - strlen (*value);
+ *name = XMALLOC (char, len);
+ strncpy (*name, arg, len-1);
+ (*name)[len - 1] = '\0';
+
+ return 0;
+}
+
+void
+lt_opt_process_env_set (const char *arg)
+{
+ char *name = NULL;
+ char *value = NULL;
+
+ if (lt_split_name_value (arg, &name, &value) != 0)
+ {
+ XFREE (name);
+ XFREE (value);
+ lt_fatal ("bad argument for %s: '%s'", env_set_opt, arg);
+ }
+
+ lt_setenv (name, value);
+ XFREE (name);
+ XFREE (value);
+}
+
+void
+lt_opt_process_env_prepend (const char *arg)
+{
+ char *name = NULL;
+ char *value = NULL;
+ char *new_value = NULL;
+
+ if (lt_split_name_value (arg, &name, &value) != 0)
+ {
+ XFREE (name);
+ XFREE (value);
+ lt_fatal ("bad argument for %s: '%s'", env_prepend_opt, arg);
+ }
+
+ new_value = lt_extend_str (getenv (name), value, 0);
+ lt_setenv (name, new_value);
+ XFREE (new_value);
+ XFREE (name);
+ XFREE (value);
+}
+
+void
+lt_opt_process_env_append (const char *arg)
+{
+ char *name = NULL;
+ char *value = NULL;
+ char *new_value = NULL;
+
+ if (lt_split_name_value (arg, &name, &value) != 0)
+ {
+ XFREE (name);
+ XFREE (value);
+ lt_fatal ("bad argument for %s: '%s'", env_append_opt, arg);
+ }
+
+ new_value = lt_extend_str (getenv (name), value, 1);
+ lt_setenv (name, new_value);
+ XFREE (new_value);
+ XFREE (name);
+ XFREE (value);
+}
+
+void
+lt_update_exe_path (const char *name, const char *value)
+{
+ LTWRAPPER_DEBUGPRINTF (("(lt_update_exe_path) modifying '%s' by prepending '%s'\n",
+ (name ? name : "<NULL>"),
+ (value ? value : "<NULL>")));
+
+ if (name && *name && value && *value)
+ {
+ char *new_value = lt_extend_str (getenv (name), value, 0);
+ /* some systems can't cope with a ':'-terminated path #' */
+ int len = strlen (new_value);
+ while (((len = strlen (new_value)) > 0) && IS_PATH_SEPARATOR (new_value[len-1]))
+ {
+ new_value[len-1] = '\0';
+ }
+ lt_setenv (name, new_value);
+ XFREE (new_value);
+ }
+}
+
+void
+lt_update_lib_path (const char *name, const char *value)
+{
+ LTWRAPPER_DEBUGPRINTF (("(lt_update_lib_path) modifying '%s' by prepending '%s'\n",
+ (name ? name : "<NULL>"),
+ (value ? value : "<NULL>")));
+
+ if (name && *name && value && *value)
+ {
+ char *new_value = lt_extend_str (getenv (name), value, 0);
+ lt_setenv (name, new_value);
+ XFREE (new_value);
+ }
+}
+
+
+EOF
+}
+# end: func_emit_cwrapperexe_src
+
+# func_mode_link arg...
+func_mode_link ()
+{
+ $opt_debug
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+ # It is impossible to link a dll without this setting, and
+ # we shouldn't force the makefile maintainer to figure out
+ # which system we are compiling for in order to pass an extra
+ # flag for every libtool invocation.
+ # allow_undefined=no
+
+ # FIXME: Unfortunately, there are problems with the above when trying
+ # to make a dll which has undefined symbols, in which case not
+ # even a static library is built. For now, we need to specify
+ # -no-undefined on the libtool link line when we can be certain
+ # that all symbols are satisfied, otherwise we get a static library.
+ allow_undefined=yes
+ ;;
+ *)
+ allow_undefined=yes
+ ;;
+ esac
+ libtool_args=$nonopt
+ base_compile="$nonopt $@"
+ compile_command=$nonopt
+ finalize_command=$nonopt
+
+ compile_rpath=
+ finalize_rpath=
+ compile_shlibpath=
+ finalize_shlibpath=
+ convenience=
+ old_convenience=
+ deplibs=
+ old_deplibs=
+ compiler_flags=
+ linker_flags=
+ dllsearchpath=
+ lib_search_path=`pwd`
+ inst_prefix_dir=
+ new_inherited_linker_flags=
+
+ avoid_version=no
+ dlfiles=
+ dlprefiles=
+ dlself=no
+ export_dynamic=no
+ export_symbols=
+ export_symbols_regex=
+ generated=
+ libobjs=
+ ltlibs=
+ module=no
+ no_install=no
+ objs=
+ non_pic_objects=
+ precious_files_regex=
+ prefer_static_libs=no
+ preload=no
+ prev=
+ prevarg=
+ release=
+ rpath=
+ xrpath=
+ perm_rpath=
+ temp_rpath=
+ thread_safe=no
+ vinfo=
+ vinfo_number=no
+ weak_libs=
+ single_module="${wl}-single_module"
+ func_infer_tag $base_compile
+
+ # We need to know -static, to get the right output filenames.
+ for arg
+ do
+ case $arg in
+ -shared)
+ test "$build_libtool_libs" != yes && \
+ func_fatal_configuration "can not build a shared library"
+ build_old_libs=no
+ break
+ ;;
+ -all-static | -static | -static-libtool-libs)
+ case $arg in
+ -all-static)
+ if test "$build_libtool_libs" = yes && test -z "$link_static_flag"; then
+ func_warning "complete static linking is impossible in this configuration"
+ fi
+ if test -n "$link_static_flag"; then
+ dlopen_self=$dlopen_self_static
+ fi
+ prefer_static_libs=yes
+ ;;
+ -static)
+ if test -z "$pic_flag" && test -n "$link_static_flag"; then
+ dlopen_self=$dlopen_self_static
+ fi
+ prefer_static_libs=built
+ ;;
+ -static-libtool-libs)
+ if test -z "$pic_flag" && test -n "$link_static_flag"; then
+ dlopen_self=$dlopen_self_static
+ fi
+ prefer_static_libs=yes
+ ;;
+ esac
+ build_libtool_libs=no
+ build_old_libs=yes
+ break
+ ;;
+ esac
+ done
+
+ # See if our shared archives depend on static archives.
+ test -n "$old_archive_from_new_cmds" && build_old_libs=yes
+
+ # Go through the arguments, transforming them on the way.
+ while test "$#" -gt 0; do
+ arg="$1"
+ shift
+ func_quote_for_eval "$arg"
+ qarg=$func_quote_for_eval_unquoted_result
+ func_append libtool_args " $func_quote_for_eval_result"
+
+ # If the previous option needs an argument, assign it.
+ if test -n "$prev"; then
+ case $prev in
+ output)
+ func_append compile_command " @OUTPUT@"
+ func_append finalize_command " @OUTPUT@"
+ ;;
+ esac
+
+ case $prev in
+ dlfiles|dlprefiles)
+ if test "$preload" = no; then
+ # Add the symbol object into the linking commands.
+ func_append compile_command " @SYMFILE@"
+ func_append finalize_command " @SYMFILE@"
+ preload=yes
+ fi
+ case $arg in
+ *.la | *.lo) ;; # We handle these cases below.
+ force)
+ if test "$dlself" = no; then
+ dlself=needless
+ export_dynamic=yes
+ fi
+ prev=
+ continue
+ ;;
+ self)
+ if test "$prev" = dlprefiles; then
+ dlself=yes
+ elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then
+ dlself=yes
+ else
+ dlself=needless
+ export_dynamic=yes
+ fi
+ prev=
+ continue
+ ;;
+ *)
+ if test "$prev" = dlfiles; then
+ dlfiles="$dlfiles $arg"
+ else
+ dlprefiles="$dlprefiles $arg"
+ fi
+ prev=
+ continue
+ ;;
+ esac
+ ;;
+ expsyms)
+ export_symbols="$arg"
+ test -f "$arg" \
+ || func_fatal_error "symbol file \`$arg' does not exist"
+ prev=
+ continue
+ ;;
+ expsyms_regex)
+ export_symbols_regex="$arg"
+ prev=
+ continue
+ ;;
+ framework)
+ case $host in
+ *-*-darwin*)
+ case "$deplibs " in
+ *" $qarg.ltframework "*) ;;
+ *) deplibs="$deplibs $qarg.ltframework" # this is fixed later
+ ;;
+ esac
+ ;;
+ esac
+ prev=
+ continue
+ ;;
+ inst_prefix)
+ inst_prefix_dir="$arg"
+ prev=
+ continue
+ ;;
+ objectlist)
+ if test -f "$arg"; then
+ save_arg=$arg
+ moreargs=
+ for fil in `cat "$save_arg"`
+ do
+# moreargs="$moreargs $fil"
+ arg=$fil
+ # A libtool-controlled object.
+
+ # Check to see that this really is a libtool object.
+ if func_lalib_unsafe_p "$arg"; then
+ pic_object=
+ non_pic_object=
+
+ # Read the .lo file
+ func_source "$arg"
+
+ if test -z "$pic_object" ||
+ test -z "$non_pic_object" ||
+ test "$pic_object" = none &&
+ test "$non_pic_object" = none; then
+ func_fatal_error "cannot find name of object for \`$arg'"
+ fi
+
+ # Extract subdirectory from the argument.
+ func_dirname "$arg" "/" ""
+ xdir="$func_dirname_result"
+
+ if test "$pic_object" != none; then
+ # Prepend the subdirectory the object is found in.
+ pic_object="$xdir$pic_object"
+
+ if test "$prev" = dlfiles; then
+ if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then
+ dlfiles="$dlfiles $pic_object"
+ prev=
+ continue
+ else
+ # If libtool objects are unsupported, then we need to preload.
+ prev=dlprefiles
+ fi
+ fi
+
+ # CHECK ME: I think I busted this. -Ossama
+ if test "$prev" = dlprefiles; then
+ # Preload the old-style object.
+ dlprefiles="$dlprefiles $pic_object"
+ prev=
+ fi
+
+ # A PIC object.
+ func_append libobjs " $pic_object"
+ arg="$pic_object"
+ fi
+
+ # Non-PIC object.
+ if test "$non_pic_object" != none; then
+ # Prepend the subdirectory the object is found in.
+ non_pic_object="$xdir$non_pic_object"
+
+ # A standard non-PIC object
+ func_append non_pic_objects " $non_pic_object"
+ if test -z "$pic_object" || test "$pic_object" = none ; then
+ arg="$non_pic_object"
+ fi
+ else
+ # If the PIC object exists, use it instead.
+ # $xdir was prepended to $pic_object above.
+ non_pic_object="$pic_object"
+ func_append non_pic_objects " $non_pic_object"
+ fi
+ else
+ # Only an error if not doing a dry-run.
+ if $opt_dry_run; then
+ # Extract subdirectory from the argument.
+ func_dirname "$arg" "/" ""
+ xdir="$func_dirname_result"
+
+ func_lo2o "$arg"
+ pic_object=$xdir$objdir/$func_lo2o_result
+ non_pic_object=$xdir$func_lo2o_result
+ func_append libobjs " $pic_object"
+ func_append non_pic_objects " $non_pic_object"
+ else
+ func_fatal_error "\`$arg' is not a valid libtool object"
+ fi
+ fi
+ done
+ else
+ func_fatal_error "link input file \`$arg' does not exist"
+ fi
+ arg=$save_arg
+ prev=
+ continue
+ ;;
+ precious_regex)
+ precious_files_regex="$arg"
+ prev=
+ continue
+ ;;
+ release)
+ release="-$arg"
+ prev=
+ continue
+ ;;
+ rpath | xrpath)
+ # We need an absolute path.
+ case $arg in
+ [\\/]* | [A-Za-z]:[\\/]*) ;;
+ *)
+ func_fatal_error "only absolute run-paths are allowed"
+ ;;
+ esac
+ if test "$prev" = rpath; then
+ case "$rpath " in
+ *" $arg "*) ;;
+ *) rpath="$rpath $arg" ;;
+ esac
+ else
+ case "$xrpath " in
+ *" $arg "*) ;;
+ *) xrpath="$xrpath $arg" ;;
+ esac
+ fi
+ prev=
+ continue
+ ;;
+ shrext)
+ shrext_cmds="$arg"
+ prev=
+ continue
+ ;;
+ weak)
+ weak_libs="$weak_libs $arg"
+ prev=
+ continue
+ ;;
+ xcclinker)
+ linker_flags="$linker_flags $qarg"
+ compiler_flags="$compiler_flags $qarg"
+ prev=
+ func_append compile_command " $qarg"
+ func_append finalize_command " $qarg"
+ continue
+ ;;
+ xcompiler)
+ compiler_flags="$compiler_flags $qarg"
+ prev=
+ func_append compile_command " $qarg"
+ func_append finalize_command " $qarg"
+ continue
+ ;;
+ xlinker)
+ linker_flags="$linker_flags $qarg"
+ compiler_flags="$compiler_flags $wl$qarg"
+ prev=
+ func_append compile_command " $wl$qarg"
+ func_append finalize_command " $wl$qarg"
+ continue
+ ;;
+ *)
+ eval "$prev=\"\$arg\""
+ prev=
+ continue
+ ;;
+ esac
+ fi # test -n "$prev"
+
+ prevarg="$arg"
+
+ case $arg in
+ -all-static)
+ if test -n "$link_static_flag"; then
+ # See comment for -static flag below, for more details.
+ func_append compile_command " $link_static_flag"
+ func_append finalize_command " $link_static_flag"
+ fi
+ continue
+ ;;
+
+ -allow-undefined)
+ # FIXME: remove this flag sometime in the future.
+ func_fatal_error "\`-allow-undefined' must not be used because it is the default"
+ ;;
+
+ -avoid-version)
+ avoid_version=yes
+ continue
+ ;;
+
+ -dlopen)
+ prev=dlfiles
+ continue
+ ;;
+
+ -dlpreopen)
+ prev=dlprefiles
+ continue
+ ;;
+
+ -export-dynamic)
+ export_dynamic=yes
+ continue
+ ;;
+
+ -export-symbols | -export-symbols-regex)
+ if test -n "$export_symbols" || test -n "$export_symbols_regex"; then
+ func_fatal_error "more than one -exported-symbols argument is not allowed"
+ fi
+ if test "X$arg" = "X-export-symbols"; then
+ prev=expsyms
+ else
+ prev=expsyms_regex
+ fi
+ continue
+ ;;
+
+ -framework)
+ prev=framework
+ continue
+ ;;
+
+ -inst-prefix-dir)
+ prev=inst_prefix
+ continue
+ ;;
+
+ # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:*
+ # so, if we see these flags be careful not to treat them like -L
+ -L[A-Z][A-Z]*:*)
+ case $with_gcc/$host in
+ no/*-*-irix* | /*-*-irix*)
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ ;;
+ esac
+ continue
+ ;;
+
+ -L*)
+ func_stripname '-L' '' "$arg"
+ dir=$func_stripname_result
+ if test -z "$dir"; then
+ if test "$#" -gt 0; then
+ func_fatal_error "require no space between \`-L' and \`$1'"
+ else
+ func_fatal_error "need path for \`-L' option"
+ fi
+ fi
+ # We need an absolute path.
+ case $dir in
+ [\\/]* | [A-Za-z]:[\\/]*) ;;
+ *)
+ absdir=`cd "$dir" && pwd`
+ test -z "$absdir" && \
+ func_fatal_error "cannot determine absolute directory name of \`$dir'"
+ dir="$absdir"
+ ;;
+ esac
+ case "$deplibs " in
+ *" -L$dir "*) ;;
+ *)
+ deplibs="$deplibs -L$dir"
+ lib_search_path="$lib_search_path $dir"
+ ;;
+ esac
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+ testbindir=`$ECHO "X$dir" | $Xsed -e 's*/lib$*/bin*'`
+ case :$dllsearchpath: in
+ *":$dir:"*) ;;
+ ::) dllsearchpath=$dir;;
+ *) dllsearchpath="$dllsearchpath:$dir";;
+ esac
+ case :$dllsearchpath: in
+ *":$testbindir:"*) ;;
+ ::) dllsearchpath=$testbindir;;
+ *) dllsearchpath="$dllsearchpath:$testbindir";;
+ esac
+ ;;
+ esac
+ continue
+ ;;
+
+ -l*)
+ if test "X$arg" = "X-lc" || test "X$arg" = "X-lm"; then
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc*)
+ # These systems don't actually have a C or math library (as such)
+ continue
+ ;;
+ *-*-os2*)
+ # These systems don't actually have a C library (as such)
+ test "X$arg" = "X-lc" && continue
+ ;;
+ *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*)
+ # Do not include libc due to us having libc/libc_r.
+ test "X$arg" = "X-lc" && continue
+ ;;
+ *-*-rhapsody* | *-*-darwin1.[012])
+ # Rhapsody C and math libraries are in the System framework
+ deplibs="$deplibs System.ltframework"
+ continue
+ ;;
+ *-*-sco3.2v5* | *-*-sco5v6*)
+ # Causes problems with __ctype
+ test "X$arg" = "X-lc" && continue
+ ;;
+ *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*)
+ # Compiler inserts libc in the correct place for threads to work
+ test "X$arg" = "X-lc" && continue
+ ;;
+ esac
+ elif test "X$arg" = "X-lc_r"; then
+ case $host in
+ *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*)
+ # Do not include libc_r directly, use -pthread flag.
+ continue
+ ;;
+ esac
+ fi
+ deplibs="$deplibs $arg"
+ continue
+ ;;
+
+ -module)
+ module=yes
+ continue
+ ;;
+
+ # Tru64 UNIX uses -model [arg] to determine the layout of C++
+ # classes, name mangling, and exception handling.
+ # Darwin uses the -arch flag to determine output architecture.
+ -model|-arch|-isysroot)
+ compiler_flags="$compiler_flags $arg"
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ prev=xcompiler
+ continue
+ ;;
+
+ -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe|-threads)
+ compiler_flags="$compiler_flags $arg"
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ case "$new_inherited_linker_flags " in
+ *" $arg "*) ;;
+ * ) new_inherited_linker_flags="$new_inherited_linker_flags $arg" ;;
+ esac
+ continue
+ ;;
+
+ -multi_module)
+ single_module="${wl}-multi_module"
+ continue
+ ;;
+
+ -no-fast-install)
+ fast_install=no
+ continue
+ ;;
+
+ -no-install)
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*)
+ # The PATH hackery in wrapper scripts is required on Windows
+ # and Darwin in order for the loader to find any dlls it needs.
+ func_warning "\`-no-install' is ignored for $host"
+ func_warning "assuming \`-no-fast-install' instead"
+ fast_install=no
+ ;;
+ *) no_install=yes ;;
+ esac
+ continue
+ ;;
+
+ -no-undefined)
+ allow_undefined=no
+ continue
+ ;;
+
+ -objectlist)
+ prev=objectlist
+ continue
+ ;;
+
+ -o) prev=output ;;
+
+ -precious-files-regex)
+ prev=precious_regex
+ continue
+ ;;
+
+ -release)
+ prev=release
+ continue
+ ;;
+
+ -rpath)
+ prev=rpath
+ continue
+ ;;
+
+ -R)
+ prev=xrpath
+ continue
+ ;;
+
+ -R*)
+ func_stripname '-R' '' "$arg"
+ dir=$func_stripname_result
+ # We need an absolute path.
+ case $dir in
+ [\\/]* | [A-Za-z]:[\\/]*) ;;
+ *)
+ func_fatal_error "only absolute run-paths are allowed"
+ ;;
+ esac
+ case "$xrpath " in
+ *" $dir "*) ;;
+ *) xrpath="$xrpath $dir" ;;
+ esac
+ continue
+ ;;
+
+ -shared)
+ # The effects of -shared are defined in a previous loop.
+ continue
+ ;;
+
+ -shrext)
+ prev=shrext
+ continue
+ ;;
+
+ -static | -static-libtool-libs)
+ # The effects of -static are defined in a previous loop.
+ # We used to do the same as -all-static on platforms that
+ # didn't have a PIC flag, but the assumption that the effects
+ # would be equivalent was wrong. It would break on at least
+ # Digital Unix and AIX.
+ continue
+ ;;
+
+ -thread-safe)
+ thread_safe=yes
+ continue
+ ;;
+
+ -version-info)
+ prev=vinfo
+ continue
+ ;;
+
+ -version-number)
+ prev=vinfo
+ vinfo_number=yes
+ continue
+ ;;
+
+ -weak)
+ prev=weak
+ continue
+ ;;
+
+ -Wc,*)
+ func_stripname '-Wc,' '' "$arg"
+ args=$func_stripname_result
+ arg=
+ save_ifs="$IFS"; IFS=','
+ for flag in $args; do
+ IFS="$save_ifs"
+ func_quote_for_eval "$flag"
+ arg="$arg $wl$func_quote_for_eval_result"
+ compiler_flags="$compiler_flags $func_quote_for_eval_result"
+ done
+ IFS="$save_ifs"
+ func_stripname ' ' '' "$arg"
+ arg=$func_stripname_result
+ ;;
+
+ -Wl,*)
+ func_stripname '-Wl,' '' "$arg"
+ args=$func_stripname_result
+ arg=
+ save_ifs="$IFS"; IFS=','
+ for flag in $args; do
+ IFS="$save_ifs"
+ func_quote_for_eval "$flag"
+ arg="$arg $wl$func_quote_for_eval_result"
+ compiler_flags="$compiler_flags $wl$func_quote_for_eval_result"
+ linker_flags="$linker_flags $func_quote_for_eval_result"
+ done
+ IFS="$save_ifs"
+ func_stripname ' ' '' "$arg"
+ arg=$func_stripname_result
+ ;;
+
+ -Xcompiler)
+ prev=xcompiler
+ continue
+ ;;
+
+ -Xlinker)
+ prev=xlinker
+ continue
+ ;;
+
+ -XCClinker)
+ prev=xcclinker
+ continue
+ ;;
+
+ # -msg_* for osf cc
+ -msg_*)
+ func_quote_for_eval "$arg"
+ arg="$func_quote_for_eval_result"
+ ;;
+
+ # -64, -mips[0-9] enable 64-bit mode on the SGI compiler
+ # -r[0-9][0-9]* specifies the processor on the SGI compiler
+ # -xarch=*, -xtarget=* enable 64-bit mode on the Sun compiler
+ # +DA*, +DD* enable 64-bit mode on the HP compiler
+ # -q* pass through compiler args for the IBM compiler
+ # -m*, -t[45]*, -txscale* pass through architecture-specific
+ # compiler args for GCC
+ # -F/path gives path to uninstalled frameworks, gcc on darwin
+ # -p, -pg, --coverage, -fprofile-* pass through profiling flag for GCC
+ # @file GCC response files
+ -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \
+ -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*)
+ func_quote_for_eval "$arg"
+ arg="$func_quote_for_eval_result"
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ compiler_flags="$compiler_flags $arg"
+ continue
+ ;;
+
+ # Some other compiler flag.
+ -* | +*)
+ func_quote_for_eval "$arg"
+ arg="$func_quote_for_eval_result"
+ ;;
+
+ *.$objext)
+ # A standard object.
+ objs="$objs $arg"
+ ;;
+
+ *.lo)
+ # A libtool-controlled object.
+
+ # Check to see that this really is a libtool object.
+ if func_lalib_unsafe_p "$arg"; then
+ pic_object=
+ non_pic_object=
+
+ # Read the .lo file
+ func_source "$arg"
+
+ if test -z "$pic_object" ||
+ test -z "$non_pic_object" ||
+ test "$pic_object" = none &&
+ test "$non_pic_object" = none; then
+ func_fatal_error "cannot find name of object for \`$arg'"
+ fi
+
+ # Extract subdirectory from the argument.
+ func_dirname "$arg" "/" ""
+ xdir="$func_dirname_result"
+
+ if test "$pic_object" != none; then
+ # Prepend the subdirectory the object is found in.
+ pic_object="$xdir$pic_object"
+
+ if test "$prev" = dlfiles; then
+ if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then
+ dlfiles="$dlfiles $pic_object"
+ prev=
+ continue
+ else
+ # If libtool objects are unsupported, then we need to preload.
+ prev=dlprefiles
+ fi
+ fi
+
+ # CHECK ME: I think I busted this. -Ossama
+ if test "$prev" = dlprefiles; then
+ # Preload the old-style object.
+ dlprefiles="$dlprefiles $pic_object"
+ prev=
+ fi
+
+ # A PIC object.
+ func_append libobjs " $pic_object"
+ arg="$pic_object"
+ fi
+
+ # Non-PIC object.
+ if test "$non_pic_object" != none; then
+ # Prepend the subdirectory the object is found in.
+ non_pic_object="$xdir$non_pic_object"
+
+ # A standard non-PIC object
+ func_append non_pic_objects " $non_pic_object"
+ if test -z "$pic_object" || test "$pic_object" = none ; then
+ arg="$non_pic_object"
+ fi
+ else
+ # If the PIC object exists, use it instead.
+ # $xdir was prepended to $pic_object above.
+ non_pic_object="$pic_object"
+ func_append non_pic_objects " $non_pic_object"
+ fi
+ else
+ # Only an error if not doing a dry-run.
+ if $opt_dry_run; then
+ # Extract subdirectory from the argument.
+ func_dirname "$arg" "/" ""
+ xdir="$func_dirname_result"
+
+ func_lo2o "$arg"
+ pic_object=$xdir$objdir/$func_lo2o_result
+ non_pic_object=$xdir$func_lo2o_result
+ func_append libobjs " $pic_object"
+ func_append non_pic_objects " $non_pic_object"
+ else
+ func_fatal_error "\`$arg' is not a valid libtool object"
+ fi
+ fi
+ ;;
+
+ *.$libext)
+ # An archive.
+ deplibs="$deplibs $arg"
+ old_deplibs="$old_deplibs $arg"
+ continue
+ ;;
+
+ *.la)
+ # A libtool-controlled library.
+
+ if test "$prev" = dlfiles; then
+ # This library was specified with -dlopen.
+ dlfiles="$dlfiles $arg"
+ prev=
+ elif test "$prev" = dlprefiles; then
+ # The library was specified with -dlpreopen.
+ dlprefiles="$dlprefiles $arg"
+ prev=
+ else
+ deplibs="$deplibs $arg"
+ fi
+ continue
+ ;;
+
+ # Some other compiler argument.
+ *)
+ # Unknown arguments in both finalize_command and compile_command need
+ # to be aesthetically quoted because they are evaled later.
+ func_quote_for_eval "$arg"
+ arg="$func_quote_for_eval_result"
+ ;;
+ esac # arg
+
+ # Now actually substitute the argument into the commands.
+ if test -n "$arg"; then
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ fi
+ done # argument parsing loop
+
+ test -n "$prev" && \
+ func_fatal_help "the \`$prevarg' option requires an argument"
+
+ if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then
+ eval arg=\"$export_dynamic_flag_spec\"
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ fi
+
+ oldlibs=
+ # calculate the name of the file, without its directory
+ func_basename "$output"
+ outputname="$func_basename_result"
+ libobjs_save="$libobjs"
+
+ if test -n "$shlibpath_var"; then
+ # get the directories listed in $shlibpath_var
+ eval shlib_search_path=\`\$ECHO \"X\${$shlibpath_var}\" \| \$Xsed -e \'s/:/ /g\'\`
+ else
+ shlib_search_path=
+ fi
+ eval sys_lib_search_path=\"$sys_lib_search_path_spec\"
+ eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\"
+
+ func_dirname "$output" "/" ""
+ output_objdir="$func_dirname_result$objdir"
+ # Create the object directory.
+ func_mkdir_p "$output_objdir"
+
+ # Determine the type of output
+ case $output in
+ "")
+ func_fatal_help "you must specify an output file"
+ ;;
+ *.$libext) linkmode=oldlib ;;
+ *.lo | *.$objext) linkmode=obj ;;
+ *.la) linkmode=lib ;;
+ *) linkmode=prog ;; # Anything else should be a program.
+ esac
+
+ specialdeplibs=
+
+ libs=
+ # Find all interdependent deplibs by searching for libraries
+ # that are linked more than once (e.g. -la -lb -la)
+ for deplib in $deplibs; do
+ if $opt_duplicate_deps ; then
+ case "$libs " in
+ *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;;
+ esac
+ fi
+ libs="$libs $deplib"
+ done
+
+ if test "$linkmode" = lib; then
+ libs="$predeps $libs $compiler_lib_search_path $postdeps"
+
+ # Compute libraries that are listed more than once in $predeps
+ # $postdeps and mark them as special (i.e., whose duplicates are
+ # not to be eliminated).
+ pre_post_deps=
+ if $opt_duplicate_compiler_generated_deps; then
+ for pre_post_dep in $predeps $postdeps; do
+ case "$pre_post_deps " in
+ *" $pre_post_dep "*) specialdeplibs="$specialdeplibs $pre_post_deps" ;;
+ esac
+ pre_post_deps="$pre_post_deps $pre_post_dep"
+ done
+ fi
+ pre_post_deps=
+ fi
+
+ deplibs=
+ newdependency_libs=
+ newlib_search_path=
+ need_relink=no # whether we're linking any uninstalled libtool libraries
+ notinst_deplibs= # not-installed libtool libraries
+ notinst_path= # paths that contain not-installed libtool libraries
+
+ case $linkmode in
+ lib)
+ passes="conv dlpreopen link"
+ for file in $dlfiles $dlprefiles; do
+ case $file in
+ *.la) ;;
+ *)
+ func_fatal_help "libraries can \`-dlopen' only libtool libraries: $file"
+ ;;
+ esac
+ done
+ ;;
+ prog)
+ compile_deplibs=
+ finalize_deplibs=
+ alldeplibs=no
+ newdlfiles=
+ newdlprefiles=
+ passes="conv scan dlopen dlpreopen link"
+ ;;
+ *) passes="conv"
+ ;;
+ esac
+
+ for pass in $passes; do
+ # The preopen pass in lib mode reverses $deplibs; put it back here
+ # so that -L comes before libs that need it for instance...
+ if test "$linkmode,$pass" = "lib,link"; then
+ ## FIXME: Find the place where the list is rebuilt in the wrong
+ ## order, and fix it there properly
+ tmp_deplibs=
+ for deplib in $deplibs; do
+ tmp_deplibs="$deplib $tmp_deplibs"
+ done
+ deplibs="$tmp_deplibs"
+ fi
+
+ if test "$linkmode,$pass" = "lib,link" ||
+ test "$linkmode,$pass" = "prog,scan"; then
+ libs="$deplibs"
+ deplibs=
+ fi
+ if test "$linkmode" = prog; then
+ case $pass in
+ dlopen) libs="$dlfiles" ;;
+ dlpreopen) libs="$dlprefiles" ;;
+ link) libs="$deplibs %DEPLIBS% $dependency_libs" ;;
+ esac
+ fi
+ if test "$linkmode,$pass" = "lib,dlpreopen"; then
+ # Collect and forward deplibs of preopened libtool libs
+ for lib in $dlprefiles; do
+ # Ignore non-libtool-libs
+ dependency_libs=
+ case $lib in
+ *.la) func_source "$lib" ;;
+ esac
+
+ # Collect preopened libtool deplibs, except any this library
+ # has declared as weak libs
+ for deplib in $dependency_libs; do
+ deplib_base=`$ECHO "X$deplib" | $Xsed -e "$basename"`
+ case " $weak_libs " in
+ *" $deplib_base "*) ;;
+ *) deplibs="$deplibs $deplib" ;;
+ esac
+ done
+ done
+ libs="$dlprefiles"
+ fi
+ if test "$pass" = dlopen; then
+ # Collect dlpreopened libraries
+ save_deplibs="$deplibs"
+ deplibs=
+ fi
+
+ for deplib in $libs; do
+ lib=
+ found=no
+ case $deplib in
+ -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe|-threads)
+ if test "$linkmode,$pass" = "prog,link"; then
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ else
+ compiler_flags="$compiler_flags $deplib"
+ if test "$linkmode" = lib ; then
+ case "$new_inherited_linker_flags " in
+ *" $deplib "*) ;;
+ * ) new_inherited_linker_flags="$new_inherited_linker_flags $deplib" ;;
+ esac
+ fi
+ fi
+ continue
+ ;;
+ -l*)
+ if test "$linkmode" != lib && test "$linkmode" != prog; then
+ func_warning "\`-l' is ignored for archives/objects"
+ continue
+ fi
+ func_stripname '-l' '' "$deplib"
+ name=$func_stripname_result
+ if test "$linkmode" = lib; then
+ searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path"
+ else
+ searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path"
+ fi
+ for searchdir in $searchdirs; do
+ for search_ext in .la $std_shrext .so .a; do
+ # Search the libtool library
+ lib="$searchdir/lib${name}${search_ext}"
+ if test -f "$lib"; then
+ if test "$search_ext" = ".la"; then
+ found=yes
+ else
+ found=no
+ fi
+ break 2
+ fi
+ done
+ done
+ if test "$found" != yes; then
+ # deplib doesn't seem to be a libtool library
+ if test "$linkmode,$pass" = "prog,link"; then
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ else
+ deplibs="$deplib $deplibs"
+ test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs"
+ fi
+ continue
+ else # deplib is a libtool library
+ # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib,
+ # We need to do some special things here, and not later.
+ if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+ case " $predeps $postdeps " in
+ *" $deplib "*)
+ if func_lalib_p "$lib"; then
+ library_names=
+ old_library=
+ func_source "$lib"
+ for l in $old_library $library_names; do
+ ll="$l"
+ done
+ if test "X$ll" = "X$old_library" ; then # only static version available
+ found=no
+ func_dirname "$lib" "" "."
+ ladir="$func_dirname_result"
+ lib=$ladir/$old_library
+ if test "$linkmode,$pass" = "prog,link"; then
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ else
+ deplibs="$deplib $deplibs"
+ test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs"
+ fi
+ continue
+ fi
+ fi
+ ;;
+ *) ;;
+ esac
+ fi
+ fi
+ ;; # -l
+ *.ltframework)
+ if test "$linkmode,$pass" = "prog,link"; then
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ else
+ deplibs="$deplib $deplibs"
+ if test "$linkmode" = lib ; then
+ case "$new_inherited_linker_flags " in
+ *" $deplib "*) ;;
+ * ) new_inherited_linker_flags="$new_inherited_linker_flags $deplib" ;;
+ esac
+ fi
+ fi
+ continue
+ ;;
+ -L*)
+ case $linkmode in
+ lib)
+ deplibs="$deplib $deplibs"
+ test "$pass" = conv && continue
+ newdependency_libs="$deplib $newdependency_libs"
+ func_stripname '-L' '' "$deplib"
+ newlib_search_path="$newlib_search_path $func_stripname_result"
+ ;;
+ prog)
+ if test "$pass" = conv; then
+ deplibs="$deplib $deplibs"
+ continue
+ fi
+ if test "$pass" = scan; then
+ deplibs="$deplib $deplibs"
+ else
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ fi
+ func_stripname '-L' '' "$deplib"
+ newlib_search_path="$newlib_search_path $func_stripname_result"
+ ;;
+ *)
+ func_warning "\`-L' is ignored for archives/objects"
+ ;;
+ esac # linkmode
+ continue
+ ;; # -L
+ -R*)
+ if test "$pass" = link; then
+ func_stripname '-R' '' "$deplib"
+ dir=$func_stripname_result
+ # Make sure the xrpath contains only unique directories.
+ case "$xrpath " in
+ *" $dir "*) ;;
+ *) xrpath="$xrpath $dir" ;;
+ esac
+ fi
+ deplibs="$deplib $deplibs"
+ continue
+ ;;
+ *.la) lib="$deplib" ;;
+ *.$libext)
+ if test "$pass" = conv; then
+ deplibs="$deplib $deplibs"
+ continue
+ fi
+ case $linkmode in
+ lib)
+ # Linking convenience modules into shared libraries is allowed,
+ # but linking other static libraries is non-portable.
+ case " $dlpreconveniencelibs " in
+ *" $deplib "*) ;;
+ *)
+ valid_a_lib=no
+ case $deplibs_check_method in
+ match_pattern*)
+ set dummy $deplibs_check_method; shift
+ match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
+ if eval "\$ECHO \"X$deplib\"" 2>/dev/null | $Xsed -e 10q \
+ | $EGREP "$match_pattern_regex" > /dev/null; then
+ valid_a_lib=yes
+ fi
+ ;;
+ pass_all)
+ valid_a_lib=yes
+ ;;
+ esac
+ if test "$valid_a_lib" != yes; then
+ $ECHO
+ $ECHO "*** Warning: Trying to link with static lib archive $deplib."
+ $ECHO "*** I have the capability to make that library automatically link in when"
+ $ECHO "*** you link to this library. But I can only do this if you have a"
+ $ECHO "*** shared version of the library, which you do not appear to have"
+ $ECHO "*** because the file extensions .$libext of this argument makes me believe"
+ $ECHO "*** that it is just a static archive that I should not use here."
+ else
+ $ECHO
+ $ECHO "*** Warning: Linking the shared library $output against the"
+ $ECHO "*** static library $deplib is not portable!"
+ deplibs="$deplib $deplibs"
+ fi
+ ;;
+ esac
+ continue
+ ;;
+ prog)
+ if test "$pass" != link; then
+ deplibs="$deplib $deplibs"
+ else
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ fi
+ continue
+ ;;
+ esac # linkmode
+ ;; # *.$libext
+ *.lo | *.$objext)
+ if test "$pass" = conv; then
+ deplibs="$deplib $deplibs"
+ elif test "$linkmode" = prog; then
+ if test "$pass" = dlpreopen || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then
+ # If there is no dlopen support or we're linking statically,
+ # we need to preload.
+ newdlprefiles="$newdlprefiles $deplib"
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ else
+ newdlfiles="$newdlfiles $deplib"
+ fi
+ fi
+ continue
+ ;;
+ %DEPLIBS%)
+ alldeplibs=yes
+ continue
+ ;;
+ esac # case $deplib
+
+ if test "$found" = yes || test -f "$lib"; then :
+ else
+ func_fatal_error "cannot find the library \`$lib' or unhandled argument \`$deplib'"
+ fi
+
+ # Check to see that this really is a libtool archive.
+ func_lalib_unsafe_p "$lib" \
+ || func_fatal_error "\`$lib' is not a valid libtool archive"
+
+ func_dirname "$lib" "" "."
+ ladir="$func_dirname_result"
+
+ dlname=
+ dlopen=
+ dlpreopen=
+ libdir=
+ library_names=
+ old_library=
+ inherited_linker_flags=
+ # If the library was installed with an old release of libtool,
+ # it will not redefine variables installed, or shouldnotlink
+ installed=yes
+ shouldnotlink=no
+ avoidtemprpath=
+
+
+ # Read the .la file
+ func_source "$lib"
+
+ # Convert "-framework foo" to "foo.ltframework"
+ if test -n "$inherited_linker_flags"; then
+ tmp_inherited_linker_flags=`$ECHO "X$inherited_linker_flags" | $Xsed -e 's/-framework \([^ $]*\)/\1.ltframework/g'`
+ for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do
+ case " $new_inherited_linker_flags " in
+ *" $tmp_inherited_linker_flag "*) ;;
+ *) new_inherited_linker_flags="$new_inherited_linker_flags $tmp_inherited_linker_flag";;
+ esac
+ done
+ fi
+ dependency_libs=`$ECHO "X $dependency_libs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'`
+ if test "$linkmode,$pass" = "lib,link" ||
+ test "$linkmode,$pass" = "prog,scan" ||
+ { test "$linkmode" != prog && test "$linkmode" != lib; }; then
+ test -n "$dlopen" && dlfiles="$dlfiles $dlopen"
+ test -n "$dlpreopen" && dlprefiles="$dlprefiles $dlpreopen"
+ fi
+
+ if test "$pass" = conv; then
+ # Only check for convenience libraries
+ deplibs="$lib $deplibs"
+ if test -z "$libdir"; then
+ if test -z "$old_library"; then
+ func_fatal_error "cannot find name of link library for \`$lib'"
+ fi
+ # It is a libtool convenience library, so add in its objects.
+ convenience="$convenience $ladir/$objdir/$old_library"
+ old_convenience="$old_convenience $ladir/$objdir/$old_library"
+ elif test "$linkmode" != prog && test "$linkmode" != lib; then
+ func_fatal_error "\`$lib' is not a convenience library"
+ fi
+ tmp_libs=
+ for deplib in $dependency_libs; do
+ deplibs="$deplib $deplibs"
+ if $opt_duplicate_deps ; then
+ case "$tmp_libs " in
+ *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;;
+ esac
+ fi
+ tmp_libs="$tmp_libs $deplib"
+ done
+ continue
+ fi # $pass = conv
+
+
+ # Get the name of the library we link against.
+ linklib=
+ for l in $old_library $library_names; do
+ linklib="$l"
+ done
+ if test -z "$linklib"; then
+ func_fatal_error "cannot find name of link library for \`$lib'"
+ fi
+
+ # This library was specified with -dlopen.
+ if test "$pass" = dlopen; then
+ if test -z "$libdir"; then
+ func_fatal_error "cannot -dlopen a convenience library: \`$lib'"
+ fi
+ if test -z "$dlname" ||
+ test "$dlopen_support" != yes ||
+ test "$build_libtool_libs" = no; then
+ # If there is no dlname, no dlopen support or we're linking
+ # statically, we need to preload. We also need to preload any
+ # dependent libraries so libltdl's deplib preloader doesn't
+ # bomb out in the load deplibs phase.
+ dlprefiles="$dlprefiles $lib $dependency_libs"
+ else
+ newdlfiles="$newdlfiles $lib"
+ fi
+ continue
+ fi # $pass = dlopen
+
+ # We need an absolute path.
+ case $ladir in
+ [\\/]* | [A-Za-z]:[\\/]*) abs_ladir="$ladir" ;;
+ *)
+ abs_ladir=`cd "$ladir" && pwd`
+ if test -z "$abs_ladir"; then
+ func_warning "cannot determine absolute directory name of \`$ladir'"
+ func_warning "passing it literally to the linker, although it might fail"
+ abs_ladir="$ladir"
+ fi
+ ;;
+ esac
+ func_basename "$lib"
+ laname="$func_basename_result"
+
+ # Find the relevant object directory and library name.
+ if test "X$installed" = Xyes; then
+ if test ! -f "$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then
+ func_warning "library \`$lib' was moved."
+ dir="$ladir"
+ absdir="$abs_ladir"
+ libdir="$abs_ladir"
+ else
+ dir="$libdir"
+ absdir="$libdir"
+ fi
+ test "X$hardcode_automatic" = Xyes && avoidtemprpath=yes
+ else
+ if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then
+ dir="$ladir"
+ absdir="$abs_ladir"
+ # Remove this search path later
+ notinst_path="$notinst_path $abs_ladir"
+ else
+ dir="$ladir/$objdir"
+ absdir="$abs_ladir/$objdir"
+ # Remove this search path later
+ notinst_path="$notinst_path $abs_ladir"
+ fi
+ fi # $installed = yes
+ func_stripname 'lib' '.la' "$laname"
+ name=$func_stripname_result
+
+ # This library was specified with -dlpreopen.
+ if test "$pass" = dlpreopen; then
+ if test -z "$libdir" && test "$linkmode" = prog; then
+ func_fatal_error "only libraries may -dlpreopen a convenience library: \`$lib'"
+ fi
+ # Prefer using a static library (so that no silly _DYNAMIC symbols
+ # are required to link).
+ if test -n "$old_library"; then
+ newdlprefiles="$newdlprefiles $dir/$old_library"
+ # Keep a list of preopened convenience libraries to check
+ # that they are being used correctly in the link pass.
+ test -z "$libdir" && \
+ dlpreconveniencelibs="$dlpreconveniencelibs $dir/$old_library"
+ # Otherwise, use the dlname, so that lt_dlopen finds it.
+ elif test -n "$dlname"; then
+ newdlprefiles="$newdlprefiles $dir/$dlname"
+ else
+ newdlprefiles="$newdlprefiles $dir/$linklib"
+ fi
+ fi # $pass = dlpreopen
+
+ if test -z "$libdir"; then
+ # Link the convenience library
+ if test "$linkmode" = lib; then
+ deplibs="$dir/$old_library $deplibs"
+ elif test "$linkmode,$pass" = "prog,link"; then
+ compile_deplibs="$dir/$old_library $compile_deplibs"
+ finalize_deplibs="$dir/$old_library $finalize_deplibs"
+ else
+ deplibs="$lib $deplibs" # used for prog,scan pass
+ fi
+ continue
+ fi
+
+
+ if test "$linkmode" = prog && test "$pass" != link; then
+ newlib_search_path="$newlib_search_path $ladir"
+ deplibs="$lib $deplibs"
+
+ linkalldeplibs=no
+ if test "$link_all_deplibs" != no || test -z "$library_names" ||
+ test "$build_libtool_libs" = no; then
+ linkalldeplibs=yes
+ fi
+
+ tmp_libs=
+ for deplib in $dependency_libs; do
+ case $deplib in
+ -L*) func_stripname '-L' '' "$deplib"
+ newlib_search_path="$newlib_search_path $func_stripname_result"
+ ;;
+ esac
+ # Need to link against all dependency_libs?
+ if test "$linkalldeplibs" = yes; then
+ deplibs="$deplib $deplibs"
+ else
+ # Need to hardcode shared library paths
+ # or/and link against static libraries
+ newdependency_libs="$deplib $newdependency_libs"
+ fi
+ if $opt_duplicate_deps ; then
+ case "$tmp_libs " in
+ *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;;
+ esac
+ fi
+ tmp_libs="$tmp_libs $deplib"
+ done # for deplib
+ continue
+ fi # $linkmode = prog...
+
+ if test "$linkmode,$pass" = "prog,link"; then
+ if test -n "$library_names" &&
+ { { test "$prefer_static_libs" = no ||
+ test "$prefer_static_libs,$installed" = "built,yes"; } ||
+ test -z "$old_library"; }; then
+ # We need to hardcode the library path
+ if test -n "$shlibpath_var" && test -z "$avoidtemprpath" ; then
+ # Make sure the rpath contains only unique directories.
+ case "$temp_rpath:" in
+ *"$absdir:"*) ;;
+ *) temp_rpath="$temp_rpath$absdir:" ;;
+ esac
+ fi
+
+ # Hardcode the library path.
+ # Skip directories that are in the system default run-time
+ # search path.
+ case " $sys_lib_dlsearch_path " in
+ *" $absdir "*) ;;
+ *)
+ case "$compile_rpath " in
+ *" $absdir "*) ;;
+ *) compile_rpath="$compile_rpath $absdir"
+ esac
+ ;;
+ esac
+ case " $sys_lib_dlsearch_path " in
+ *" $libdir "*) ;;
+ *)
+ case "$finalize_rpath " in
+ *" $libdir "*) ;;
+ *) finalize_rpath="$finalize_rpath $libdir"
+ esac
+ ;;
+ esac
+ fi # $linkmode,$pass = prog,link...
+
+ if test "$alldeplibs" = yes &&
+ { test "$deplibs_check_method" = pass_all ||
+ { test "$build_libtool_libs" = yes &&
+ test -n "$library_names"; }; }; then
+ # We only need to search for static libraries
+ continue
+ fi
+ fi
+
+ link_static=no # Whether the deplib will be linked statically
+ use_static_libs=$prefer_static_libs
+ if test "$use_static_libs" = built && test "$installed" = yes; then
+ use_static_libs=no
+ fi
+ if test -n "$library_names" &&
+ { test "$use_static_libs" = no || test -z "$old_library"; }; then
+ case $host in
+ *cygwin* | *mingw* | *cegcc*)
+ # No point in relinking DLLs because paths are not encoded
+ notinst_deplibs="$notinst_deplibs $lib"
+ need_relink=no
+ ;;
+ *)
+ if test "$installed" = no; then
+ notinst_deplibs="$notinst_deplibs $lib"
+ need_relink=yes
+ fi
+ ;;
+ esac
+ # This is a shared library
+
+ # Warn about portability, can't link against -module's on some
+ # systems (darwin). Don't bleat about dlopened modules though!
+ dlopenmodule=""
+ for dlpremoduletest in $dlprefiles; do
+ if test "X$dlpremoduletest" = "X$lib"; then
+ dlopenmodule="$dlpremoduletest"
+ break
+ fi
+ done
+ if test -z "$dlopenmodule" && test "$shouldnotlink" = yes && test "$pass" = link; then
+ $ECHO
+ if test "$linkmode" = prog; then
+ $ECHO "*** Warning: Linking the executable $output against the loadable module"
+ else
+ $ECHO "*** Warning: Linking the shared library $output against the loadable module"
+ fi
+ $ECHO "*** $linklib is not portable!"
+ fi
+ if test "$linkmode" = lib &&
+ test "$hardcode_into_libs" = yes; then
+ # Hardcode the library path.
+ # Skip directories that are in the system default run-time
+ # search path.
+ case " $sys_lib_dlsearch_path " in
+ *" $absdir "*) ;;
+ *)
+ case "$compile_rpath " in
+ *" $absdir "*) ;;
+ *) compile_rpath="$compile_rpath $absdir"
+ esac
+ ;;
+ esac
+ case " $sys_lib_dlsearch_path " in
+ *" $libdir "*) ;;
+ *)
+ case "$finalize_rpath " in
+ *" $libdir "*) ;;
+ *) finalize_rpath="$finalize_rpath $libdir"
+ esac
+ ;;
+ esac
+ fi
+
+ if test -n "$old_archive_from_expsyms_cmds"; then
+ # figure out the soname
+ set dummy $library_names
+ shift
+ realname="$1"
+ shift
+ libname=`eval "\\$ECHO \"$libname_spec\""`
+ # use dlname if we got it. it's perfectly good, no?
+ if test -n "$dlname"; then
+ soname="$dlname"
+ elif test -n "$soname_spec"; then
+ # bleh windows
+ case $host in
+ *cygwin* | mingw* | *cegcc*)
+ func_arith $current - $age
+ major=$func_arith_result
+ versuffix="-$major"
+ ;;
+ esac
+ eval soname=\"$soname_spec\"
+ else
+ soname="$realname"
+ fi
+
+ # Make a new name for the extract_expsyms_cmds to use
+ soroot="$soname"
+ func_basename "$soroot"
+ soname="$func_basename_result"
+ func_stripname 'lib' '.dll' "$soname"
+ newlib=libimp-$func_stripname_result.a
+
+ # If the library has no export list, then create one now
+ if test -f "$output_objdir/$soname-def"; then :
+ else
+ func_verbose "extracting exported symbol list from \`$soname'"
+ func_execute_cmds "$extract_expsyms_cmds" 'exit $?'
+ fi
+
+ # Create $newlib
+ if test -f "$output_objdir/$newlib"; then :; else
+ func_verbose "generating import library for \`$soname'"
+ func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?'
+ fi
+ # make sure the library variables are pointing to the new library
+ dir=$output_objdir
+ linklib=$newlib
+ fi # test -n "$old_archive_from_expsyms_cmds"
+
+ if test "$linkmode" = prog || test "$mode" != relink; then
+ add_shlibpath=
+ add_dir=
+ add=
+ lib_linked=yes
+ case $hardcode_action in
+ immediate | unsupported)
+ if test "$hardcode_direct" = no; then
+ add="$dir/$linklib"
+ case $host in
+ *-*-sco3.2v5.0.[024]*) add_dir="-L$dir" ;;
+ *-*-sysv4*uw2*) add_dir="-L$dir" ;;
+ *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \
+ *-*-unixware7*) add_dir="-L$dir" ;;
+ *-*-darwin* )
+ # if the lib is a (non-dlopened) module then we can not
+ # link against it, someone is ignoring the earlier warnings
+ if /usr/bin/file -L $add 2> /dev/null |
+ $GREP ": [^:]* bundle" >/dev/null ; then
+ if test "X$dlopenmodule" != "X$lib"; then
+ $ECHO "*** Warning: lib $linklib is a module, not a shared library"
+ if test -z "$old_library" ; then
+ $ECHO
+ $ECHO "*** And there doesn't seem to be a static archive available"
+ $ECHO "*** The link will probably fail, sorry"
+ else
+ add="$dir/$old_library"
+ fi
+ elif test -n "$old_library"; then
+ add="$dir/$old_library"
+ fi
+ fi
+ esac
+ elif test "$hardcode_minus_L" = no; then
+ case $host in
+ *-*-sunos*) add_shlibpath="$dir" ;;
+ esac
+ add_dir="-L$dir"
+ add="-l$name"
+ elif test "$hardcode_shlibpath_var" = no; then
+ add_shlibpath="$dir"
+ add="-l$name"
+ else
+ lib_linked=no
+ fi
+ ;;
+ relink)
+ if test "$hardcode_direct" = yes &&
+ test "$hardcode_direct_absolute" = no; then
+ add="$dir/$linklib"
+ elif test "$hardcode_minus_L" = yes; then
+ add_dir="-L$dir"
+ # Try looking first in the location we're being installed to.
+ if test -n "$inst_prefix_dir"; then
+ case $libdir in
+ [\\/]*)
+ add_dir="$add_dir -L$inst_prefix_dir$libdir"
+ ;;
+ esac
+ fi
+ add="-l$name"
+ elif test "$hardcode_shlibpath_var" = yes; then
+ add_shlibpath="$dir"
+ add="-l$name"
+ else
+ lib_linked=no
+ fi
+ ;;
+ *) lib_linked=no ;;
+ esac
+
+ if test "$lib_linked" != yes; then
+ func_fatal_configuration "unsupported hardcode properties"
+ fi
+
+ if test -n "$add_shlibpath"; then
+ case :$compile_shlibpath: in
+ *":$add_shlibpath:"*) ;;
+ *) compile_shlibpath="$compile_shlibpath$add_shlibpath:" ;;
+ esac
+ fi
+ if test "$linkmode" = prog; then
+ test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs"
+ test -n "$add" && compile_deplibs="$add $compile_deplibs"
+ else
+ test -n "$add_dir" && deplibs="$add_dir $deplibs"
+ test -n "$add" && deplibs="$add $deplibs"
+ if test "$hardcode_direct" != yes &&
+ test "$hardcode_minus_L" != yes &&
+ test "$hardcode_shlibpath_var" = yes; then
+ case :$finalize_shlibpath: in
+ *":$libdir:"*) ;;
+ *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;;
+ esac
+ fi
+ fi
+ fi
+
+ if test "$linkmode" = prog || test "$mode" = relink; then
+ add_shlibpath=
+ add_dir=
+ add=
+ # Finalize command for both is simple: just hardcode it.
+ if test "$hardcode_direct" = yes &&
+ test "$hardcode_direct_absolute" = no; then
+ add="$libdir/$linklib"
+ elif test "$hardcode_minus_L" = yes; then
+ add_dir="-L$libdir"
+ add="-l$name"
+ elif test "$hardcode_shlibpath_var" = yes; then
+ case :$finalize_shlibpath: in
+ *":$libdir:"*) ;;
+ *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;;
+ esac
+ add="-l$name"
+ elif test "$hardcode_automatic" = yes; then
+ if test -n "$inst_prefix_dir" &&
+ test -f "$inst_prefix_dir$libdir/$linklib" ; then
+ add="$inst_prefix_dir$libdir/$linklib"
+ else
+ add="$libdir/$linklib"
+ fi
+ else
+ # We cannot seem to hardcode it, guess we'll fake it.
+ add_dir="-L$libdir"
+ # Try looking first in the location we're being installed to.
+ if test -n "$inst_prefix_dir"; then
+ case $libdir in
+ [\\/]*)
+ add_dir="$add_dir -L$inst_prefix_dir$libdir"
+ ;;
+ esac
+ fi
+ add="-l$name"
+ fi
+
+ if test "$linkmode" = prog; then
+ test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs"
+ test -n "$add" && finalize_deplibs="$add $finalize_deplibs"
+ else
+ test -n "$add_dir" && deplibs="$add_dir $deplibs"
+ test -n "$add" && deplibs="$add $deplibs"
+ fi
+ fi
+ elif test "$linkmode" = prog; then
+ # Here we assume that one of hardcode_direct or hardcode_minus_L
+ # is not unsupported. This is valid on all known static and
+ # shared platforms.
+ if test "$hardcode_direct" != unsupported; then
+ test -n "$old_library" && linklib="$old_library"
+ compile_deplibs="$dir/$linklib $compile_deplibs"
+ finalize_deplibs="$dir/$linklib $finalize_deplibs"
+ else
+ compile_deplibs="-l$name -L$dir $compile_deplibs"
+ finalize_deplibs="-l$name -L$dir $finalize_deplibs"
+ fi
+ elif test "$build_libtool_libs" = yes; then
+ # Not a shared library
+ if test "$deplibs_check_method" != pass_all; then
+ # We're trying link a shared library against a static one
+ # but the system doesn't support it.
+
+ # Just print a warning and add the library to dependency_libs so
+ # that the program can be linked against the static library.
+ $ECHO
+ $ECHO "*** Warning: This system can not link to static lib archive $lib."
+ $ECHO "*** I have the capability to make that library automatically link in when"
+ $ECHO "*** you link to this library. But I can only do this if you have a"
+ $ECHO "*** shared version of the library, which you do not appear to have."
+ if test "$module" = yes; then
+ $ECHO "*** But as you try to build a module library, libtool will still create "
+ $ECHO "*** a static module, that should work as long as the dlopening application"
+ $ECHO "*** is linked with the -dlopen flag to resolve symbols at runtime."
+ if test -z "$global_symbol_pipe"; then
+ $ECHO
+ $ECHO "*** However, this would only work if libtool was able to extract symbol"
+ $ECHO "*** lists from a program, using \`nm' or equivalent, but libtool could"
+ $ECHO "*** not find such a program. So, this module is probably useless."
+ $ECHO "*** \`nm' from GNU binutils and a full rebuild may help."
+ fi
+ if test "$build_old_libs" = no; then
+ build_libtool_libs=module
+ build_old_libs=yes
+ else
+ build_libtool_libs=no
+ fi
+ fi
+ else
+ deplibs="$dir/$old_library $deplibs"
+ link_static=yes
+ fi
+ fi # link shared/static library?
+
+ if test "$linkmode" = lib; then
+ if test -n "$dependency_libs" &&
+ { test "$hardcode_into_libs" != yes ||
+ test "$build_old_libs" = yes ||
+ test "$link_static" = yes; }; then
+ # Extract -R from dependency_libs
+ temp_deplibs=
+ for libdir in $dependency_libs; do
+ case $libdir in
+ -R*) func_stripname '-R' '' "$libdir"
+ temp_xrpath=$func_stripname_result
+ case " $xrpath " in
+ *" $temp_xrpath "*) ;;
+ *) xrpath="$xrpath $temp_xrpath";;
+ esac;;
+ *) temp_deplibs="$temp_deplibs $libdir";;
+ esac
+ done
+ dependency_libs="$temp_deplibs"
+ fi
+
+ newlib_search_path="$newlib_search_path $absdir"
+ # Link against this library
+ test "$link_static" = no && newdependency_libs="$abs_ladir/$laname $newdependency_libs"
+ # ... and its dependency_libs
+ tmp_libs=
+ for deplib in $dependency_libs; do
+ newdependency_libs="$deplib $newdependency_libs"
+ if $opt_duplicate_deps ; then
+ case "$tmp_libs " in
+ *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;;
+ esac
+ fi
+ tmp_libs="$tmp_libs $deplib"
+ done
+
+ if test "$link_all_deplibs" != no; then
+ # Add the search paths of all dependency libraries
+ for deplib in $dependency_libs; do
+ case $deplib in
+ -L*) path="$deplib" ;;
+ *.la)
+ func_dirname "$deplib" "" "."
+ dir="$func_dirname_result"
+ # We need an absolute path.
+ case $dir in
+ [\\/]* | [A-Za-z]:[\\/]*) absdir="$dir" ;;
+ *)
+ absdir=`cd "$dir" && pwd`
+ if test -z "$absdir"; then
+ func_warning "cannot determine absolute directory name of \`$dir'"
+ absdir="$dir"
+ fi
+ ;;
+ esac
+ if $GREP "^installed=no" $deplib > /dev/null; then
+ case $host in
+ *-*-darwin*)
+ depdepl=
+ eval deplibrary_names=`${SED} -n -e 's/^library_names=\(.*\)$/\1/p' $deplib`
+ if test -n "$deplibrary_names" ; then
+ for tmp in $deplibrary_names ; do
+ depdepl=$tmp
+ done
+ if test -f "$absdir/$objdir/$depdepl" ; then
+ depdepl="$absdir/$objdir/$depdepl"
+ darwin_install_name=`${OTOOL} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'`
+ if test -z "$darwin_install_name"; then
+ darwin_install_name=`${OTOOL64} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'`
+ fi
+ compiler_flags="$compiler_flags ${wl}-dylib_file ${wl}${darwin_install_name}:${depdepl}"
+ linker_flags="$linker_flags -dylib_file ${darwin_install_name}:${depdepl}"
+ path=
+ fi
+ fi
+ ;;
+ *)
+ path="-L$absdir/$objdir"
+ ;;
+ esac
+ else
+ eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib`
+ test -z "$libdir" && \
+ func_fatal_error "\`$deplib' is not a valid libtool archive"
+ test "$absdir" != "$libdir" && \
+ func_warning "\`$deplib' seems to be moved"
+
+ path="-L$absdir"
+ fi
+ ;;
+ esac
+ case " $deplibs " in
+ *" $path "*) ;;
+ *) deplibs="$path $deplibs" ;;
+ esac
+ done
+ fi # link_all_deplibs != no
+ fi # linkmode = lib
+ done # for deplib in $libs
+ if test "$pass" = link; then
+ if test "$linkmode" = "prog"; then
+ compile_deplibs="$new_inherited_linker_flags $compile_deplibs"
+ finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs"
+ else
+ compiler_flags="$compiler_flags "`$ECHO "X $new_inherited_linker_flags" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'`
+ fi
+ fi
+ dependency_libs="$newdependency_libs"
+ if test "$pass" = dlpreopen; then
+ # Link the dlpreopened libraries before other libraries
+ for deplib in $save_deplibs; do
+ deplibs="$deplib $deplibs"
+ done
+ fi
+ if test "$pass" != dlopen; then
+ if test "$pass" != conv; then
+ # Make sure lib_search_path contains only unique directories.
+ lib_search_path=
+ for dir in $newlib_search_path; do
+ case "$lib_search_path " in
+ *" $dir "*) ;;
+ *) lib_search_path="$lib_search_path $dir" ;;
+ esac
+ done
+ newlib_search_path=
+ fi
+
+ if test "$linkmode,$pass" != "prog,link"; then
+ vars="deplibs"
+ else
+ vars="compile_deplibs finalize_deplibs"
+ fi
+ for var in $vars dependency_libs; do
+ # Add libraries to $var in reverse order
+ eval tmp_libs=\"\$$var\"
+ new_libs=
+ for deplib in $tmp_libs; do
+ # FIXME: Pedantically, this is the right thing to do, so
+ # that some nasty dependency loop isn't accidentally
+ # broken:
+ #new_libs="$deplib $new_libs"
+ # Pragmatically, this seems to cause very few problems in
+ # practice:
+ case $deplib in
+ -L*) new_libs="$deplib $new_libs" ;;
+ -R*) ;;
+ *)
+ # And here is the reason: when a library appears more
+ # than once as an explicit dependence of a library, or
+ # is implicitly linked in more than once by the
+ # compiler, it is considered special, and multiple
+ # occurrences thereof are not removed. Compare this
+ # with having the same library being listed as a
+ # dependency of multiple other libraries: in this case,
+ # we know (pedantically, we assume) the library does not
+ # need to be listed more than once, so we keep only the
+ # last copy. This is not always right, but it is rare
+ # enough that we require users that really mean to play
+ # such unportable linking tricks to link the library
+ # using -Wl,-lname, so that libtool does not consider it
+ # for duplicate removal.
+ case " $specialdeplibs " in
+ *" $deplib "*) new_libs="$deplib $new_libs" ;;
+ *)
+ case " $new_libs " in
+ *" $deplib "*) ;;
+ *) new_libs="$deplib $new_libs" ;;
+ esac
+ ;;
+ esac
+ ;;
+ esac
+ done
+ tmp_libs=
+ for deplib in $new_libs; do
+ case $deplib in
+ -L*)
+ case " $tmp_libs " in
+ *" $deplib "*) ;;
+ *) tmp_libs="$tmp_libs $deplib" ;;
+ esac
+ ;;
+ *) tmp_libs="$tmp_libs $deplib" ;;
+ esac
+ done
+ eval $var=\"$tmp_libs\"
+ done # for var
+ fi
+ # Last step: remove runtime libs from dependency_libs
+ # (they stay in deplibs)
+ tmp_libs=
+ for i in $dependency_libs ; do
+ case " $predeps $postdeps $compiler_lib_search_path " in
+ *" $i "*)
+ i=""
+ ;;
+ esac
+ if test -n "$i" ; then
+ tmp_libs="$tmp_libs $i"
+ fi
+ done
+ dependency_libs=$tmp_libs
+ done # for pass
+ if test "$linkmode" = prog; then
+ dlfiles="$newdlfiles"
+ fi
+ if test "$linkmode" = prog || test "$linkmode" = lib; then
+ dlprefiles="$newdlprefiles"
+ fi
+
+ case $linkmode in
+ oldlib)
+ if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then
+ func_warning "\`-dlopen' is ignored for archives"
+ fi
+
+ case " $deplibs" in
+ *\ -l* | *\ -L*)
+ func_warning "\`-l' and \`-L' are ignored for archives" ;;
+ esac
+
+ test -n "$rpath" && \
+ func_warning "\`-rpath' is ignored for archives"
+
+ test -n "$xrpath" && \
+ func_warning "\`-R' is ignored for archives"
+
+ test -n "$vinfo" && \
+ func_warning "\`-version-info/-version-number' is ignored for archives"
+
+ test -n "$release" && \
+ func_warning "\`-release' is ignored for archives"
+
+ test -n "$export_symbols$export_symbols_regex" && \
+ func_warning "\`-export-symbols' is ignored for archives"
+
+ # Now set the variables for building old libraries.
+ build_libtool_libs=no
+ oldlibs="$output"
+ objs="$objs$old_deplibs"
+ ;;
+
+ lib)
+ # Make sure we only generate libraries of the form `libNAME.la'.
+ case $outputname in
+ lib*)
+ func_stripname 'lib' '.la' "$outputname"
+ name=$func_stripname_result
+ eval shared_ext=\"$shrext_cmds\"
+ eval libname=\"$libname_spec\"
+ ;;
+ *)
+ test "$module" = no && \
+ func_fatal_help "libtool library \`$output' must begin with \`lib'"
+
+ if test "$need_lib_prefix" != no; then
+ # Add the "lib" prefix for modules if required
+ func_stripname '' '.la' "$outputname"
+ name=$func_stripname_result
+ eval shared_ext=\"$shrext_cmds\"
+ eval libname=\"$libname_spec\"
+ else
+ func_stripname '' '.la' "$outputname"
+ libname=$func_stripname_result
+ fi
+ ;;
+ esac
+
+ if test -n "$objs"; then
+ if test "$deplibs_check_method" != pass_all; then
+ func_fatal_error "cannot build libtool library \`$output' from non-libtool objects on this host:$objs"
+ else
+ $ECHO
+ $ECHO "*** Warning: Linking the shared library $output against the non-libtool"
+ $ECHO "*** objects $objs is not portable!"
+ libobjs="$libobjs $objs"
+ fi
+ fi
+
+ test "$dlself" != no && \
+ func_warning "\`-dlopen self' is ignored for libtool libraries"
+
+ set dummy $rpath
+ shift
+ test "$#" -gt 1 && \
+ func_warning "ignoring multiple \`-rpath's for a libtool library"
+
+ install_libdir="$1"
+
+ oldlibs=
+ if test -z "$rpath"; then
+ if test "$build_libtool_libs" = yes; then
+ # Building a libtool convenience library.
+ # Some compilers have problems with a `.al' extension so
+ # convenience libraries should have the same extension an
+ # archive normally would.
+ oldlibs="$output_objdir/$libname.$libext $oldlibs"
+ build_libtool_libs=convenience
+ build_old_libs=yes
+ fi
+
+ test -n "$vinfo" && \
+ func_warning "\`-version-info/-version-number' is ignored for convenience libraries"
+
+ test -n "$release" && \
+ func_warning "\`-release' is ignored for convenience libraries"
+ else
+
+ # Parse the version information argument.
+ save_ifs="$IFS"; IFS=':'
+ set dummy $vinfo 0 0 0
+ shift
+ IFS="$save_ifs"
+
+ test -n "$7" && \
+ func_fatal_help "too many parameters to \`-version-info'"
+
+ # convert absolute version numbers to libtool ages
+ # this retains compatibility with .la files and attempts
+ # to make the code below a bit more comprehensible
+
+ case $vinfo_number in
+ yes)
+ number_major="$1"
+ number_minor="$2"
+ number_revision="$3"
+ #
+ # There are really only two kinds -- those that
+ # use the current revision as the major version
+ # and those that subtract age and use age as
+ # a minor version. But, then there is irix
+ # which has an extra 1 added just for fun
+ #
+ case $version_type in
+ darwin|linux|osf|windows|none)
+ func_arith $number_major + $number_minor
+ current=$func_arith_result
+ age="$number_minor"
+ revision="$number_revision"
+ ;;
+ freebsd-aout|freebsd-elf|sunos)
+ current="$number_major"
+ revision="$number_minor"
+ age="0"
+ ;;
+ irix|nonstopux)
+ func_arith $number_major + $number_minor
+ current=$func_arith_result
+ age="$number_minor"
+ revision="$number_minor"
+ lt_irix_increment=no
+ ;;
+ esac
+ ;;
+ no)
+ current="$1"
+ revision="$2"
+ age="$3"
+ ;;
+ esac
+
+ # Check that each of the things are valid numbers.
+ case $current in
+ 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
+ *)
+ func_error "CURRENT \`$current' must be a nonnegative integer"
+ func_fatal_error "\`$vinfo' is not valid version information"
+ ;;
+ esac
+
+ case $revision in
+ 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
+ *)
+ func_error "REVISION \`$revision' must be a nonnegative integer"
+ func_fatal_error "\`$vinfo' is not valid version information"
+ ;;
+ esac
+
+ case $age in
+ 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
+ *)
+ func_error "AGE \`$age' must be a nonnegative integer"
+ func_fatal_error "\`$vinfo' is not valid version information"
+ ;;
+ esac
+
+ if test "$age" -gt "$current"; then
+ func_error "AGE \`$age' is greater than the current interface number \`$current'"
+ func_fatal_error "\`$vinfo' is not valid version information"
+ fi
+
+ # Calculate the version variables.
+ major=
+ versuffix=
+ verstring=
+ case $version_type in
+ none) ;;
+
+ darwin)
+ # Like Linux, but with the current version available in
+ # verstring for coding it into the library header
+ func_arith $current - $age
+ major=.$func_arith_result
+ versuffix="$major.$age.$revision"
+ # Darwin ld doesn't like 0 for these options...
+ func_arith $current + 1
+ minor_current=$func_arith_result
+ xlcverstring="${wl}-compatibility_version ${wl}$minor_current ${wl}-current_version ${wl}$minor_current.$revision"
+ verstring="-compatibility_version $minor_current -current_version $minor_current.$revision"
+ ;;
+
+ freebsd-aout)
+ major=".$current"
+ versuffix=".$current.$revision";
+ ;;
+
+ freebsd-elf)
+ major=".$current"
+ versuffix=".$current"
+ ;;
+
+ irix | nonstopux)
+ if test "X$lt_irix_increment" = "Xno"; then
+ func_arith $current - $age
+ else
+ func_arith $current - $age + 1
+ fi
+ major=$func_arith_result
+
+ case $version_type in
+ nonstopux) verstring_prefix=nonstopux ;;
+ *) verstring_prefix=sgi ;;
+ esac
+ verstring="$verstring_prefix$major.$revision"
+
+ # Add in all the interfaces that we are compatible with.
+ loop=$revision
+ while test "$loop" -ne 0; do
+ func_arith $revision - $loop
+ iface=$func_arith_result
+ func_arith $loop - 1
+ loop=$func_arith_result
+ verstring="$verstring_prefix$major.$iface:$verstring"
+ done
+
+ # Before this point, $major must not contain `.'.
+ major=.$major
+ versuffix="$major.$revision"
+ ;;
+
+ linux)
+ func_arith $current - $age
+ major=.$func_arith_result
+ versuffix="$major.$age.$revision"
+ ;;
+
+ osf)
+ func_arith $current - $age
+ major=.$func_arith_result
+ versuffix=".$current.$age.$revision"
+ verstring="$current.$age.$revision"
+
+ # Add in all the interfaces that we are compatible with.
+ loop=$age
+ while test "$loop" -ne 0; do
+ func_arith $current - $loop
+ iface=$func_arith_result
+ func_arith $loop - 1
+ loop=$func_arith_result
+ verstring="$verstring:${iface}.0"
+ done
+
+ # Make executables depend on our current version.
+ verstring="$verstring:${current}.0"
+ ;;
+
+ qnx)
+ major=".$current"
+ versuffix=".$current"
+ ;;
+
+ sunos)
+ major=".$current"
+ versuffix=".$current.$revision"
+ ;;
+
+ windows)
+ # Use '-' rather than '.', since we only want one
+ # extension on DOS 8.3 filesystems.
+ func_arith $current - $age
+ major=$func_arith_result
+ versuffix="-$major"
+ ;;
+
+ *)
+ func_fatal_configuration "unknown library version type \`$version_type'"
+ ;;
+ esac
+
+ # Clear the version info if we defaulted, and they specified a release.
+ if test -z "$vinfo" && test -n "$release"; then
+ major=
+ case $version_type in
+ darwin)
+ # we can't check for "0.0" in archive_cmds due to quoting
+ # problems, so we reset it completely
+ verstring=
+ ;;
+ *)
+ verstring="0.0"
+ ;;
+ esac
+ if test "$need_version" = no; then
+ versuffix=
+ else
+ versuffix=".0.0"
+ fi
+ fi
+
+ # Remove version info from name if versioning should be avoided
+ if test "$avoid_version" = yes && test "$need_version" = no; then
+ major=
+ versuffix=
+ verstring=""
+ fi
+
+ # Check to see if the archive will have undefined symbols.
+ if test "$allow_undefined" = yes; then
+ if test "$allow_undefined_flag" = unsupported; then
+ func_warning "undefined symbols not allowed in $host shared libraries"
+ build_libtool_libs=no
+ build_old_libs=yes
+ fi
+ else
+ # Don't allow undefined symbols.
+ allow_undefined_flag="$no_undefined_flag"
+ fi
+
+ fi
+
+ func_generate_dlsyms "$libname" "$libname" "yes"
+ libobjs="$libobjs $symfileobj"
+ test "X$libobjs" = "X " && libobjs=
+
+ if test "$mode" != relink; then
+ # Remove our outputs, but don't remove object files since they
+ # may have been created when compiling PIC objects.
+ removelist=
+ tempremovelist=`$ECHO "$output_objdir/*"`
+ for p in $tempremovelist; do
+ case $p in
+ *.$objext | *.gcno)
+ ;;
+ $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/${libname}${release}.*)
+ if test "X$precious_files_regex" != "X"; then
+ if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1
+ then
+ continue
+ fi
+ fi
+ removelist="$removelist $p"
+ ;;
+ *) ;;
+ esac
+ done
+ test -n "$removelist" && \
+ func_show_eval "${RM}r \$removelist"
+ fi
+
+ # Now set the variables for building old libraries.
+ if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then
+ oldlibs="$oldlibs $output_objdir/$libname.$libext"
+
+ # Transform .lo files to .o files.
+ oldobjs="$objs "`$ECHO "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}'$/d' -e "$lo2o" | $NL2SP`
+ fi
+
+ # Eliminate all temporary directories.
+ #for path in $notinst_path; do
+ # lib_search_path=`$ECHO "X$lib_search_path " | $Xsed -e "s% $path % %g"`
+ # deplibs=`$ECHO "X$deplibs " | $Xsed -e "s% -L$path % %g"`
+ # dependency_libs=`$ECHO "X$dependency_libs " | $Xsed -e "s% -L$path % %g"`
+ #done
+
+ if test -n "$xrpath"; then
+ # If the user specified any rpath flags, then add them.
+ temp_xrpath=
+ for libdir in $xrpath; do
+ temp_xrpath="$temp_xrpath -R$libdir"
+ case "$finalize_rpath " in
+ *" $libdir "*) ;;
+ *) finalize_rpath="$finalize_rpath $libdir" ;;
+ esac
+ done
+ if test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes; then
+ dependency_libs="$temp_xrpath $dependency_libs"
+ fi
+ fi
+
+ # Make sure dlfiles contains only unique files that won't be dlpreopened
+ old_dlfiles="$dlfiles"
+ dlfiles=
+ for lib in $old_dlfiles; do
+ case " $dlprefiles $dlfiles " in
+ *" $lib "*) ;;
+ *) dlfiles="$dlfiles $lib" ;;
+ esac
+ done
+
+ # Make sure dlprefiles contains only unique files
+ old_dlprefiles="$dlprefiles"
+ dlprefiles=
+ for lib in $old_dlprefiles; do
+ case "$dlprefiles " in
+ *" $lib "*) ;;
+ *) dlprefiles="$dlprefiles $lib" ;;
+ esac
+ done
+
+ if test "$build_libtool_libs" = yes; then
+ if test -n "$rpath"; then
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc*)
+ # these systems don't actually have a c library (as such)!
+ ;;
+ *-*-rhapsody* | *-*-darwin1.[012])
+ # Rhapsody C library is in the System framework
+ deplibs="$deplibs System.ltframework"
+ ;;
+ *-*-netbsd*)
+ # Don't link with libc until the a.out ld.so is fixed.
+ ;;
+ *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*)
+ # Do not include libc due to us having libc/libc_r.
+ ;;
+ *-*-sco3.2v5* | *-*-sco5v6*)
+ # Causes problems with __ctype
+ ;;
+ *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*)
+ # Compiler inserts libc in the correct place for threads to work
+ ;;
+ *)
+ # Add libc to deplibs on all other systems if necessary.
+ if test "$build_libtool_need_lc" = "yes"; then
+ deplibs="$deplibs -lc"
+ fi
+ ;;
+ esac
+ fi
+
+ # Transform deplibs into only deplibs that can be linked in shared.
+ name_save=$name
+ libname_save=$libname
+ release_save=$release
+ versuffix_save=$versuffix
+ major_save=$major
+ # I'm not sure if I'm treating the release correctly. I think
+ # release should show up in the -l (ie -lgmp5) so we don't want to
+ # add it in twice. Is that correct?
+ release=""
+ versuffix=""
+ major=""
+ newdeplibs=
+ droppeddeps=no
+ case $deplibs_check_method in
+ pass_all)
+ # Don't check for shared/static. Everything works.
+ # This might be a little naive. We might want to check
+ # whether the library exists or not. But this is on
+ # osf3 & osf4 and I'm not really sure... Just
+ # implementing what was already the behavior.
+ newdeplibs=$deplibs
+ ;;
+ test_compile)
+ # This code stresses the "libraries are programs" paradigm to its
+ # limits. Maybe even breaks it. We compile a program, linking it
+ # against the deplibs as a proxy for the library. Then we can check
+ # whether they linked in statically or dynamically with ldd.
+ $opt_dry_run || $RM conftest.c
+ cat > conftest.c <<EOF
+ int main() { return 0; }
+EOF
+ $opt_dry_run || $RM conftest
+ if $LTCC $LTCFLAGS -o conftest conftest.c $deplibs; then
+ ldd_output=`ldd conftest`
+ for i in $deplibs; do
+ case $i in
+ -l*)
+ func_stripname -l '' "$i"
+ name=$func_stripname_result
+ if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+ case " $predeps $postdeps " in
+ *" $i "*)
+ newdeplibs="$newdeplibs $i"
+ i=""
+ ;;
+ esac
+ fi
+ if test -n "$i" ; then
+ libname=`eval "\\$ECHO \"$libname_spec\""`
+ deplib_matches=`eval "\\$ECHO \"$library_names_spec\""`
+ set dummy $deplib_matches; shift
+ deplib_match=$1
+ if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0 ; then
+ newdeplibs="$newdeplibs $i"
+ else
+ droppeddeps=yes
+ $ECHO
+ $ECHO "*** Warning: dynamic linker does not accept needed library $i."
+ $ECHO "*** I have the capability to make that library automatically link in when"
+ $ECHO "*** you link to this library. But I can only do this if you have a"
+ $ECHO "*** shared version of the library, which I believe you do not have"
+ $ECHO "*** because a test_compile did reveal that the linker did not use it for"
+ $ECHO "*** its dynamic dependency list that programs get resolved with at runtime."
+ fi
+ fi
+ ;;
+ *)
+ newdeplibs="$newdeplibs $i"
+ ;;
+ esac
+ done
+ else
+ # Error occurred in the first compile. Let's try to salvage
+ # the situation: Compile a separate program for each library.
+ for i in $deplibs; do
+ case $i in
+ -l*)
+ func_stripname -l '' "$i"
+ name=$func_stripname_result
+ $opt_dry_run || $RM conftest
+ if $LTCC $LTCFLAGS -o conftest conftest.c $i; then
+ ldd_output=`ldd conftest`
+ if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+ case " $predeps $postdeps " in
+ *" $i "*)
+ newdeplibs="$newdeplibs $i"
+ i=""
+ ;;
+ esac
+ fi
+ if test -n "$i" ; then
+ libname=`eval "\\$ECHO \"$libname_spec\""`
+ deplib_matches=`eval "\\$ECHO \"$library_names_spec\""`
+ set dummy $deplib_matches; shift
+ deplib_match=$1
+ if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0 ; then
+ newdeplibs="$newdeplibs $i"
+ else
+ droppeddeps=yes
+ $ECHO
+ $ECHO "*** Warning: dynamic linker does not accept needed library $i."
+ $ECHO "*** I have the capability to make that library automatically link in when"
+ $ECHO "*** you link to this library. But I can only do this if you have a"
+ $ECHO "*** shared version of the library, which you do not appear to have"
+ $ECHO "*** because a test_compile did reveal that the linker did not use this one"
+ $ECHO "*** as a dynamic dependency that programs can get resolved with at runtime."
+ fi
+ fi
+ else
+ droppeddeps=yes
+ $ECHO
+ $ECHO "*** Warning! Library $i is needed by this library but I was not able to"
+ $ECHO "*** make it link in! You will probably need to install it or some"
+ $ECHO "*** library that it depends on before this library will be fully"
+ $ECHO "*** functional. Installing it before continuing would be even better."
+ fi
+ ;;
+ *)
+ newdeplibs="$newdeplibs $i"
+ ;;
+ esac
+ done
+ fi
+ ;;
+ file_magic*)
+ set dummy $deplibs_check_method; shift
+ file_magic_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
+ for a_deplib in $deplibs; do
+ case $a_deplib in
+ -l*)
+ func_stripname -l '' "$a_deplib"
+ name=$func_stripname_result
+ if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+ case " $predeps $postdeps " in
+ *" $a_deplib "*)
+ newdeplibs="$newdeplibs $a_deplib"
+ a_deplib=""
+ ;;
+ esac
+ fi
+ if test -n "$a_deplib" ; then
+ libname=`eval "\\$ECHO \"$libname_spec\""`
+ for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do
+ potential_libs=`ls $i/$libname[.-]* 2>/dev/null`
+ for potent_lib in $potential_libs; do
+ # Follow soft links.
+ if ls -lLd "$potent_lib" 2>/dev/null |
+ $GREP " -> " >/dev/null; then
+ continue
+ fi
+ # The statement above tries to avoid entering an
+ # endless loop below, in case of cyclic links.
+ # We might still enter an endless loop, since a link
+ # loop can be closed while we follow links,
+ # but so what?
+ potlib="$potent_lib"
+ while test -h "$potlib" 2>/dev/null; do
+ potliblink=`ls -ld $potlib | ${SED} 's/.* -> //'`
+ case $potliblink in
+ [\\/]* | [A-Za-z]:[\\/]*) potlib="$potliblink";;
+ *) potlib=`$ECHO "X$potlib" | $Xsed -e 's,[^/]*$,,'`"$potliblink";;
+ esac
+ done
+ if eval $file_magic_cmd \"\$potlib\" 2>/dev/null |
+ $SED -e 10q |
+ $EGREP "$file_magic_regex" > /dev/null; then
+ newdeplibs="$newdeplibs $a_deplib"
+ a_deplib=""
+ break 2
+ fi
+ done
+ done
+ fi
+ if test -n "$a_deplib" ; then
+ droppeddeps=yes
+ $ECHO
+ $ECHO "*** Warning: linker path does not have real file for library $a_deplib."
+ $ECHO "*** I have the capability to make that library automatically link in when"
+ $ECHO "*** you link to this library. But I can only do this if you have a"
+ $ECHO "*** shared version of the library, which you do not appear to have"
+ $ECHO "*** because I did check the linker path looking for a file starting"
+ if test -z "$potlib" ; then
+ $ECHO "*** with $libname but no candidates were found. (...for file magic test)"
+ else
+ $ECHO "*** with $libname and none of the candidates passed a file format test"
+ $ECHO "*** using a file magic. Last file checked: $potlib"
+ fi
+ fi
+ ;;
+ *)
+ # Add a -L argument.
+ newdeplibs="$newdeplibs $a_deplib"
+ ;;
+ esac
+ done # Gone through all deplibs.
+ ;;
+ match_pattern*)
+ set dummy $deplibs_check_method; shift
+ match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
+ for a_deplib in $deplibs; do
+ case $a_deplib in
+ -l*)
+ func_stripname -l '' "$a_deplib"
+ name=$func_stripname_result
+ if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+ case " $predeps $postdeps " in
+ *" $a_deplib "*)
+ newdeplibs="$newdeplibs $a_deplib"
+ a_deplib=""
+ ;;
+ esac
+ fi
+ if test -n "$a_deplib" ; then
+ libname=`eval "\\$ECHO \"$libname_spec\""`
+ for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do
+ potential_libs=`ls $i/$libname[.-]* 2>/dev/null`
+ for potent_lib in $potential_libs; do
+ potlib="$potent_lib" # see symlink-check above in file_magic test
+ if eval "\$ECHO \"X$potent_lib\"" 2>/dev/null | $Xsed -e 10q | \
+ $EGREP "$match_pattern_regex" > /dev/null; then
+ newdeplibs="$newdeplibs $a_deplib"
+ a_deplib=""
+ break 2
+ fi
+ done
+ done
+ fi
+ if test -n "$a_deplib" ; then
+ droppeddeps=yes
+ $ECHO
+ $ECHO "*** Warning: linker path does not have real file for library $a_deplib."
+ $ECHO "*** I have the capability to make that library automatically link in when"
+ $ECHO "*** you link to this library. But I can only do this if you have a"
+ $ECHO "*** shared version of the library, which you do not appear to have"
+ $ECHO "*** because I did check the linker path looking for a file starting"
+ if test -z "$potlib" ; then
+ $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)"
+ else
+ $ECHO "*** with $libname and none of the candidates passed a file format test"
+ $ECHO "*** using a regex pattern. Last file checked: $potlib"
+ fi
+ fi
+ ;;
+ *)
+ # Add a -L argument.
+ newdeplibs="$newdeplibs $a_deplib"
+ ;;
+ esac
+ done # Gone through all deplibs.
+ ;;
+ none | unknown | *)
+ newdeplibs=""
+ tmp_deplibs=`$ECHO "X $deplibs" | $Xsed \
+ -e 's/ -lc$//' -e 's/ -[LR][^ ]*//g'`
+ if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+ for i in $predeps $postdeps ; do
+ # can't use Xsed below, because $i might contain '/'
+ tmp_deplibs=`$ECHO "X $tmp_deplibs" | $Xsed -e "s,$i,,"`
+ done
+ fi
+ if $ECHO "X $tmp_deplibs" | $Xsed -e 's/[ ]//g' |
+ $GREP . >/dev/null; then
+ $ECHO
+ if test "X$deplibs_check_method" = "Xnone"; then
+ $ECHO "*** Warning: inter-library dependencies are not supported in this platform."
+ else
+ $ECHO "*** Warning: inter-library dependencies are not known to be supported."
+ fi
+ $ECHO "*** All declared inter-library dependencies are being dropped."
+ droppeddeps=yes
+ fi
+ ;;
+ esac
+ versuffix=$versuffix_save
+ major=$major_save
+ release=$release_save
+ libname=$libname_save
+ name=$name_save
+
+ case $host in
+ *-*-rhapsody* | *-*-darwin1.[012])
+ # On Rhapsody replace the C library with the System framework
+ newdeplibs=`$ECHO "X $newdeplibs" | $Xsed -e 's/ -lc / System.ltframework /'`
+ ;;
+ esac
+
+ if test "$droppeddeps" = yes; then
+ if test "$module" = yes; then
+ $ECHO
+ $ECHO "*** Warning: libtool could not satisfy all declared inter-library"
+ $ECHO "*** dependencies of module $libname. Therefore, libtool will create"
+ $ECHO "*** a static module, that should work as long as the dlopening"
+ $ECHO "*** application is linked with the -dlopen flag."
+ if test -z "$global_symbol_pipe"; then
+ $ECHO
+ $ECHO "*** However, this would only work if libtool was able to extract symbol"
+ $ECHO "*** lists from a program, using \`nm' or equivalent, but libtool could"
+ $ECHO "*** not find such a program. So, this module is probably useless."
+ $ECHO "*** \`nm' from GNU binutils and a full rebuild may help."
+ fi
+ if test "$build_old_libs" = no; then
+ oldlibs="$output_objdir/$libname.$libext"
+ build_libtool_libs=module
+ build_old_libs=yes
+ else
+ build_libtool_libs=no
+ fi
+ else
+ $ECHO "*** The inter-library dependencies that have been dropped here will be"
+ $ECHO "*** automatically added whenever a program is linked with this library"
+ $ECHO "*** or is declared to -dlopen it."
+
+ if test "$allow_undefined" = no; then
+ $ECHO
+ $ECHO "*** Since this library must not contain undefined symbols,"
+ $ECHO "*** because either the platform does not support them or"
+ $ECHO "*** it was explicitly requested with -no-undefined,"
+ $ECHO "*** libtool will only create a static version of it."
+ if test "$build_old_libs" = no; then
+ oldlibs="$output_objdir/$libname.$libext"
+ build_libtool_libs=module
+ build_old_libs=yes
+ else
+ build_libtool_libs=no
+ fi
+ fi
+ fi
+ fi
+ # Done checking deplibs!
+ deplibs=$newdeplibs
+ fi
+ # Time to change all our "foo.ltframework" stuff back to "-framework foo"
+ case $host in
+ *-*-darwin*)
+ newdeplibs=`$ECHO "X $newdeplibs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'`
+ new_inherited_linker_flags=`$ECHO "X $new_inherited_linker_flags" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'`
+ deplibs=`$ECHO "X $deplibs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'`
+ ;;
+ esac
+
+ # move library search paths that coincide with paths to not yet
+ # installed libraries to the beginning of the library search list
+ new_libs=
+ for path in $notinst_path; do
+ case " $new_libs " in
+ *" -L$path/$objdir "*) ;;
+ *)
+ case " $deplibs " in
+ *" -L$path/$objdir "*)
+ new_libs="$new_libs -L$path/$objdir" ;;
+ esac
+ ;;
+ esac
+ done
+ for deplib in $deplibs; do
+ case $deplib in
+ -L*)
+ case " $new_libs " in
+ *" $deplib "*) ;;
+ *) new_libs="$new_libs $deplib" ;;
+ esac
+ ;;
+ *) new_libs="$new_libs $deplib" ;;
+ esac
+ done
+ deplibs="$new_libs"
+
+ # All the library-specific variables (install_libdir is set above).
+ library_names=
+ old_library=
+ dlname=
+
+ # Test again, we may have decided not to build it any more
+ if test "$build_libtool_libs" = yes; then
+ if test "$hardcode_into_libs" = yes; then
+ # Hardcode the library paths
+ hardcode_libdirs=
+ dep_rpath=
+ rpath="$finalize_rpath"
+ test "$mode" != relink && rpath="$compile_rpath$rpath"
+ for libdir in $rpath; do
+ if test -n "$hardcode_libdir_flag_spec"; then
+ if test -n "$hardcode_libdir_separator"; then
+ if test -z "$hardcode_libdirs"; then
+ hardcode_libdirs="$libdir"
+ else
+ # Just accumulate the unique libdirs.
+ case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+ *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+ ;;
+ *)
+ hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir"
+ ;;
+ esac
+ fi
+ else
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ dep_rpath="$dep_rpath $flag"
+ fi
+ elif test -n "$runpath_var"; then
+ case "$perm_rpath " in
+ *" $libdir "*) ;;
+ *) perm_rpath="$perm_rpath $libdir" ;;
+ esac
+ fi
+ done
+ # Substitute the hardcoded libdirs into the rpath.
+ if test -n "$hardcode_libdir_separator" &&
+ test -n "$hardcode_libdirs"; then
+ libdir="$hardcode_libdirs"
+ if test -n "$hardcode_libdir_flag_spec_ld"; then
+ eval dep_rpath=\"$hardcode_libdir_flag_spec_ld\"
+ else
+ eval dep_rpath=\"$hardcode_libdir_flag_spec\"
+ fi
+ fi
+ if test -n "$runpath_var" && test -n "$perm_rpath"; then
+ # We should set the runpath_var.
+ rpath=
+ for dir in $perm_rpath; do
+ rpath="$rpath$dir:"
+ done
+ eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var"
+ fi
+ test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs"
+ fi
+
+ shlibpath="$finalize_shlibpath"
+ test "$mode" != relink && shlibpath="$compile_shlibpath$shlibpath"
+ if test -n "$shlibpath"; then
+ eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var"
+ fi
+
+ # Get the real and link names of the library.
+ eval shared_ext=\"$shrext_cmds\"
+ eval library_names=\"$library_names_spec\"
+ set dummy $library_names
+ shift
+ realname="$1"
+ shift
+
+ if test -n "$soname_spec"; then
+ eval soname=\"$soname_spec\"
+ else
+ soname="$realname"
+ fi
+ if test -z "$dlname"; then
+ dlname=$soname
+ fi
+
+ lib="$output_objdir/$realname"
+ linknames=
+ for link
+ do
+ linknames="$linknames $link"
+ done
+
+ # Use standard objects if they are pic
+ test -z "$pic_flag" && libobjs=`$ECHO "X$libobjs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP`
+ test "X$libobjs" = "X " && libobjs=
+
+ delfiles=
+ if test -n "$export_symbols" && test -n "$include_expsyms"; then
+ $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp"
+ export_symbols="$output_objdir/$libname.uexp"
+ delfiles="$delfiles $export_symbols"
+ fi
+
+ orig_export_symbols=
+ case $host_os in
+ cygwin* | mingw* | cegcc*)
+ if test -n "$export_symbols" && test -z "$export_symbols_regex"; then
+ # exporting using user supplied symfile
+ if test "x`$SED 1q $export_symbols`" != xEXPORTS; then
+ # and it's NOT already a .def file. Must figure out
+ # which of the given symbols are data symbols and tag
+ # them as such. So, trigger use of export_symbols_cmds.
+ # export_symbols gets reassigned inside the "prepare
+ # the list of exported symbols" if statement, so the
+ # include_expsyms logic still works.
+ orig_export_symbols="$export_symbols"
+ export_symbols=
+ always_export_symbols=yes
+ fi
+ fi
+ ;;
+ esac
+
+ # Prepare the list of exported symbols
+ if test -z "$export_symbols"; then
+ if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then
+ func_verbose "generating symbol list for \`$libname.la'"
+ export_symbols="$output_objdir/$libname.exp"
+ $opt_dry_run || $RM $export_symbols
+ cmds=$export_symbols_cmds
+ save_ifs="$IFS"; IFS='~'
+ for cmd in $cmds; do
+ IFS="$save_ifs"
+ eval cmd=\"$cmd\"
+ func_len " $cmd"
+ len=$func_len_result
+ if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then
+ func_show_eval "$cmd" 'exit $?'
+ skipped_export=false
+ else
+ # The command line is too long to execute in one step.
+ func_verbose "using reloadable object file for export list..."
+ skipped_export=:
+ # Break out early, otherwise skipped_export may be
+ # set to false by a later but shorter cmd.
+ break
+ fi
+ done
+ IFS="$save_ifs"
+ if test -n "$export_symbols_regex" && test "X$skipped_export" != "X:"; then
+ func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"'
+ func_show_eval '$MV "${export_symbols}T" "$export_symbols"'
+ fi
+ fi
+ fi
+
+ if test -n "$export_symbols" && test -n "$include_expsyms"; then
+ tmp_export_symbols="$export_symbols"
+ test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols"
+ $opt_dry_run || eval '$ECHO "X$include_expsyms" | $Xsed | $SP2NL >> "$tmp_export_symbols"'
+ fi
+
+ if test "X$skipped_export" != "X:" && test -n "$orig_export_symbols"; then
+ # The given exports_symbols file has to be filtered, so filter it.
+ func_verbose "filter symbol list for \`$libname.la' to tag DATA exports"
+ # FIXME: $output_objdir/$libname.filter potentially contains lots of
+ # 's' commands which not all seds can handle. GNU sed should be fine
+ # though. Also, the filter scales superlinearly with the number of
+ # global variables. join(1) would be nice here, but unfortunately
+ # isn't a blessed tool.
+ $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter
+ delfiles="$delfiles $export_symbols $output_objdir/$libname.filter"
+ export_symbols=$output_objdir/$libname.def
+ $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols
+ fi
+
+ tmp_deplibs=
+ for test_deplib in $deplibs; do
+ case " $convenience " in
+ *" $test_deplib "*) ;;
+ *)
+ tmp_deplibs="$tmp_deplibs $test_deplib"
+ ;;
+ esac
+ done
+ deplibs="$tmp_deplibs"
+
+ if test -n "$convenience"; then
+ if test -n "$whole_archive_flag_spec" &&
+ test "$compiler_needs_object" = yes &&
+ test -z "$libobjs"; then
+ # extract the archives, so we have objects to list.
+ # TODO: could optimize this to just extract one archive.
+ whole_archive_flag_spec=
+ fi
+ if test -n "$whole_archive_flag_spec"; then
+ save_libobjs=$libobjs
+ eval libobjs=\"\$libobjs $whole_archive_flag_spec\"
+ test "X$libobjs" = "X " && libobjs=
+ else
+ gentop="$output_objdir/${outputname}x"
+ generated="$generated $gentop"
+
+ func_extract_archives $gentop $convenience
+ libobjs="$libobjs $func_extract_archives_result"
+ test "X$libobjs" = "X " && libobjs=
+ fi
+ fi
+
+ if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then
+ eval flag=\"$thread_safe_flag_spec\"
+ linker_flags="$linker_flags $flag"
+ fi
+
+ # Make a backup of the uninstalled library when relinking
+ if test "$mode" = relink; then
+ $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $?
+ fi
+
+ # Do each of the archive commands.
+ if test "$module" = yes && test -n "$module_cmds" ; then
+ if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then
+ eval test_cmds=\"$module_expsym_cmds\"
+ cmds=$module_expsym_cmds
+ else
+ eval test_cmds=\"$module_cmds\"
+ cmds=$module_cmds
+ fi
+ else
+ if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then
+ eval test_cmds=\"$archive_expsym_cmds\"
+ cmds=$archive_expsym_cmds
+ else
+ eval test_cmds=\"$archive_cmds\"
+ cmds=$archive_cmds
+ fi
+ fi
+
+ if test "X$skipped_export" != "X:" &&
+ func_len " $test_cmds" &&
+ len=$func_len_result &&
+ test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then
+ :
+ else
+ # The command line is too long to link in one step, link piecewise
+ # or, if using GNU ld and skipped_export is not :, use a linker
+ # script.
+
+ # Save the value of $output and $libobjs because we want to
+ # use them later. If we have whole_archive_flag_spec, we
+ # want to use save_libobjs as it was before
+ # whole_archive_flag_spec was expanded, because we can't
+ # assume the linker understands whole_archive_flag_spec.
+ # This may have to be revisited, in case too many
+ # convenience libraries get linked in and end up exceeding
+ # the spec.
+ if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then
+ save_libobjs=$libobjs
+ fi
+ save_output=$output
+ output_la=`$ECHO "X$output" | $Xsed -e "$basename"`
+
+ # Clear the reloadable object creation command queue and
+ # initialize k to one.
+ test_cmds=
+ concat_cmds=
+ objlist=
+ last_robj=
+ k=1
+
+ if test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "$with_gnu_ld" = yes; then
+ output=${output_objdir}/${output_la}.lnkscript
+ func_verbose "creating GNU ld script: $output"
+ $ECHO 'INPUT (' > $output
+ for obj in $save_libobjs
+ do
+ $ECHO "$obj" >> $output
+ done
+ $ECHO ')' >> $output
+ delfiles="$delfiles $output"
+ elif test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "X$file_list_spec" != X; then
+ output=${output_objdir}/${output_la}.lnk
+ func_verbose "creating linker input file list: $output"
+ : > $output
+ set x $save_libobjs
+ shift
+ firstobj=
+ if test "$compiler_needs_object" = yes; then
+ firstobj="$1 "
+ shift
+ fi
+ for obj
+ do
+ $ECHO "$obj" >> $output
+ done
+ delfiles="$delfiles $output"
+ output=$firstobj\"$file_list_spec$output\"
+ else
+ if test -n "$save_libobjs"; then
+ func_verbose "creating reloadable object files..."
+ output=$output_objdir/$output_la-${k}.$objext
+ eval test_cmds=\"$reload_cmds\"
+ func_len " $test_cmds"
+ len0=$func_len_result
+ len=$len0
+
+ # Loop over the list of objects to be linked.
+ for obj in $save_libobjs
+ do
+ func_len " $obj"
+ func_arith $len + $func_len_result
+ len=$func_arith_result
+ if test "X$objlist" = X ||
+ test "$len" -lt "$max_cmd_len"; then
+ func_append objlist " $obj"
+ else
+ # The command $test_cmds is almost too long, add a
+ # command to the queue.
+ if test "$k" -eq 1 ; then
+ # The first file doesn't have a previous command to add.
+ eval concat_cmds=\"$reload_cmds $objlist $last_robj\"
+ else
+ # All subsequent reloadable object files will link in
+ # the last one created.
+ eval concat_cmds=\"\$concat_cmds~$reload_cmds $objlist $last_robj~\$RM $last_robj\"
+ fi
+ last_robj=$output_objdir/$output_la-${k}.$objext
+ func_arith $k + 1
+ k=$func_arith_result
+ output=$output_objdir/$output_la-${k}.$objext
+ objlist=$obj
+ func_len " $last_robj"
+ func_arith $len0 + $func_len_result
+ len=$func_arith_result
+ fi
+ done
+ # Handle the remaining objects by creating one last
+ # reloadable object file. All subsequent reloadable object
+ # files will link in the last one created.
+ test -z "$concat_cmds" || concat_cmds=$concat_cmds~
+ eval concat_cmds=\"\${concat_cmds}$reload_cmds $objlist $last_robj\"
+ if test -n "$last_robj"; then
+ eval concat_cmds=\"\${concat_cmds}~\$RM $last_robj\"
+ fi
+ delfiles="$delfiles $output"
+
+ else
+ output=
+ fi
+
+ if ${skipped_export-false}; then
+ func_verbose "generating symbol list for \`$libname.la'"
+ export_symbols="$output_objdir/$libname.exp"
+ $opt_dry_run || $RM $export_symbols
+ libobjs=$output
+ # Append the command to create the export file.
+ test -z "$concat_cmds" || concat_cmds=$concat_cmds~
+ eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\"
+ if test -n "$last_robj"; then
+ eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\"
+ fi
+ fi
+
+ test -n "$save_libobjs" &&
+ func_verbose "creating a temporary reloadable object file: $output"
+
+ # Loop through the commands generated above and execute them.
+ save_ifs="$IFS"; IFS='~'
+ for cmd in $concat_cmds; do
+ IFS="$save_ifs"
+ $opt_silent || {
+ func_quote_for_expand "$cmd"
+ eval "func_echo $func_quote_for_expand_result"
+ }
+ $opt_dry_run || eval "$cmd" || {
+ lt_exit=$?
+
+ # Restore the uninstalled library and exit
+ if test "$mode" = relink; then
+ ( cd "$output_objdir" && \
+ $RM "${realname}T" && \
+ $MV "${realname}U" "$realname" )
+ fi
+
+ exit $lt_exit
+ }
+ done
+ IFS="$save_ifs"
+
+ if test -n "$export_symbols_regex" && ${skipped_export-false}; then
+ func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"'
+ func_show_eval '$MV "${export_symbols}T" "$export_symbols"'
+ fi
+ fi
+
+ if ${skipped_export-false}; then
+ if test -n "$export_symbols" && test -n "$include_expsyms"; then
+ tmp_export_symbols="$export_symbols"
+ test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols"
+ $opt_dry_run || eval '$ECHO "X$include_expsyms" | $Xsed | $SP2NL >> "$tmp_export_symbols"'
+ fi
+
+ if test -n "$orig_export_symbols"; then
+ # The given exports_symbols file has to be filtered, so filter it.
+ func_verbose "filter symbol list for \`$libname.la' to tag DATA exports"
+ # FIXME: $output_objdir/$libname.filter potentially contains lots of
+ # 's' commands which not all seds can handle. GNU sed should be fine
+ # though. Also, the filter scales superlinearly with the number of
+ # global variables. join(1) would be nice here, but unfortunately
+ # isn't a blessed tool.
+ $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter
+ delfiles="$delfiles $export_symbols $output_objdir/$libname.filter"
+ export_symbols=$output_objdir/$libname.def
+ $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols
+ fi
+ fi
+
+ libobjs=$output
+ # Restore the value of output.
+ output=$save_output
+
+ if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then
+ eval libobjs=\"\$libobjs $whole_archive_flag_spec\"
+ test "X$libobjs" = "X " && libobjs=
+ fi
+ # Expand the library linking commands again to reset the
+ # value of $libobjs for piecewise linking.
+
+ # Do each of the archive commands.
+ if test "$module" = yes && test -n "$module_cmds" ; then
+ if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then
+ cmds=$module_expsym_cmds
+ else
+ cmds=$module_cmds
+ fi
+ else
+ if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then
+ cmds=$archive_expsym_cmds
+ else
+ cmds=$archive_cmds
+ fi
+ fi
+ fi
+
+ if test -n "$delfiles"; then
+ # Append the command to remove temporary files to $cmds.
+ eval cmds=\"\$cmds~\$RM $delfiles\"
+ fi
+
+ # Add any objects from preloaded convenience libraries
+ if test -n "$dlprefiles"; then
+ gentop="$output_objdir/${outputname}x"
+ generated="$generated $gentop"
+
+ func_extract_archives $gentop $dlprefiles
+ libobjs="$libobjs $func_extract_archives_result"
+ test "X$libobjs" = "X " && libobjs=
+ fi
+
+ save_ifs="$IFS"; IFS='~'
+ for cmd in $cmds; do
+ IFS="$save_ifs"
+ eval cmd=\"$cmd\"
+ $opt_silent || {
+ func_quote_for_expand "$cmd"
+ eval "func_echo $func_quote_for_expand_result"
+ }
+ $opt_dry_run || eval "$cmd" || {
+ lt_exit=$?
+
+ # Restore the uninstalled library and exit
+ if test "$mode" = relink; then
+ ( cd "$output_objdir" && \
+ $RM "${realname}T" && \
+ $MV "${realname}U" "$realname" )
+ fi
+
+ exit $lt_exit
+ }
+ done
+ IFS="$save_ifs"
+
+ # Restore the uninstalled library and exit
+ if test "$mode" = relink; then
+ $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $?
+
+ if test -n "$convenience"; then
+ if test -z "$whole_archive_flag_spec"; then
+ func_show_eval '${RM}r "$gentop"'
+ fi
+ fi
+
+ exit $EXIT_SUCCESS
+ fi
+
+ # Create links to the real library.
+ for linkname in $linknames; do
+ if test "$realname" != "$linkname"; then
+ func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?'
+ fi
+ done
+
+ # If -module or -export-dynamic was specified, set the dlname.
+ if test "$module" = yes || test "$export_dynamic" = yes; then
+ # On all known operating systems, these are identical.
+ dlname="$soname"
+ fi
+ fi
+ ;;
+
+ obj)
+ if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then
+ func_warning "\`-dlopen' is ignored for objects"
+ fi
+
+ case " $deplibs" in
+ *\ -l* | *\ -L*)
+ func_warning "\`-l' and \`-L' are ignored for objects" ;;
+ esac
+
+ test -n "$rpath" && \
+ func_warning "\`-rpath' is ignored for objects"
+
+ test -n "$xrpath" && \
+ func_warning "\`-R' is ignored for objects"
+
+ test -n "$vinfo" && \
+ func_warning "\`-version-info' is ignored for objects"
+
+ test -n "$release" && \
+ func_warning "\`-release' is ignored for objects"
+
+ case $output in
+ *.lo)
+ test -n "$objs$old_deplibs" && \
+ func_fatal_error "cannot build library object \`$output' from non-libtool objects"
+
+ libobj=$output
+ func_lo2o "$libobj"
+ obj=$func_lo2o_result
+ ;;
+ *)
+ libobj=
+ obj="$output"
+ ;;
+ esac
+
+ # Delete the old objects.
+ $opt_dry_run || $RM $obj $libobj
+
+ # Objects from convenience libraries. This assumes
+ # single-version convenience libraries. Whenever we create
+ # different ones for PIC/non-PIC, this we'll have to duplicate
+ # the extraction.
+ reload_conv_objs=
+ gentop=
+ # reload_cmds runs $LD directly, so let us get rid of
+ # -Wl from whole_archive_flag_spec and hope we can get by with
+ # turning comma into space..
+ wl=
+
+ if test -n "$convenience"; then
+ if test -n "$whole_archive_flag_spec"; then
+ eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\"
+ reload_conv_objs=$reload_objs\ `$ECHO "X$tmp_whole_archive_flags" | $Xsed -e 's|,| |g'`
+ else
+ gentop="$output_objdir/${obj}x"
+ generated="$generated $gentop"
+
+ func_extract_archives $gentop $convenience
+ reload_conv_objs="$reload_objs $func_extract_archives_result"
+ fi
+ fi
+
+ # Create the old-style object.
+ reload_objs="$objs$old_deplibs "`$ECHO "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}$'/d' -e '/\.lib$/d' -e "$lo2o" | $NL2SP`" $reload_conv_objs" ### testsuite: skip nested quoting test
+
+ output="$obj"
+ func_execute_cmds "$reload_cmds" 'exit $?'
+
+ # Exit if we aren't doing a library object file.
+ if test -z "$libobj"; then
+ if test -n "$gentop"; then
+ func_show_eval '${RM}r "$gentop"'
+ fi
+
+ exit $EXIT_SUCCESS
+ fi
+
+ if test "$build_libtool_libs" != yes; then
+ if test -n "$gentop"; then
+ func_show_eval '${RM}r "$gentop"'
+ fi
+
+ # Create an invalid libtool object if no PIC, so that we don't
+ # accidentally link it into a program.
+ # $show "echo timestamp > $libobj"
+ # $opt_dry_run || eval "echo timestamp > $libobj" || exit $?
+ exit $EXIT_SUCCESS
+ fi
+
+ if test -n "$pic_flag" || test "$pic_mode" != default; then
+ # Only do commands if we really have different PIC objects.
+ reload_objs="$libobjs $reload_conv_objs"
+ output="$libobj"
+ func_execute_cmds "$reload_cmds" 'exit $?'
+ fi
+
+ if test -n "$gentop"; then
+ func_show_eval '${RM}r "$gentop"'
+ fi
+
+ exit $EXIT_SUCCESS
+ ;;
+
+ prog)
+ case $host in
+ *cygwin*) func_stripname '' '.exe' "$output"
+ output=$func_stripname_result.exe;;
+ esac
+ test -n "$vinfo" && \
+ func_warning "\`-version-info' is ignored for programs"
+
+ test -n "$release" && \
+ func_warning "\`-release' is ignored for programs"
+
+ test "$preload" = yes \
+ && test "$dlopen_support" = unknown \
+ && test "$dlopen_self" = unknown \
+ && test "$dlopen_self_static" = unknown && \
+ func_warning "\`LT_INIT([dlopen])' not used. Assuming no dlopen support."
+
+ case $host in
+ *-*-rhapsody* | *-*-darwin1.[012])
+ # On Rhapsody replace the C library is the System framework
+ compile_deplibs=`$ECHO "X $compile_deplibs" | $Xsed -e 's/ -lc / System.ltframework /'`
+ finalize_deplibs=`$ECHO "X $finalize_deplibs" | $Xsed -e 's/ -lc / System.ltframework /'`
+ ;;
+ esac
+
+ case $host in
+ *-*-darwin*)
+ # Don't allow lazy linking, it breaks C++ global constructors
+ # But is supposedly fixed on 10.4 or later (yay!).
+ if test "$tagname" = CXX ; then
+ case ${MACOSX_DEPLOYMENT_TARGET-10.0} in
+ 10.[0123])
+ compile_command="$compile_command ${wl}-bind_at_load"
+ finalize_command="$finalize_command ${wl}-bind_at_load"
+ ;;
+ esac
+ fi
+ # Time to change all our "foo.ltframework" stuff back to "-framework foo"
+ compile_deplibs=`$ECHO "X $compile_deplibs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'`
+ finalize_deplibs=`$ECHO "X $finalize_deplibs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'`
+ ;;
+ esac
+
+
+ # move library search paths that coincide with paths to not yet
+ # installed libraries to the beginning of the library search list
+ new_libs=
+ for path in $notinst_path; do
+ case " $new_libs " in
+ *" -L$path/$objdir "*) ;;
+ *)
+ case " $compile_deplibs " in
+ *" -L$path/$objdir "*)
+ new_libs="$new_libs -L$path/$objdir" ;;
+ esac
+ ;;
+ esac
+ done
+ for deplib in $compile_deplibs; do
+ case $deplib in
+ -L*)
+ case " $new_libs " in
+ *" $deplib "*) ;;
+ *) new_libs="$new_libs $deplib" ;;
+ esac
+ ;;
+ *) new_libs="$new_libs $deplib" ;;
+ esac
+ done
+ compile_deplibs="$new_libs"
+
+
+ compile_command="$compile_command $compile_deplibs"
+ finalize_command="$finalize_command $finalize_deplibs"
+
+ if test -n "$rpath$xrpath"; then
+ # If the user specified any rpath flags, then add them.
+ for libdir in $rpath $xrpath; do
+ # This is the magic to use -rpath.
+ case "$finalize_rpath " in
+ *" $libdir "*) ;;
+ *) finalize_rpath="$finalize_rpath $libdir" ;;
+ esac
+ done
+ fi
+
+ # Now hardcode the library paths
+ rpath=
+ hardcode_libdirs=
+ for libdir in $compile_rpath $finalize_rpath; do
+ if test -n "$hardcode_libdir_flag_spec"; then
+ if test -n "$hardcode_libdir_separator"; then
+ if test -z "$hardcode_libdirs"; then
+ hardcode_libdirs="$libdir"
+ else
+ # Just accumulate the unique libdirs.
+ case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+ *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+ ;;
+ *)
+ hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir"
+ ;;
+ esac
+ fi
+ else
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ rpath="$rpath $flag"
+ fi
+ elif test -n "$runpath_var"; then
+ case "$perm_rpath " in
+ *" $libdir "*) ;;
+ *) perm_rpath="$perm_rpath $libdir" ;;
+ esac
+ fi
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+ testbindir=`${ECHO} "$libdir" | ${SED} -e 's*/lib$*/bin*'`
+ case :$dllsearchpath: in
+ *":$libdir:"*) ;;
+ ::) dllsearchpath=$libdir;;
+ *) dllsearchpath="$dllsearchpath:$libdir";;
+ esac
+ case :$dllsearchpath: in
+ *":$testbindir:"*) ;;
+ ::) dllsearchpath=$testbindir;;
+ *) dllsearchpath="$dllsearchpath:$testbindir";;
+ esac
+ ;;
+ esac
+ done
+ # Substitute the hardcoded libdirs into the rpath.
+ if test -n "$hardcode_libdir_separator" &&
+ test -n "$hardcode_libdirs"; then
+ libdir="$hardcode_libdirs"
+ eval rpath=\" $hardcode_libdir_flag_spec\"
+ fi
+ compile_rpath="$rpath"
+
+ rpath=
+ hardcode_libdirs=
+ for libdir in $finalize_rpath; do
+ if test -n "$hardcode_libdir_flag_spec"; then
+ if test -n "$hardcode_libdir_separator"; then
+ if test -z "$hardcode_libdirs"; then
+ hardcode_libdirs="$libdir"
+ else
+ # Just accumulate the unique libdirs.
+ case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+ *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+ ;;
+ *)
+ hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir"
+ ;;
+ esac
+ fi
+ else
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ rpath="$rpath $flag"
+ fi
+ elif test -n "$runpath_var"; then
+ case "$finalize_perm_rpath " in
+ *" $libdir "*) ;;
+ *) finalize_perm_rpath="$finalize_perm_rpath $libdir" ;;
+ esac
+ fi
+ done
+ # Substitute the hardcoded libdirs into the rpath.
+ if test -n "$hardcode_libdir_separator" &&
+ test -n "$hardcode_libdirs"; then
+ libdir="$hardcode_libdirs"
+ eval rpath=\" $hardcode_libdir_flag_spec\"
+ fi
+ finalize_rpath="$rpath"
+
+ if test -n "$libobjs" && test "$build_old_libs" = yes; then
+ # Transform all the library objects into standard objects.
+ compile_command=`$ECHO "X$compile_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP`
+ finalize_command=`$ECHO "X$finalize_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP`
+ fi
+
+ func_generate_dlsyms "$outputname" "@PROGRAM@" "no"
+
+ # template prelinking step
+ if test -n "$prelink_cmds"; then
+ func_execute_cmds "$prelink_cmds" 'exit $?'
+ fi
+
+ wrappers_required=yes
+ case $host in
+ *cygwin* | *mingw* )
+ if test "$build_libtool_libs" != yes; then
+ wrappers_required=no
+ fi
+ ;;
+ *cegcc)
+ # Disable wrappers for cegcc, we are cross compiling anyway.
+ wrappers_required=no
+ ;;
+ *)
+ if test "$need_relink" = no || test "$build_libtool_libs" != yes; then
+ wrappers_required=no
+ fi
+ ;;
+ esac
+ if test "$wrappers_required" = no; then
+ # Replace the output file specification.
+ compile_command=`$ECHO "X$compile_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'`
+ link_command="$compile_command$compile_rpath"
+
+ # We have no uninstalled library dependencies, so finalize right now.
+ exit_status=0
+ func_show_eval "$link_command" 'exit_status=$?'
+
+ # Delete the generated files.
+ if test -f "$output_objdir/${outputname}S.${objext}"; then
+ func_show_eval '$RM "$output_objdir/${outputname}S.${objext}"'
+ fi
+
+ exit $exit_status
+ fi
+
+ if test -n "$compile_shlibpath$finalize_shlibpath"; then
+ compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command"
+ fi
+ if test -n "$finalize_shlibpath"; then
+ finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command"
+ fi
+
+ compile_var=
+ finalize_var=
+ if test -n "$runpath_var"; then
+ if test -n "$perm_rpath"; then
+ # We should set the runpath_var.
+ rpath=
+ for dir in $perm_rpath; do
+ rpath="$rpath$dir:"
+ done
+ compile_var="$runpath_var=\"$rpath\$$runpath_var\" "
+ fi
+ if test -n "$finalize_perm_rpath"; then
+ # We should set the runpath_var.
+ rpath=
+ for dir in $finalize_perm_rpath; do
+ rpath="$rpath$dir:"
+ done
+ finalize_var="$runpath_var=\"$rpath\$$runpath_var\" "
+ fi
+ fi
+
+ if test "$no_install" = yes; then
+ # We don't need to create a wrapper script.
+ link_command="$compile_var$compile_command$compile_rpath"
+ # Replace the output file specification.
+ link_command=`$ECHO "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'`
+ # Delete the old output file.
+ $opt_dry_run || $RM $output
+ # Link the executable and exit
+ func_show_eval "$link_command" 'exit $?'
+ exit $EXIT_SUCCESS
+ fi
+
+ if test "$hardcode_action" = relink; then
+ # Fast installation is not supported
+ link_command="$compile_var$compile_command$compile_rpath"
+ relink_command="$finalize_var$finalize_command$finalize_rpath"
+
+ func_warning "this platform does not like uninstalled shared libraries"
+ func_warning "\`$output' will be relinked during installation"
+ else
+ if test "$fast_install" != no; then
+ link_command="$finalize_var$compile_command$finalize_rpath"
+ if test "$fast_install" = yes; then
+ relink_command=`$ECHO "X$compile_var$compile_command$compile_rpath" | $Xsed -e 's%@OUTPUT@%\$progdir/\$file%g'`
+ else
+ # fast_install is set to needless
+ relink_command=
+ fi
+ else
+ link_command="$compile_var$compile_command$compile_rpath"
+ relink_command="$finalize_var$finalize_command$finalize_rpath"
+ fi
+ fi
+
+ # Replace the output file specification.
+ link_command=`$ECHO "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'`
+
+ # Delete the old output files.
+ $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname
+
+ func_show_eval "$link_command" 'exit $?'
+
+ # Now create the wrapper script.
+ func_verbose "creating $output"
+
+ # Quote the relink command for shipping.
+ if test -n "$relink_command"; then
+ # Preserve any variables that may affect compiler behavior
+ for var in $variables_saved_for_relink; do
+ if eval test -z \"\${$var+set}\"; then
+ relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command"
+ elif eval var_value=\$$var; test -z "$var_value"; then
+ relink_command="$var=; export $var; $relink_command"
+ else
+ func_quote_for_eval "$var_value"
+ relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command"
+ fi
+ done
+ relink_command="(cd `pwd`; $relink_command)"
+ relink_command=`$ECHO "X$relink_command" | $Xsed -e "$sed_quote_subst"`
+ fi
+
+ # Quote $ECHO for shipping.
+ if test "X$ECHO" = "X$SHELL $progpath --fallback-echo"; then
+ case $progpath in
+ [\\/]* | [A-Za-z]:[\\/]*) qecho="$SHELL $progpath --fallback-echo";;
+ *) qecho="$SHELL `pwd`/$progpath --fallback-echo";;
+ esac
+ qecho=`$ECHO "X$qecho" | $Xsed -e "$sed_quote_subst"`
+ else
+ qecho=`$ECHO "X$ECHO" | $Xsed -e "$sed_quote_subst"`
+ fi
+
+ # Only actually do things if not in dry run mode.
+ $opt_dry_run || {
+ # win32 will think the script is a binary if it has
+ # a .exe suffix, so we strip it off here.
+ case $output in
+ *.exe) func_stripname '' '.exe' "$output"
+ output=$func_stripname_result ;;
+ esac
+ # test for cygwin because mv fails w/o .exe extensions
+ case $host in
+ *cygwin*)
+ exeext=.exe
+ func_stripname '' '.exe' "$outputname"
+ outputname=$func_stripname_result ;;
+ *) exeext= ;;
+ esac
+ case $host in
+ *cygwin* | *mingw* )
+ func_dirname_and_basename "$output" "" "."
+ output_name=$func_basename_result
+ output_path=$func_dirname_result
+ cwrappersource="$output_path/$objdir/lt-$output_name.c"
+ cwrapper="$output_path/$output_name.exe"
+ $RM $cwrappersource $cwrapper
+ trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15
+
+ func_emit_cwrapperexe_src > $cwrappersource
+
+ # The wrapper executable is built using the $host compiler,
+ # because it contains $host paths and files. If cross-
+ # compiling, it, like the target executable, must be
+ # executed on the $host or under an emulation environment.
+ $opt_dry_run || {
+ $LTCC $LTCFLAGS -o $cwrapper $cwrappersource
+ $STRIP $cwrapper
+ }
+
+ # Now, create the wrapper script for func_source use:
+ func_ltwrapper_scriptname $cwrapper
+ $RM $func_ltwrapper_scriptname_result
+ trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15
+ $opt_dry_run || {
+ # note: this script will not be executed, so do not chmod.
+ if test "x$build" = "x$host" ; then
+ $cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result
+ else
+ func_emit_wrapper no > $func_ltwrapper_scriptname_result
+ fi
+ }
+ ;;
+ * )
+ $RM $output
+ trap "$RM $output; exit $EXIT_FAILURE" 1 2 15
+
+ func_emit_wrapper no > $output
+ chmod +x $output
+ ;;
+ esac
+ }
+ exit $EXIT_SUCCESS
+ ;;
+ esac
+
+ # See if we need to build an old-fashioned archive.
+ for oldlib in $oldlibs; do
+
+ if test "$build_libtool_libs" = convenience; then
+ oldobjs="$libobjs_save $symfileobj"
+ addlibs="$convenience"
+ build_libtool_libs=no
+ else
+ if test "$build_libtool_libs" = module; then
+ oldobjs="$libobjs_save"
+ build_libtool_libs=no
+ else
+ oldobjs="$old_deplibs $non_pic_objects"
+ if test "$preload" = yes && test -f "$symfileobj"; then
+ oldobjs="$oldobjs $symfileobj"
+ fi
+ fi
+ addlibs="$old_convenience"
+ fi
+
+ if test -n "$addlibs"; then
+ gentop="$output_objdir/${outputname}x"
+ generated="$generated $gentop"
+
+ func_extract_archives $gentop $addlibs
+ oldobjs="$oldobjs $func_extract_archives_result"
+ fi
+
+ # Do each command in the archive commands.
+ if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then
+ cmds=$old_archive_from_new_cmds
+ else
+
+ # Add any objects from preloaded convenience libraries
+ if test -n "$dlprefiles"; then
+ gentop="$output_objdir/${outputname}x"
+ generated="$generated $gentop"
+
+ func_extract_archives $gentop $dlprefiles
+ oldobjs="$oldobjs $func_extract_archives_result"
+ fi
+
+ # POSIX demands no paths to be encoded in archives. We have
+ # to avoid creating archives with duplicate basenames if we
+ # might have to extract them afterwards, e.g., when creating a
+ # static archive out of a convenience library, or when linking
+ # the entirety of a libtool archive into another (currently
+ # not supported by libtool).
+ if (for obj in $oldobjs
+ do
+ func_basename "$obj"
+ $ECHO "$func_basename_result"
+ done | sort | sort -uc >/dev/null 2>&1); then
+ :
+ else
+ $ECHO "copying selected object files to avoid basename conflicts..."
+ gentop="$output_objdir/${outputname}x"
+ generated="$generated $gentop"
+ func_mkdir_p "$gentop"
+ save_oldobjs=$oldobjs
+ oldobjs=
+ counter=1
+ for obj in $save_oldobjs
+ do
+ func_basename "$obj"
+ objbase="$func_basename_result"
+ case " $oldobjs " in
+ " ") oldobjs=$obj ;;
+ *[\ /]"$objbase "*)
+ while :; do
+ # Make sure we don't pick an alternate name that also
+ # overlaps.
+ newobj=lt$counter-$objbase
+ func_arith $counter + 1
+ counter=$func_arith_result
+ case " $oldobjs " in
+ *[\ /]"$newobj "*) ;;
+ *) if test ! -f "$gentop/$newobj"; then break; fi ;;
+ esac
+ done
+ func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj"
+ oldobjs="$oldobjs $gentop/$newobj"
+ ;;
+ *) oldobjs="$oldobjs $obj" ;;
+ esac
+ done
+ fi
+ eval cmds=\"$old_archive_cmds\"
+
+ func_len " $cmds"
+ len=$func_len_result
+ if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then
+ cmds=$old_archive_cmds
+ else
+ # the command line is too long to link in one step, link in parts
+ func_verbose "using piecewise archive linking..."
+ save_RANLIB=$RANLIB
+ RANLIB=:
+ objlist=
+ concat_cmds=
+ save_oldobjs=$oldobjs
+ oldobjs=
+ # Is there a better way of finding the last object in the list?
+ for obj in $save_oldobjs
+ do
+ last_oldobj=$obj
+ done
+ eval test_cmds=\"$old_archive_cmds\"
+ func_len " $test_cmds"
+ len0=$func_len_result
+ len=$len0
+ for obj in $save_oldobjs
+ do
+ func_len " $obj"
+ func_arith $len + $func_len_result
+ len=$func_arith_result
+ func_append objlist " $obj"
+ if test "$len" -lt "$max_cmd_len"; then
+ :
+ else
+ # the above command should be used before it gets too long
+ oldobjs=$objlist
+ if test "$obj" = "$last_oldobj" ; then
+ RANLIB=$save_RANLIB
+ fi
+ test -z "$concat_cmds" || concat_cmds=$concat_cmds~
+ eval concat_cmds=\"\${concat_cmds}$old_archive_cmds\"
+ objlist=
+ len=$len0
+ fi
+ done
+ RANLIB=$save_RANLIB
+ oldobjs=$objlist
+ if test "X$oldobjs" = "X" ; then
+ eval cmds=\"\$concat_cmds\"
+ else
+ eval cmds=\"\$concat_cmds~\$old_archive_cmds\"
+ fi
+ fi
+ fi
+ func_execute_cmds "$cmds" 'exit $?'
+ done
+
+ test -n "$generated" && \
+ func_show_eval "${RM}r$generated"
+
+ # Now create the libtool archive.
+ case $output in
+ *.la)
+ old_library=
+ test "$build_old_libs" = yes && old_library="$libname.$libext"
+ func_verbose "creating $output"
+
+ # Preserve any variables that may affect compiler behavior
+ for var in $variables_saved_for_relink; do
+ if eval test -z \"\${$var+set}\"; then
+ relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command"
+ elif eval var_value=\$$var; test -z "$var_value"; then
+ relink_command="$var=; export $var; $relink_command"
+ else
+ func_quote_for_eval "$var_value"
+ relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command"
+ fi
+ done
+ # Quote the link command for shipping.
+ relink_command="(cd `pwd`; $SHELL $progpath $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)"
+ relink_command=`$ECHO "X$relink_command" | $Xsed -e "$sed_quote_subst"`
+ if test "$hardcode_automatic" = yes ; then
+ relink_command=
+ fi
+
+ # Only create the output if not a dry run.
+ $opt_dry_run || {
+ for installed in no yes; do
+ if test "$installed" = yes; then
+ if test -z "$install_libdir"; then
+ break
+ fi
+ output="$output_objdir/$outputname"i
+ # Replace all uninstalled libtool libraries with the installed ones
+ newdependency_libs=
+ for deplib in $dependency_libs; do
+ case $deplib in
+ *.la)
+ func_basename "$deplib"
+ name="$func_basename_result"
+ eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib`
+ test -z "$libdir" && \
+ func_fatal_error "\`$deplib' is not a valid libtool archive"
+ newdependency_libs="$newdependency_libs $libdir/$name"
+ ;;
+ *) newdependency_libs="$newdependency_libs $deplib" ;;
+ esac
+ done
+ dependency_libs="$newdependency_libs"
+ newdlfiles=
+
+ for lib in $dlfiles; do
+ case $lib in
+ *.la)
+ func_basename "$lib"
+ name="$func_basename_result"
+ eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib`
+ test -z "$libdir" && \
+ func_fatal_error "\`$lib' is not a valid libtool archive"
+ newdlfiles="$newdlfiles $libdir/$name"
+ ;;
+ *) newdlfiles="$newdlfiles $lib" ;;
+ esac
+ done
+ dlfiles="$newdlfiles"
+ newdlprefiles=
+ for lib in $dlprefiles; do
+ case $lib in
+ *.la)
+ # Only pass preopened files to the pseudo-archive (for
+ # eventual linking with the app. that links it) if we
+ # didn't already link the preopened objects directly into
+ # the library:
+ func_basename "$lib"
+ name="$func_basename_result"
+ eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib`
+ test -z "$libdir" && \
+ func_fatal_error "\`$lib' is not a valid libtool archive"
+ newdlprefiles="$newdlprefiles $libdir/$name"
+ ;;
+ esac
+ done
+ dlprefiles="$newdlprefiles"
+ else
+ newdlfiles=
+ for lib in $dlfiles; do
+ case $lib in
+ [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;;
+ *) abs=`pwd`"/$lib" ;;
+ esac
+ newdlfiles="$newdlfiles $abs"
+ done
+ dlfiles="$newdlfiles"
+ newdlprefiles=
+ for lib in $dlprefiles; do
+ case $lib in
+ [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;;
+ *) abs=`pwd`"/$lib" ;;
+ esac
+ newdlprefiles="$newdlprefiles $abs"
+ done
+ dlprefiles="$newdlprefiles"
+ fi
+ $RM $output
+ # place dlname in correct position for cygwin
+ tdlname=$dlname
+ case $host,$output,$installed,$module,$dlname in
+ *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll) tdlname=../bin/$dlname ;;
+ esac
+ $ECHO > $output "\
+# $outputname - a libtool library file
+# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION
+#
+# Please DO NOT delete this file!
+# It is necessary for linking the library.
+
+# The name that we can dlopen(3).
+dlname='$tdlname'
+
+# Names of this library.
+library_names='$library_names'
+
+# The name of the static archive.
+old_library='$old_library'
+
+# Linker flags that can not go in dependency_libs.
+inherited_linker_flags='$new_inherited_linker_flags'
+
+# Libraries that this one depends upon.
+dependency_libs='$dependency_libs'
+
+# Names of additional weak libraries provided by this library
+weak_library_names='$weak_libs'
+
+# Version information for $libname.
+current=$current
+age=$age
+revision=$revision
+
+# Is this an already installed library?
+installed=$installed
+
+# Should we warn about portability when linking against -modules?
+shouldnotlink=$module
+
+# Files to dlopen/dlpreopen
+dlopen='$dlfiles'
+dlpreopen='$dlprefiles'
+
+# Directory that this library needs to be installed in:
+libdir='$install_libdir'"
+ if test "$installed" = no && test "$need_relink" = yes; then
+ $ECHO >> $output "\
+relink_command=\"$relink_command\""
+ fi
+ done
+ }
+
+ # Do a symbolic link so that the libtool archive can be found in
+ # LD_LIBRARY_PATH before the program is installed.
+ func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?'
+ ;;
+ esac
+ exit $EXIT_SUCCESS
+}
+
+{ test "$mode" = link || test "$mode" = relink; } &&
+ func_mode_link ${1+"$@"}
+
+
+# func_mode_uninstall arg...
+func_mode_uninstall ()
+{
+ $opt_debug
+ RM="$nonopt"
+ files=
+ rmforce=
+ exit_status=0
+
+ # This variable tells wrapper scripts just to set variables rather
+ # than running their programs.
+ libtool_install_magic="$magic"
+
+ for arg
+ do
+ case $arg in
+ -f) RM="$RM $arg"; rmforce=yes ;;
+ -*) RM="$RM $arg" ;;
+ *) files="$files $arg" ;;
+ esac
+ done
+
+ test -z "$RM" && \
+ func_fatal_help "you must specify an RM program"
+
+ rmdirs=
+
+ origobjdir="$objdir"
+ for file in $files; do
+ func_dirname "$file" "" "."
+ dir="$func_dirname_result"
+ if test "X$dir" = X.; then
+ objdir="$origobjdir"
+ else
+ objdir="$dir/$origobjdir"
+ fi
+ func_basename "$file"
+ name="$func_basename_result"
+ test "$mode" = uninstall && objdir="$dir"
+
+ # Remember objdir for removal later, being careful to avoid duplicates
+ if test "$mode" = clean; then
+ case " $rmdirs " in
+ *" $objdir "*) ;;
+ *) rmdirs="$rmdirs $objdir" ;;
+ esac
+ fi
+
+ # Don't error if the file doesn't exist and rm -f was used.
+ if { test -L "$file"; } >/dev/null 2>&1 ||
+ { test -h "$file"; } >/dev/null 2>&1 ||
+ test -f "$file"; then
+ :
+ elif test -d "$file"; then
+ exit_status=1
+ continue
+ elif test "$rmforce" = yes; then
+ continue
+ fi
+
+ rmfiles="$file"
+
+ case $name in
+ *.la)
+ # Possibly a libtool archive, so verify it.
+ if func_lalib_p "$file"; then
+ func_source $dir/$name
+
+ # Delete the libtool libraries and symlinks.
+ for n in $library_names; do
+ rmfiles="$rmfiles $objdir/$n"
+ done
+ test -n "$old_library" && rmfiles="$rmfiles $objdir/$old_library"
+
+ case "$mode" in
+ clean)
+ case " $library_names " in
+ # " " in the beginning catches empty $dlname
+ *" $dlname "*) ;;
+ *) rmfiles="$rmfiles $objdir/$dlname" ;;
+ esac
+ test -n "$libdir" && rmfiles="$rmfiles $objdir/$name $objdir/${name}i"
+ ;;
+ uninstall)
+ if test -n "$library_names"; then
+ # Do each command in the postuninstall commands.
+ func_execute_cmds "$postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1'
+ fi
+
+ if test -n "$old_library"; then
+ # Do each command in the old_postuninstall commands.
+ func_execute_cmds "$old_postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1'
+ fi
+ # FIXME: should reinstall the best remaining shared library.
+ ;;
+ esac
+ fi
+ ;;
+
+ *.lo)
+ # Possibly a libtool object, so verify it.
+ if func_lalib_p "$file"; then
+
+ # Read the .lo file
+ func_source $dir/$name
+
+ # Add PIC object to the list of files to remove.
+ if test -n "$pic_object" &&
+ test "$pic_object" != none; then
+ rmfiles="$rmfiles $dir/$pic_object"
+ fi
+
+ # Add non-PIC object to the list of files to remove.
+ if test -n "$non_pic_object" &&
+ test "$non_pic_object" != none; then
+ rmfiles="$rmfiles $dir/$non_pic_object"
+ fi
+ fi
+ ;;
+
+ *)
+ if test "$mode" = clean ; then
+ noexename=$name
+ case $file in
+ *.exe)
+ func_stripname '' '.exe' "$file"
+ file=$func_stripname_result
+ func_stripname '' '.exe' "$name"
+ noexename=$func_stripname_result
+ # $file with .exe has already been added to rmfiles,
+ # add $file without .exe
+ rmfiles="$rmfiles $file"
+ ;;
+ esac
+ # Do a test to see if this is a libtool program.
+ if func_ltwrapper_p "$file"; then
+ if func_ltwrapper_executable_p "$file"; then
+ func_ltwrapper_scriptname "$file"
+ relink_command=
+ func_source $func_ltwrapper_scriptname_result
+ rmfiles="$rmfiles $func_ltwrapper_scriptname_result"
+ else
+ relink_command=
+ func_source $dir/$noexename
+ fi
+
+ # note $name still contains .exe if it was in $file originally
+ # as does the version of $file that was added into $rmfiles
+ rmfiles="$rmfiles $objdir/$name $objdir/${name}S.${objext}"
+ if test "$fast_install" = yes && test -n "$relink_command"; then
+ rmfiles="$rmfiles $objdir/lt-$name"
+ fi
+ if test "X$noexename" != "X$name" ; then
+ rmfiles="$rmfiles $objdir/lt-${noexename}.c"
+ fi
+ fi
+ fi
+ ;;
+ esac
+ func_show_eval "$RM $rmfiles" 'exit_status=1'
+ done
+ objdir="$origobjdir"
+
+ # Try to remove the ${objdir}s in the directories where we deleted files
+ for dir in $rmdirs; do
+ if test -d "$dir"; then
+ func_show_eval "rmdir $dir >/dev/null 2>&1"
+ fi
+ done
+
+ exit $exit_status
+}
+
+{ test "$mode" = uninstall || test "$mode" = clean; } &&
+ func_mode_uninstall ${1+"$@"}
+
+test -z "$mode" && {
+ help="$generic_help"
+ func_fatal_help "you must specify a MODE"
+}
+
+test -z "$exec_cmd" && \
+ func_fatal_help "invalid operation mode \`$mode'"
+
+if test -n "$exec_cmd"; then
+ eval exec "$exec_cmd"
+ exit $EXIT_FAILURE
+fi
+
+exit $exit_status
+
+
+# The TAGs below are defined such that we never get into a situation
+# in which we disable both kinds of libraries. Given conflicting
+# choices, we go for a static library, that is the most portable,
+# since we can't tell whether shared libraries were disabled because
+# the user asked for that or because the platform doesn't support
+# them. This is particularly important on AIX, because we don't
+# support having both static and shared libraries enabled at the same
+# time on that platform, so we default to a shared-only configuration.
+# If a disable-shared tag is given, we'll fallback to a static-only
+# configuration. But we'll never go from static-only to shared-only.
+
+# ### BEGIN LIBTOOL TAG CONFIG: disable-shared
+build_libtool_libs=no
+build_old_libs=yes
+# ### END LIBTOOL TAG CONFIG: disable-shared
+
+# ### BEGIN LIBTOOL TAG CONFIG: disable-static
+build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac`
+# ### END LIBTOOL TAG CONFIG: disable-static
+
+# Local Variables:
+# mode:shell-script
+# sh-indentation:2
+# End:
+# vi:sw=2
+
diff --git a/missing b/missing
new file mode 100755
index 0000000..28055d2
--- /dev/null
+++ b/missing
@@ -0,0 +1,376 @@
+#! /bin/sh
+# Common stub for a few missing GNU programs while installing.
+
+scriptversion=2009-04-28.21; # UTC
+
+# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005, 2006,
+# 2008, 2009 Free Software Foundation, Inc.
+# Originally by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
+
+# 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, 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, see <http://www.gnu.org/licenses/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+if test $# -eq 0; then
+ echo 1>&2 "Try \`$0 --help' for more information"
+ exit 1
+fi
+
+run=:
+sed_output='s/.* --output[ =]\([^ ]*\).*/\1/p'
+sed_minuso='s/.* -o \([^ ]*\).*/\1/p'
+
+# In the cases where this matters, `missing' is being run in the
+# srcdir already.
+if test -f configure.ac; then
+ configure_ac=configure.ac
+else
+ configure_ac=configure.in
+fi
+
+msg="missing on your system"
+
+case $1 in
+--run)
+ # Try to run requested program, and just exit if it succeeds.
+ run=
+ shift
+ "$@" && exit 0
+ # Exit code 63 means version mismatch. This often happens
+ # when the user try to use an ancient version of a tool on
+ # a file that requires a minimum version. In this case we
+ # we should proceed has if the program had been absent, or
+ # if --run hadn't been passed.
+ if test $? = 63; then
+ run=:
+ msg="probably too old"
+ fi
+ ;;
+
+ -h|--h|--he|--hel|--help)
+ echo "\
+$0 [OPTION]... PROGRAM [ARGUMENT]...
+
+Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an
+error status if there is no known handling for PROGRAM.
+
+Options:
+ -h, --help display this help and exit
+ -v, --version output version information and exit
+ --run try to run the given command, and emulate it if it fails
+
+Supported PROGRAM values:
+ aclocal touch file \`aclocal.m4'
+ autoconf touch file \`configure'
+ autoheader touch file \`config.h.in'
+ autom4te touch the output file, or create a stub one
+ automake touch all \`Makefile.in' files
+ bison create \`y.tab.[ch]', if possible, from existing .[ch]
+ flex create \`lex.yy.c', if possible, from existing .c
+ help2man touch the output file
+ lex create \`lex.yy.c', if possible, from existing .c
+ makeinfo touch the output file
+ tar try tar, gnutar, gtar, then tar without non-portable flags
+ yacc create \`y.tab.[ch]', if possible, from existing .[ch]
+
+Version suffixes to PROGRAM as well as the prefixes \`gnu-', \`gnu', and
+\`g' are ignored when checking the name.
+
+Send bug reports to <bug-automake@gnu.org>."
+ exit $?
+ ;;
+
+ -v|--v|--ve|--ver|--vers|--versi|--versio|--version)
+ echo "missing $scriptversion (GNU Automake)"
+ exit $?
+ ;;
+
+ -*)
+ echo 1>&2 "$0: Unknown \`$1' option"
+ echo 1>&2 "Try \`$0 --help' for more information"
+ exit 1
+ ;;
+
+esac
+
+# normalize program name to check for.
+program=`echo "$1" | sed '
+ s/^gnu-//; t
+ s/^gnu//; t
+ s/^g//; t'`
+
+# Now exit if we have it, but it failed. Also exit now if we
+# don't have it and --version was passed (most likely to detect
+# the program). This is about non-GNU programs, so use $1 not
+# $program.
+case $1 in
+ lex*|yacc*)
+ # Not GNU programs, they don't have --version.
+ ;;
+
+ tar*)
+ if test -n "$run"; then
+ echo 1>&2 "ERROR: \`tar' requires --run"
+ exit 1
+ elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
+ exit 1
+ fi
+ ;;
+
+ *)
+ if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
+ # We have it, but it failed.
+ exit 1
+ elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
+ # Could not run --version or --help. This is probably someone
+ # running `$TOOL --version' or `$TOOL --help' to check whether
+ # $TOOL exists and not knowing $TOOL uses missing.
+ exit 1
+ fi
+ ;;
+esac
+
+# If it does not exist, or fails to run (possibly an outdated version),
+# try to emulate it.
+case $program in
+ aclocal*)
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified \`acinclude.m4' or \`${configure_ac}'. You might want
+ to install the \`Automake' and \`Perl' packages. Grab them from
+ any GNU archive site."
+ touch aclocal.m4
+ ;;
+
+ autoconf*)
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified \`${configure_ac}'. You might want to install the
+ \`Autoconf' and \`GNU m4' packages. Grab them from any GNU
+ archive site."
+ touch configure
+ ;;
+
+ autoheader*)
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified \`acconfig.h' or \`${configure_ac}'. You might want
+ to install the \`Autoconf' and \`GNU m4' packages. Grab them
+ from any GNU archive site."
+ files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}`
+ test -z "$files" && files="config.h"
+ touch_files=
+ for f in $files; do
+ case $f in
+ *:*) touch_files="$touch_files "`echo "$f" |
+ sed -e 's/^[^:]*://' -e 's/:.*//'`;;
+ *) touch_files="$touch_files $f.in";;
+ esac
+ done
+ touch $touch_files
+ ;;
+
+ automake*)
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'.
+ You might want to install the \`Automake' and \`Perl' packages.
+ Grab them from any GNU archive site."
+ find . -type f -name Makefile.am -print |
+ sed 's/\.am$/.in/' |
+ while read f; do touch "$f"; done
+ ;;
+
+ autom4te*)
+ echo 1>&2 "\
+WARNING: \`$1' is needed, but is $msg.
+ You might have modified some files without having the
+ proper tools for further handling them.
+ You can get \`$1' as part of \`Autoconf' from any GNU
+ archive site."
+
+ file=`echo "$*" | sed -n "$sed_output"`
+ test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
+ if test -f "$file"; then
+ touch $file
+ else
+ test -z "$file" || exec >$file
+ echo "#! /bin/sh"
+ echo "# Created by GNU Automake missing as a replacement of"
+ echo "# $ $@"
+ echo "exit 0"
+ chmod +x $file
+ exit 1
+ fi
+ ;;
+
+ bison*|yacc*)
+ echo 1>&2 "\
+WARNING: \`$1' $msg. You should only need it if
+ you modified a \`.y' file. You may need the \`Bison' package
+ in order for those modifications to take effect. You can get
+ \`Bison' from any GNU archive site."
+ rm -f y.tab.c y.tab.h
+ if test $# -ne 1; then
+ eval LASTARG="\${$#}"
+ case $LASTARG in
+ *.y)
+ SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'`
+ if test -f "$SRCFILE"; then
+ cp "$SRCFILE" y.tab.c
+ fi
+ SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'`
+ if test -f "$SRCFILE"; then
+ cp "$SRCFILE" y.tab.h
+ fi
+ ;;
+ esac
+ fi
+ if test ! -f y.tab.h; then
+ echo >y.tab.h
+ fi
+ if test ! -f y.tab.c; then
+ echo 'main() { return 0; }' >y.tab.c
+ fi
+ ;;
+
+ lex*|flex*)
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified a \`.l' file. You may need the \`Flex' package
+ in order for those modifications to take effect. You can get
+ \`Flex' from any GNU archive site."
+ rm -f lex.yy.c
+ if test $# -ne 1; then
+ eval LASTARG="\${$#}"
+ case $LASTARG in
+ *.l)
+ SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'`
+ if test -f "$SRCFILE"; then
+ cp "$SRCFILE" lex.yy.c
+ fi
+ ;;
+ esac
+ fi
+ if test ! -f lex.yy.c; then
+ echo 'main() { return 0; }' >lex.yy.c
+ fi
+ ;;
+
+ help2man*)
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified a dependency of a manual page. You may need the
+ \`Help2man' package in order for those modifications to take
+ effect. You can get \`Help2man' from any GNU archive site."
+
+ file=`echo "$*" | sed -n "$sed_output"`
+ test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
+ if test -f "$file"; then
+ touch $file
+ else
+ test -z "$file" || exec >$file
+ echo ".ab help2man is required to generate this page"
+ exit $?
+ fi
+ ;;
+
+ makeinfo*)
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified a \`.texi' or \`.texinfo' file, or any other file
+ indirectly affecting the aspect of the manual. The spurious
+ call might also be the consequence of using a buggy \`make' (AIX,
+ DU, IRIX). You might want to install the \`Texinfo' package or
+ the \`GNU make' package. Grab either from any GNU archive site."
+ # The file to touch is that specified with -o ...
+ file=`echo "$*" | sed -n "$sed_output"`
+ test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
+ if test -z "$file"; then
+ # ... or it is the one specified with @setfilename ...
+ infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'`
+ file=`sed -n '
+ /^@setfilename/{
+ s/.* \([^ ]*\) *$/\1/
+ p
+ q
+ }' $infile`
+ # ... or it is derived from the source name (dir/f.texi becomes f.info)
+ test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info
+ fi
+ # If the file does not exist, the user really needs makeinfo;
+ # let's fail without touching anything.
+ test -f $file || exit 1
+ touch $file
+ ;;
+
+ tar*)
+ shift
+
+ # We have already tried tar in the generic part.
+ # Look for gnutar/gtar before invocation to avoid ugly error
+ # messages.
+ if (gnutar --version > /dev/null 2>&1); then
+ gnutar "$@" && exit 0
+ fi
+ if (gtar --version > /dev/null 2>&1); then
+ gtar "$@" && exit 0
+ fi
+ firstarg="$1"
+ if shift; then
+ case $firstarg in
+ *o*)
+ firstarg=`echo "$firstarg" | sed s/o//`
+ tar "$firstarg" "$@" && exit 0
+ ;;
+ esac
+ case $firstarg in
+ *h*)
+ firstarg=`echo "$firstarg" | sed s/h//`
+ tar "$firstarg" "$@" && exit 0
+ ;;
+ esac
+ fi
+
+ echo 1>&2 "\
+WARNING: I can't seem to be able to run \`tar' with the given arguments.
+ You may want to install GNU tar or Free paxutils, or check the
+ command line arguments."
+ exit 1
+ ;;
+
+ *)
+ echo 1>&2 "\
+WARNING: \`$1' is needed, and is $msg.
+ You might have modified some files without having the
+ proper tools for further handling them. Check the \`README' file,
+ it often tells you about the needed prerequisites for installing
+ this package. You may also peek at any GNU archive site, in case
+ some other package would contain this missing \`$1' program."
+ exit 1
+ ;;
+esac
+
+exit 0
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/network/common.c b/network/common.c
new file mode 100644
index 0000000..ef72679
--- /dev/null
+++ b/network/common.c
@@ -0,0 +1,262 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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 <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <net/if.h>
+#include <linux/sockios.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/bnep.h>
+
+#include <glib.h>
+
+#include "log.h"
+#include "common.h"
+
+static int ctl;
+
+static struct {
+ const char *name; /* Friendly name */
+ const char *uuid128; /* UUID 128 */
+ uint16_t id; /* Service class identifier */
+} __svc[] = {
+ { "panu", PANU_UUID, BNEP_SVC_PANU },
+ { "gn", GN_UUID, BNEP_SVC_GN },
+ { "nap", NAP_UUID, BNEP_SVC_NAP },
+ { NULL }
+};
+
+uint16_t bnep_service_id(const char *svc)
+{
+ int i;
+ uint16_t id;
+
+ /* Friendly service name */
+ for (i = 0; __svc[i].name; i++)
+ if (!strcasecmp(svc, __svc[i].name)) {
+ return __svc[i].id;
+ }
+
+ /* UUID 128 string */
+ for (i = 0; __svc[i].uuid128; i++)
+ if (!strcasecmp(svc, __svc[i].uuid128)) {
+ return __svc[i].id;
+ }
+
+ /* Try convert to HEX */
+ id = strtol(svc, NULL, 16);
+ if ((id < BNEP_SVC_PANU) || (id > BNEP_SVC_GN))
+ return 0;
+
+ return id;
+}
+
+const char *bnep_uuid(uint16_t id)
+{
+ int i;
+
+ for (i = 0; __svc[i].uuid128; i++)
+ if (__svc[i].id == id)
+ return __svc[i].uuid128;
+ return NULL;
+}
+
+const char *bnep_name(uint16_t id)
+{
+ int i;
+
+ for (i = 0; __svc[i].name; i++)
+ if (__svc[i].id == id)
+ return __svc[i].name;
+ return NULL;
+}
+
+int bnep_init(void)
+{
+ ctl = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_BNEP);
+
+ if (ctl < 0) {
+ int err = errno;
+ error("Failed to open control socket: %s (%d)",
+ strerror(err), err);
+ return -err;
+ }
+
+ return 0;
+}
+
+int bnep_cleanup(void)
+{
+ close(ctl);
+ return 0;
+}
+
+int bnep_kill_connection(bdaddr_t *dst)
+{
+ struct bnep_conndel_req req;
+
+ memset(&req, 0, sizeof(req));
+ baswap((bdaddr_t *)&req.dst, dst);
+ req.flags = 0;
+ if (ioctl(ctl, BNEPCONNDEL, &req)) {
+ int err = errno;
+ error("Failed to kill connection: %s (%d)",
+ strerror(err), err);
+ return -err;
+ }
+ return 0;
+}
+
+int bnep_kill_all_connections(void)
+{
+ struct bnep_connlist_req req;
+ struct bnep_conninfo ci[7];
+ unsigned int i;
+ int err;
+
+ memset(&req, 0, sizeof(req));
+ req.cnum = 7;
+ req.ci = ci;
+ if (ioctl(ctl, BNEPGETCONNLIST, &req)) {
+ err = errno;
+ error("Failed to get connection list: %s (%d)",
+ strerror(err), err);
+ return -err;
+ }
+
+ for (i = 0; i < req.cnum; i++) {
+ struct bnep_conndel_req del;
+
+ memset(&del, 0, sizeof(del));
+ memcpy(del.dst, ci[i].dst, ETH_ALEN);
+ del.flags = 0;
+ ioctl(ctl, BNEPCONNDEL, &del);
+ }
+ return 0;
+}
+
+int bnep_connadd(int sk, uint16_t role, char *dev)
+{
+ struct bnep_connadd_req req;
+
+ memset(&req, 0, sizeof(req));
+ strncpy(req.device, dev, 16);
+ req.device[15] = '\0';
+ req.sock = sk;
+ req.role = role;
+ if (ioctl(ctl, BNEPCONNADD, &req) < 0) {
+ int err = errno;
+ error("Failed to add device %s: %s(%d)",
+ dev, strerror(err), err);
+ return -err;
+ }
+
+ strncpy(dev, req.device, 16);
+ return 0;
+}
+
+int bnep_if_up(const char *devname)
+{
+ struct ifreq ifr;
+ int sk, err;
+
+ sk = socket(AF_INET, SOCK_DGRAM, 0);
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, devname, IF_NAMESIZE - 1);
+
+ ifr.ifr_flags |= IFF_UP;
+ ifr.ifr_flags |= IFF_MULTICAST;
+
+ err = ioctl(sk, SIOCSIFFLAGS, (caddr_t) &ifr);
+
+ close(sk);
+
+ if (err < 0) {
+ error("Could not bring up %s", devname);
+ return err;
+ }
+
+ return 0;
+}
+
+int bnep_if_down(const char *devname)
+{
+ struct ifreq ifr;
+ int sk, err;
+
+ sk = socket(AF_INET, SOCK_DGRAM, 0);
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, devname, IF_NAMESIZE - 1);
+
+ ifr.ifr_flags &= ~IFF_UP;
+
+ /* Bring down the interface */
+ err = ioctl(sk, SIOCSIFFLAGS, (caddr_t) &ifr);
+
+ close(sk);
+
+ return 0;
+}
+
+int bnep_add_to_bridge(const char *devname, const char *bridge)
+{
+ int ifindex = if_nametoindex(devname);
+ struct ifreq ifr;
+ int sk, err;
+
+ if (!devname || !bridge)
+ return -EINVAL;
+
+ sk = socket(AF_INET, SOCK_STREAM, 0);
+ if (sk < 0)
+ return -1;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, bridge, IFNAMSIZ - 1);
+ ifr.ifr_ifindex = ifindex;
+
+ err = ioctl(sk, SIOCBRADDIF, &ifr);
+
+ close(sk);
+
+ if (err < 0)
+ return err;
+
+ info("bridge %s: interface %s added", bridge, devname);
+
+ return 0;
+}
diff --git a/network/common.h b/network/common.h
new file mode 100644
index 0000000..fefb754
--- /dev/null
+++ b/network/common.h
@@ -0,0 +1,42 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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
+ *
+ */
+
+#define PANU_UUID "00001115-0000-1000-8000-00805f9b34fb"
+#define NAP_UUID "00001116-0000-1000-8000-00805f9b34fb"
+#define GN_UUID "00001117-0000-1000-8000-00805f9b34fb"
+#define BNEP_SVC_UUID "0000000f-0000-1000-8000-00805f9b34fb"
+
+int bnep_init(void);
+int bnep_cleanup(void);
+
+uint16_t bnep_service_id(const char *svc);
+const char *bnep_uuid(uint16_t id);
+const char *bnep_name(uint16_t id);
+
+int bnep_kill_connection(bdaddr_t *dst);
+int bnep_kill_all_connections(void);
+
+int bnep_connadd(int sk, uint16_t role, char *dev);
+int bnep_if_up(const char *devname);
+int bnep_if_down(const char *devname);
+int bnep_add_to_bridge(const char *devname, const char *bridge);
diff --git a/network/connection.c b/network/connection.c
new file mode 100644
index 0000000..f4dd74e
--- /dev/null
+++ b/network/connection.c
@@ -0,0 +1,622 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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 <netinet/in.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/bnep.h>
+#include <bluetooth/sdp.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "log.h"
+#include "glib-helper.h"
+#include "btio.h"
+#include "dbus-common.h"
+#include "adapter.h"
+#include "device.h"
+
+#include "error.h"
+#include "common.h"
+#include "connection.h"
+
+#define NETWORK_PEER_INTERFACE "org.bluez.Network"
+
+typedef enum {
+ CONNECTED,
+ CONNECTING,
+ DISCONNECTED
+} conn_state;
+
+struct network_peer {
+ bdaddr_t src;
+ bdaddr_t dst;
+ char *path; /* D-Bus path */
+ struct btd_device *device;
+ GSList *connections;
+};
+
+struct network_conn {
+ DBusMessage *msg;
+ char dev[16]; /* Interface name */
+ uint16_t id; /* Role: Service Class Identifier */
+ conn_state state;
+ GIOChannel *io;
+ guint watch; /* Disconnect watch */
+ guint dc_id;
+ struct network_peer *peer;
+};
+
+struct __service_16 {
+ uint16_t dst;
+ uint16_t src;
+} __attribute__ ((packed));
+
+static DBusConnection *connection = NULL;
+static GSList *peers = NULL;
+
+static struct network_peer *find_peer(GSList *list, const char *path)
+{
+ GSList *l;
+
+ for (l = list; l; l = l->next) {
+ struct network_peer *peer = l->data;
+
+ if (!strcmp(peer->path, path))
+ return peer;
+ }
+
+ return NULL;
+}
+
+static struct network_conn *find_connection(GSList *list, uint16_t id)
+{
+ GSList *l;
+
+ for (l = list; l; l = l->next) {
+ struct network_conn *nc = l->data;
+
+ if (nc->id == id)
+ return nc;
+ }
+
+ return NULL;
+}
+
+static gboolean bnep_watchdog_cb(GIOChannel *chan, GIOCondition cond,
+ gpointer data)
+{
+ struct network_conn *nc = data;
+
+ if (connection != NULL) {
+ gboolean connected = FALSE;
+ const char *property = "";
+ emit_property_changed(connection, nc->peer->path,
+ NETWORK_PEER_INTERFACE, "Connected",
+ DBUS_TYPE_BOOLEAN, &connected);
+ emit_property_changed(connection, nc->peer->path,
+ NETWORK_PEER_INTERFACE, "Interface",
+ DBUS_TYPE_STRING, &property);
+ emit_property_changed(connection, nc->peer->path,
+ NETWORK_PEER_INTERFACE, "UUID",
+ DBUS_TYPE_STRING, &property);
+ device_remove_disconnect_watch(nc->peer->device, nc->dc_id);
+ nc->dc_id = 0;
+ if (nc->watch) {
+ g_dbus_remove_watch(connection, nc->watch);
+ nc->watch = 0;
+ }
+ }
+
+ info("%s disconnected", nc->dev);
+
+ bnep_if_down(nc->dev);
+ nc->state = DISCONNECTED;
+ memset(nc->dev, 0, sizeof(nc->dev));
+ strcpy(nc->dev, "bnep%d");
+
+ return FALSE;
+}
+
+static void cancel_connection(struct network_conn *nc, const char *err_msg)
+{
+ DBusMessage *reply;
+
+ if (nc->watch) {
+ g_dbus_remove_watch(connection, nc->watch);
+ nc->watch = 0;
+ }
+
+ if (nc->msg && err_msg) {
+ reply = btd_error_failed(nc->msg, err_msg);
+ g_dbus_send_message(connection, reply);
+ }
+
+ g_io_channel_shutdown(nc->io, TRUE, NULL);
+ g_io_channel_unref(nc->io);
+ nc->io = NULL;
+
+ nc->state = DISCONNECTED;
+}
+
+static void connection_destroy(DBusConnection *conn, void *user_data)
+{
+ struct network_conn *nc = user_data;
+
+ if (nc->state == CONNECTED) {
+ bnep_if_down(nc->dev);
+ bnep_kill_connection(&nc->peer->dst);
+ } else if (nc->io)
+ cancel_connection(nc, NULL);
+}
+
+static void disconnect_cb(struct btd_device *device, gboolean removal,
+ void *user_data)
+{
+ struct network_conn *nc = user_data;
+
+ info("Network: disconnect %s", nc->peer->path);
+
+ connection_destroy(NULL, user_data);
+}
+
+static gboolean bnep_setup_cb(GIOChannel *chan, GIOCondition cond,
+ gpointer data)
+{
+ struct network_conn *nc = data;
+ struct bnep_control_rsp *rsp;
+ struct timeval timeo;
+ char pkt[BNEP_MTU];
+ ssize_t r;
+ int sk;
+ const char *pdev, *uuid;
+ gboolean connected;
+
+ if (cond & G_IO_NVAL)
+ return FALSE;
+
+ if (cond & (G_IO_HUP | G_IO_ERR)) {
+ error("Hangup or error on l2cap server socket");
+ goto failed;
+ }
+
+ sk = g_io_channel_unix_get_fd(chan);
+
+ memset(pkt, 0, BNEP_MTU);
+ r = read(sk, pkt, sizeof(pkt) -1);
+ if (r < 0) {
+ error("IO Channel read error");
+ goto failed;
+ }
+
+ if (r == 0) {
+ error("No packet received on l2cap socket");
+ goto failed;
+ }
+
+ errno = EPROTO;
+
+ if ((size_t) r < sizeof(*rsp)) {
+ error("Packet received is not bnep type");
+ goto failed;
+ }
+
+ rsp = (void *) pkt;
+ if (rsp->type != BNEP_CONTROL) {
+ error("Packet received is not bnep type");
+ goto failed;
+ }
+
+ if (rsp->ctrl != BNEP_SETUP_CONN_RSP)
+ return TRUE;
+
+ r = ntohs(rsp->resp);
+
+ if (r != BNEP_SUCCESS) {
+ error("bnep failed");
+ goto failed;
+ }
+
+ memset(&timeo, 0, sizeof(timeo));
+ timeo.tv_sec = 0;
+
+ setsockopt(sk, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo));
+
+ if (bnep_connadd(sk, BNEP_SVC_PANU, nc->dev)) {
+ error("%s could not be added", nc->dev);
+ goto failed;
+ }
+
+ bnep_if_up(nc->dev);
+ pdev = nc->dev;
+ uuid = bnep_uuid(nc->id);
+
+ g_dbus_send_reply(connection, nc->msg,
+ DBUS_TYPE_STRING, &pdev,
+ DBUS_TYPE_INVALID);
+
+ connected = TRUE;
+ emit_property_changed(connection, nc->peer->path,
+ NETWORK_PEER_INTERFACE, "Connected",
+ DBUS_TYPE_BOOLEAN, &connected);
+ emit_property_changed(connection, nc->peer->path,
+ NETWORK_PEER_INTERFACE, "Interface",
+ DBUS_TYPE_STRING, &pdev);
+ emit_property_changed(connection, nc->peer->path,
+ NETWORK_PEER_INTERFACE, "UUID",
+ DBUS_TYPE_STRING, &uuid);
+
+ nc->state = CONNECTED;
+ nc->dc_id = device_add_disconnect_watch(nc->peer->device, disconnect_cb,
+ nc, NULL);
+
+ info("%s connected", nc->dev);
+ /* Start watchdog */
+ g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ (GIOFunc) bnep_watchdog_cb, nc);
+ g_io_channel_unref(nc->io);
+ nc->io = NULL;
+
+ return FALSE;
+
+failed:
+ cancel_connection(nc, "bnep setup failed");
+
+ return FALSE;
+}
+
+static int bnep_connect(struct network_conn *nc)
+{
+ struct bnep_setup_conn_req *req;
+ struct __service_16 *s;
+ struct timeval timeo;
+ unsigned char pkt[BNEP_MTU];
+ int fd;
+
+ /* Send request */
+ req = (void *) pkt;
+ req->type = BNEP_CONTROL;
+ req->ctrl = BNEP_SETUP_CONN_REQ;
+ req->uuid_size = 2; /* 16bit UUID */
+ s = (void *) req->service;
+ s->dst = htons(nc->id);
+ s->src = htons(BNEP_SVC_PANU);
+
+ memset(&timeo, 0, sizeof(timeo));
+ timeo.tv_sec = 30;
+
+ fd = g_io_channel_unix_get_fd(nc->io);
+ setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo));
+
+ if (send(fd, pkt, sizeof(*req) + sizeof(*s), 0) < 0)
+ return -errno;
+
+ g_io_add_watch(nc->io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ (GIOFunc) bnep_setup_cb, nc);
+
+ return 0;
+}
+
+static void connect_cb(GIOChannel *chan, GError *err, gpointer data)
+{
+ struct network_conn *nc = data;
+ const char *err_msg;
+ int perr;
+
+ if (err) {
+ error("%s", err->message);
+ err_msg = err->message;
+ goto failed;
+ }
+
+ perr = bnep_connect(nc);
+ if (perr < 0) {
+ err_msg = strerror(-perr);
+ error("bnep connect(): %s (%d)", err_msg, -perr);
+ goto failed;
+ }
+
+ return;
+
+failed:
+ cancel_connection(nc, err_msg);
+}
+
+/* Connect and initiate BNEP session */
+static DBusMessage *connection_connect(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct network_peer *peer = data;
+ struct network_conn *nc;
+ const char *svc;
+ uint16_t id;
+ GError *err = NULL;
+
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &svc,
+ DBUS_TYPE_INVALID) == FALSE)
+ return NULL;
+
+ id = bnep_service_id(svc);
+ nc = find_connection(peer->connections, id);
+ if (!nc)
+ return btd_error_not_supported(msg);
+
+ if (nc->state != DISCONNECTED)
+ return btd_error_already_connected(msg);
+
+ nc->io = bt_io_connect(BT_IO_L2CAP, connect_cb, nc,
+ NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &peer->src,
+ BT_IO_OPT_DEST_BDADDR, &peer->dst,
+ BT_IO_OPT_PSM, BNEP_PSM,
+ BT_IO_OPT_OMTU, BNEP_MTU,
+ BT_IO_OPT_IMTU, BNEP_MTU,
+ BT_IO_OPT_INVALID);
+ if (!nc->io) {
+ DBusMessage *reply;
+ error("%s", err->message);
+ reply = btd_error_failed(msg, err->message);
+ g_error_free(err);
+ return reply;
+ }
+
+ nc->state = CONNECTING;
+ nc->msg = dbus_message_ref(msg);
+ nc->watch = g_dbus_add_disconnect_watch(conn,
+ dbus_message_get_sender(msg),
+ connection_destroy,
+ nc, NULL);
+
+ return NULL;
+}
+
+static DBusMessage *connection_cancel(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct network_conn *nc = data;
+ const char *owner = dbus_message_get_sender(nc->msg);
+ const char *caller = dbus_message_get_sender(msg);
+
+ if (!g_str_equal(owner, caller))
+ return btd_error_not_authorized(msg);
+
+ connection_destroy(conn, nc);
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *connection_disconnect(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct network_peer *peer = data;
+ GSList *l;
+
+ for (l = peer->connections; l; l = l->next) {
+ struct network_conn *nc = l->data;
+
+ if (nc->state == DISCONNECTED)
+ continue;
+
+ return connection_cancel(conn, msg, nc);
+ }
+
+ return btd_error_not_connected(msg);
+}
+
+static DBusMessage *connection_get_properties(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct network_peer *peer = data;
+ struct network_conn *nc = NULL;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ dbus_bool_t connected;
+ const char *property;
+ GSList *l;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ /* Connected */
+ for (l = peer->connections; l; l = l->next) {
+ struct network_conn *tmp = l->data;
+
+ if (tmp->state != CONNECTED)
+ continue;
+
+ nc = tmp;
+ break;
+ }
+
+ connected = nc ? TRUE : FALSE;
+ dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &connected);
+
+ /* Interface */
+ property = nc ? nc->dev : "";
+ dict_append_entry(&dict, "Interface", DBUS_TYPE_STRING, &property);
+
+ /* UUID */
+ property = nc ? bnep_uuid(nc->id) : "";
+ dict_append_entry(&dict, "UUID", DBUS_TYPE_STRING, &property);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static void connection_free(struct network_conn *nc)
+{
+ if (nc->dc_id)
+ device_remove_disconnect_watch(nc->peer->device, nc->dc_id);
+
+ connection_destroy(connection, nc);
+
+ g_free(nc);
+ nc = NULL;
+}
+
+static void peer_free(struct network_peer *peer)
+{
+ g_slist_foreach(peer->connections, (GFunc) connection_free, NULL);
+ g_slist_free(peer->connections);
+ btd_device_unref(peer->device);
+ g_free(peer->path);
+ g_free(peer);
+}
+
+static void path_unregister(void *data)
+{
+ struct network_peer *peer = data;
+
+ DBG("Unregistered interface %s on path %s",
+ NETWORK_PEER_INTERFACE, peer->path);
+
+ peers = g_slist_remove(peers, peer);
+ peer_free(peer);
+}
+
+static GDBusMethodTable connection_methods[] = {
+ { "Connect", "s", "s", connection_connect,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "Disconnect", "", "", connection_disconnect },
+ { "GetProperties", "", "a{sv}",connection_get_properties },
+ { }
+};
+
+static GDBusSignalTable connection_signals[] = {
+ { "PropertyChanged", "sv" },
+ { }
+};
+
+void connection_unregister(const char *path, uint16_t id)
+{
+ struct network_peer *peer;
+ struct network_conn *nc;
+
+ peer = find_peer(peers, path);
+ if (!peer)
+ return;
+
+ nc = find_connection(peer->connections, id);
+ if (!nc)
+ return;
+
+ peer->connections = g_slist_remove(peer->connections, nc);
+ connection_free(nc);
+ if (peer->connections)
+ return;
+
+ g_dbus_unregister_interface(connection, path, NETWORK_PEER_INTERFACE);
+}
+
+static struct network_peer *create_peer(struct btd_device *device,
+ const char *path, bdaddr_t *src,
+ bdaddr_t *dst)
+{
+ struct network_peer *peer;
+
+ peer = g_new0(struct network_peer, 1);
+ peer->device = btd_device_ref(device);
+ peer->path = g_strdup(path);
+ bacpy(&peer->src, src);
+ bacpy(&peer->dst, dst);
+
+ if (g_dbus_register_interface(connection, path,
+ NETWORK_PEER_INTERFACE,
+ connection_methods,
+ connection_signals, NULL,
+ peer, path_unregister) == FALSE) {
+ error("D-Bus failed to register %s interface",
+ NETWORK_PEER_INTERFACE);
+ peer_free(peer);
+ return NULL;
+ }
+
+ DBG("Registered interface %s on path %s",
+ NETWORK_PEER_INTERFACE, path);
+
+ return peer;
+}
+
+int connection_register(struct btd_device *device, const char *path,
+ bdaddr_t *src, bdaddr_t *dst, uint16_t id)
+{
+ struct network_peer *peer;
+ struct network_conn *nc;
+
+ if (!path)
+ return -EINVAL;
+
+ peer = find_peer(peers, path);
+ if (!peer) {
+ peer = create_peer(device, path, src, dst);
+ if (!peer)
+ return -1;
+ peers = g_slist_append(peers, peer);
+ }
+
+ nc = find_connection(peer->connections, id);
+ if (nc)
+ return 0;
+
+ nc = g_new0(struct network_conn, 1);
+ nc->id = id;
+ memset(nc->dev, 0, sizeof(nc->dev));
+ strcpy(nc->dev, "bnep%d");
+ nc->state = DISCONNECTED;
+ nc->peer = peer;
+
+ peer->connections = g_slist_append(peer->connections, nc);
+
+ return 0;
+}
+
+int connection_init(DBusConnection *conn)
+{
+ connection = dbus_connection_ref(conn);
+
+ return 0;
+}
+
+void connection_exit(void)
+{
+ dbus_connection_unref(connection);
+ connection = NULL;
+}
diff --git a/network/connection.h b/network/connection.h
new file mode 100644
index 0000000..5ea4147
--- /dev/null
+++ b/network/connection.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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
+ *
+ */
+
+int connection_init(DBusConnection *conn);
+void connection_exit(void);
+int connection_register(struct btd_device *device, const char *path,
+ bdaddr_t *src, bdaddr_t *dst, uint16_t id);
+void connection_unregister(const char *path, uint16_t id);
diff --git a/network/main.c b/network/main.c
new file mode 100644
index 0000000..88e77ee
--- /dev/null
+++ b/network/main.c
@@ -0,0 +1,59 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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 <errno.h>
+
+#include <gdbus.h>
+
+#include "plugin.h"
+#include "manager.h"
+
+static DBusConnection *connection;
+
+static int network_init(void)
+{
+ connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+ if (connection == NULL)
+ return -EIO;
+
+ if (network_manager_init(connection) < 0) {
+ dbus_connection_unref(connection);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void network_exit(void)
+{
+ network_manager_exit();
+
+ dbus_connection_unref(connection);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(network, VERSION,
+ BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, network_init, network_exit)
diff --git a/network/manager.c b/network/manager.c
new file mode 100644
index 0000000..321640b
--- /dev/null
+++ b/network/manager.c
@@ -0,0 +1,222 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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 <bluetooth/bluetooth.h>
+#include <bluetooth/bnep.h>
+#include <bluetooth/sdp.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "log.h"
+
+#include "adapter.h"
+#include "device.h"
+#include "manager.h"
+#include "common.h"
+#include "connection.h"
+#include "server.h"
+
+static DBusConnection *connection = NULL;
+
+static gboolean conf_security = TRUE;
+
+static void read_config(const char *file)
+{
+ GKeyFile *keyfile;
+ GError *err = NULL;
+
+ keyfile = g_key_file_new();
+
+ if (!g_key_file_load_from_file(keyfile, file, 0, &err)) {
+ g_clear_error(&err);
+ goto done;
+ }
+
+ conf_security = !g_key_file_get_boolean(keyfile, "General",
+ "DisableSecurity", &err);
+ if (err) {
+ DBG("%s: %s", file, err->message);
+ g_clear_error(&err);
+ }
+
+done:
+ g_key_file_free(keyfile);
+
+ DBG("Config options: Security=%s",
+ conf_security ? "true" : "false");
+}
+
+static int network_probe(struct btd_device *device, GSList *uuids, uint16_t id)
+{
+ struct btd_adapter *adapter = device_get_adapter(device);
+ const gchar *path = device_get_path(device);
+ bdaddr_t src, dst;
+
+ DBG("path %s", path);
+
+ adapter_get_address(adapter, &src);
+ device_get_address(device, &dst);
+
+ return connection_register(device, path, &src, &dst, id);
+}
+
+static void network_remove(struct btd_device *device, uint16_t id)
+{
+ const gchar *path = device_get_path(device);
+
+ DBG("path %s", path);
+
+ connection_unregister(path, id);
+}
+
+static int panu_probe(struct btd_device *device, GSList *uuids)
+{
+ return network_probe(device, uuids, BNEP_SVC_PANU);
+}
+
+static void panu_remove(struct btd_device *device)
+{
+ network_remove(device, BNEP_SVC_PANU);
+}
+
+static int gn_probe(struct btd_device *device, GSList *uuids)
+{
+ return network_probe(device, uuids, BNEP_SVC_GN);
+}
+
+static void gn_remove(struct btd_device *device)
+{
+ network_remove(device, BNEP_SVC_GN);
+}
+
+static int nap_probe(struct btd_device *device, GSList *uuids)
+{
+ return network_probe(device, uuids, BNEP_SVC_NAP);
+}
+
+static void nap_remove(struct btd_device *device)
+{
+ network_remove(device, BNEP_SVC_NAP);
+}
+
+static int network_server_probe(struct btd_adapter *adapter)
+{
+ const gchar *path = adapter_get_path(adapter);
+
+ DBG("path %s", path);
+
+ return server_register(adapter);
+}
+
+static void network_server_remove(struct btd_adapter *adapter)
+{
+ const gchar *path = adapter_get_path(adapter);
+
+ DBG("path %s", path);
+
+ server_unregister(adapter);
+}
+
+static struct btd_device_driver network_panu_driver = {
+ .name = "network-panu",
+ .uuids = BTD_UUIDS(PANU_UUID),
+ .probe = panu_probe,
+ .remove = panu_remove,
+};
+
+static struct btd_device_driver network_gn_driver = {
+ .name = "network-gn",
+ .uuids = BTD_UUIDS(GN_UUID),
+ .probe = gn_probe,
+ .remove = gn_remove,
+};
+
+static struct btd_device_driver network_nap_driver = {
+ .name = "network-nap",
+ .uuids = BTD_UUIDS(NAP_UUID),
+ .probe = nap_probe,
+ .remove = nap_remove,
+};
+
+static struct btd_adapter_driver network_server_driver = {
+ .name = "network-server",
+ .probe = network_server_probe,
+ .remove = network_server_remove,
+};
+
+int network_manager_init(DBusConnection *conn)
+{
+ read_config(CONFIGDIR "/network.conf");
+
+ if (bnep_init()) {
+ error("Can't init bnep module");
+ return -1;
+ }
+
+ /*
+ * There is one socket to handle the incomming connections. NAP,
+ * GN and PANU servers share the same PSM. The initial BNEP message
+ * (setup connection request) contains the destination service
+ * field that defines which service the source is connecting to.
+ */
+
+ if (server_init(conn, conf_security) < 0)
+ return -1;
+
+ /* Register network server if it doesn't exist */
+ btd_register_adapter_driver(&network_server_driver);
+
+ if (connection_init(conn) < 0)
+ return -1;
+
+ btd_register_device_driver(&network_panu_driver);
+ btd_register_device_driver(&network_gn_driver);
+ btd_register_device_driver(&network_nap_driver);
+
+ connection = dbus_connection_ref(conn);
+
+ return 0;
+}
+
+void network_manager_exit(void)
+{
+ server_exit();
+
+ btd_unregister_device_driver(&network_panu_driver);
+ btd_unregister_device_driver(&network_gn_driver);
+ btd_unregister_device_driver(&network_nap_driver);
+
+ connection_exit();
+
+ btd_unregister_adapter_driver(&network_server_driver);
+
+ dbus_connection_unref(connection);
+ connection = NULL;
+
+ bnep_cleanup();
+}
diff --git a/network/manager.h b/network/manager.h
new file mode 100644
index 0000000..27bc13f
--- /dev/null
+++ b/network/manager.h
@@ -0,0 +1,25 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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
+ *
+ */
+
+int network_manager_init(DBusConnection *conn);
+void network_manager_exit(void);
diff --git a/network/network.conf b/network/network.conf
new file mode 100644
index 0000000..5f11639
--- /dev/null
+++ b/network/network.conf
@@ -0,0 +1,6 @@
+# Configuration file for the network service
+
+[General]
+
+# Disable link encryption: default=false
+#DisableSecurity=true
diff --git a/network/server.c b/network/server.c
new file mode 100644
index 0000000..d1da8a9
--- /dev/null
+++ b/network/server.c
@@ -0,0 +1,809 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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 <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/bnep.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+#include <netinet/in.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "../src/dbus-common.h"
+#include "../src/adapter.h"
+
+#include "log.h"
+#include "error.h"
+#include "sdpd.h"
+#include "btio.h"
+#include "glib-helper.h"
+
+#include "common.h"
+#include "server.h"
+
+#define NETWORK_SERVER_INTERFACE "org.bluez.NetworkServer"
+#define SETUP_TIMEOUT 1
+
+/* Pending Authorization */
+struct network_session {
+ bdaddr_t dst; /* Remote Bluetooth Address */
+ GIOChannel *io; /* Pending connect channel */
+ guint watch; /* BNEP socket watch */
+};
+
+struct network_adapter {
+ struct btd_adapter *adapter; /* Adapter pointer */
+ GIOChannel *io; /* Bnep socket */
+ struct network_session *setup; /* Setup in progress */
+ GSList *servers; /* Server register to adapter */
+};
+
+/* Main server structure */
+struct network_server {
+ bdaddr_t src; /* Bluetooth Local Address */
+ char *iface; /* DBus interface */
+ char *name; /* Server service name */
+ char *bridge; /* Bridge name */
+ uint32_t record_id; /* Service record id */
+ uint16_t id; /* Service class identifier */
+ GSList *sessions; /* Active connections */
+ struct network_adapter *na; /* Adapter reference */
+ guint watch_id; /* Client service watch */
+};
+
+static DBusConnection *connection = NULL;
+static GSList *adapters = NULL;
+static gboolean security = TRUE;
+
+static struct network_adapter *find_adapter(GSList *list,
+ struct btd_adapter *adapter)
+{
+ GSList *l;
+
+ for (l = list; l; l = l->next) {
+ struct network_adapter *na = l->data;
+
+ if (na->adapter == adapter)
+ return na;
+ }
+
+ return NULL;
+}
+
+static struct network_server *find_server(GSList *list, uint16_t id)
+{
+ GSList *l;
+
+ for (l = list; l; l = l->next) {
+ struct network_server *ns = l->data;
+
+ if (ns->id == id)
+ return ns;
+ }
+
+ return NULL;
+}
+
+static void add_lang_attr(sdp_record_t *r)
+{
+ sdp_lang_attr_t base_lang;
+ sdp_list_t *langs = 0;
+
+ /* UTF-8 MIBenum (http://www.iana.org/assignments/character-sets) */
+ base_lang.code_ISO639 = (0x65 << 8) | 0x6e;
+ base_lang.encoding = 106;
+ base_lang.base_offset = SDP_PRIMARY_LANG_BASE;
+ langs = sdp_list_append(0, &base_lang);
+ sdp_set_lang_attr(r, langs);
+ sdp_list_free(langs, 0);
+}
+
+static sdp_record_t *server_record_new(const char *name, uint16_t id)
+{
+ sdp_list_t *svclass, *pfseq, *apseq, *root, *aproto;
+ uuid_t root_uuid, pan, l2cap, bnep;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *proto[2];
+ sdp_data_t *v, *p;
+ uint16_t psm = BNEP_PSM, version = 0x0100;
+ uint16_t security_desc = (security ? 0x0001 : 0x0000);
+ uint16_t net_access_type = 0xfffe;
+ uint32_t max_net_access_rate = 0;
+ const char *desc = "Network service";
+ sdp_record_t *record;
+
+ record = sdp_record_alloc();
+ if (!record)
+ return NULL;
+
+ record->attrlist = NULL;
+ record->pattern = NULL;
+
+ switch (id) {
+ case BNEP_SVC_NAP:
+ sdp_uuid16_create(&pan, NAP_SVCLASS_ID);
+ svclass = sdp_list_append(NULL, &pan);
+ sdp_set_service_classes(record, svclass);
+
+ sdp_uuid16_create(&profile[0].uuid, NAP_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(NULL, &profile[0]);
+ sdp_set_profile_descs(record, pfseq);
+
+ sdp_set_info_attr(record, name, NULL, desc);
+
+ sdp_attr_add_new(record, SDP_ATTR_NET_ACCESS_TYPE,
+ SDP_UINT16, &net_access_type);
+ sdp_attr_add_new(record, SDP_ATTR_MAX_NET_ACCESSRATE,
+ SDP_UINT32, &max_net_access_rate);
+ break;
+ case BNEP_SVC_GN:
+ sdp_uuid16_create(&pan, GN_SVCLASS_ID);
+ svclass = sdp_list_append(NULL, &pan);
+ sdp_set_service_classes(record, svclass);
+
+ sdp_uuid16_create(&profile[0].uuid, GN_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(NULL, &profile[0]);
+ sdp_set_profile_descs(record, pfseq);
+
+ sdp_set_info_attr(record, name, NULL, desc);
+ break;
+ case BNEP_SVC_PANU:
+ sdp_uuid16_create(&pan, PANU_SVCLASS_ID);
+ svclass = sdp_list_append(NULL, &pan);
+ sdp_set_service_classes(record, svclass);
+
+ sdp_uuid16_create(&profile[0].uuid, PANU_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(NULL, &profile[0]);
+ sdp_set_profile_descs(record, pfseq);
+
+ sdp_set_info_attr(record, name, NULL, desc);
+ break;
+ default:
+ sdp_record_free(record);
+ return NULL;
+ }
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(record, root);
+
+ sdp_uuid16_create(&l2cap, L2CAP_UUID);
+ proto[0] = sdp_list_append(NULL, &l2cap);
+ p = sdp_data_alloc(SDP_UINT16, &psm);
+ proto[0] = sdp_list_append(proto[0], p);
+ apseq = sdp_list_append(NULL, proto[0]);
+
+ sdp_uuid16_create(&bnep, BNEP_UUID);
+ proto[1] = sdp_list_append(NULL, &bnep);
+ v = sdp_data_alloc(SDP_UINT16, &version);
+ proto[1] = sdp_list_append(proto[1], v);
+
+ /* Supported protocols */
+ {
+ uint16_t ptype[] = {
+ 0x0800, /* IPv4 */
+ 0x0806, /* ARP */
+ };
+ sdp_data_t *head, *pseq;
+ int p;
+
+ for (p = 0, head = NULL; p < 2; p++) {
+ sdp_data_t *data = sdp_data_alloc(SDP_UINT16, &ptype[p]);
+ if (head)
+ sdp_seq_append(head, data);
+ else
+ head = data;
+ }
+ pseq = sdp_data_alloc(SDP_SEQ16, head);
+ proto[1] = sdp_list_append(proto[1], pseq);
+ }
+
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(NULL, apseq);
+ sdp_set_access_protos(record, aproto);
+
+ add_lang_attr(record);
+
+ sdp_attr_add_new(record, SDP_ATTR_SECURITY_DESC,
+ SDP_UINT16, &security_desc);
+
+ sdp_data_free(p);
+ sdp_data_free(v);
+ sdp_list_free(apseq, NULL);
+ sdp_list_free(root, NULL);
+ sdp_list_free(aproto, NULL);
+ sdp_list_free(proto[0], NULL);
+ sdp_list_free(proto[1], NULL);
+ sdp_list_free(svclass, NULL);
+ sdp_list_free(pfseq, NULL);
+
+ return record;
+}
+
+static ssize_t send_bnep_ctrl_rsp(int sk, uint16_t val)
+{
+ struct bnep_control_rsp rsp;
+
+ rsp.type = BNEP_CONTROL;
+ rsp.ctrl = BNEP_SETUP_CONN_RSP;
+ rsp.resp = htons(val);
+
+ return send(sk, &rsp, sizeof(rsp), 0);
+}
+
+static int server_connadd(struct network_server *ns,
+ struct network_session *session,
+ uint16_t dst_role)
+{
+ char devname[16];
+ int err, nsk;
+
+ memset(devname, 0, sizeof(devname));
+ strcpy(devname, "bnep%d");
+
+ nsk = g_io_channel_unix_get_fd(session->io);
+ err = bnep_connadd(nsk, dst_role, devname);
+ if (err < 0)
+ return err;
+
+ info("Added new connection: %s", devname);
+
+ if (bnep_add_to_bridge(devname, ns->bridge) < 0) {
+ error("Can't add %s to the bridge %s: %s(%d)",
+ devname, ns->bridge, strerror(errno), errno);
+ return -EPERM;
+ }
+
+ bnep_if_up(devname);
+
+ ns->sessions = g_slist_append(ns->sessions, session);
+
+ return 0;
+}
+
+static uint16_t bnep_setup_chk(uint16_t dst_role, uint16_t src_role)
+{
+ /* Allowed PAN Profile scenarios */
+ switch (dst_role) {
+ case BNEP_SVC_NAP:
+ case BNEP_SVC_GN:
+ if (src_role == BNEP_SVC_PANU)
+ return 0;
+ return BNEP_CONN_INVALID_SRC;
+ case BNEP_SVC_PANU:
+ if (src_role == BNEP_SVC_PANU ||
+ src_role == BNEP_SVC_GN ||
+ src_role == BNEP_SVC_NAP)
+ return 0;
+
+ return BNEP_CONN_INVALID_SRC;
+ }
+
+ return BNEP_CONN_INVALID_DST;
+}
+
+static uint16_t bnep_setup_decode(struct bnep_setup_conn_req *req,
+ uint16_t *dst_role, uint16_t *src_role)
+{
+ uint8_t *dest, *source;
+
+ dest = req->service;
+ source = req->service + req->uuid_size;
+
+ switch (req->uuid_size) {
+ case 2: /* UUID16 */
+ *dst_role = ntohs(bt_get_unaligned((uint16_t *) dest));
+ *src_role = ntohs(bt_get_unaligned((uint16_t *) source));
+ break;
+ case 4: /* UUID32 */
+ case 16: /* UUID128 */
+ *dst_role = ntohl(bt_get_unaligned((uint32_t *) dest));
+ *src_role = ntohl(bt_get_unaligned((uint32_t *) source));
+ break;
+ default:
+ return BNEP_CONN_INVALID_SVC;
+ }
+
+ return 0;
+}
+
+static void session_free(void *data)
+{
+ struct network_session *session = data;
+
+ if (session->watch)
+ g_source_remove(session->watch);
+
+ if (session->io)
+ g_io_channel_unref(session->io);
+
+ g_free(session);
+}
+
+static void setup_destroy(void *user_data)
+{
+ struct network_adapter *na = user_data;
+ struct network_session *setup = na->setup;
+
+ if (!setup)
+ return;
+
+ na->setup = NULL;
+
+ session_free(setup);
+}
+
+static gboolean bnep_setup(GIOChannel *chan,
+ GIOCondition cond, gpointer user_data)
+{
+ struct network_adapter *na = user_data;
+ struct network_server *ns;
+ uint8_t packet[BNEP_MTU];
+ struct bnep_setup_conn_req *req = (void *) packet;
+ uint16_t src_role, dst_role, rsp = BNEP_CONN_NOT_ALLOWED;
+ int n, sk;
+
+ if (cond & G_IO_NVAL)
+ return FALSE;
+
+ if (cond & (G_IO_ERR | G_IO_HUP)) {
+ error("Hangup or error on BNEP socket");
+ return FALSE;
+ }
+
+ sk = g_io_channel_unix_get_fd(chan);
+
+ /* Reading BNEP_SETUP_CONNECTION_REQUEST_MSG */
+ n = read(sk, packet, sizeof(packet));
+ if (n < 0) {
+ error("read(): %s(%d)", strerror(errno), errno);
+ return FALSE;
+ }
+
+ /* Highest known Control command ID
+ * is BNEP_FILTER_MULT_ADDR_RSP = 0x06 */
+ if (req->type == BNEP_CONTROL &&
+ req->ctrl > BNEP_FILTER_MULT_ADDR_RSP) {
+ uint8_t pkt[3];
+
+ pkt[0] = BNEP_CONTROL;
+ pkt[1] = BNEP_CMD_NOT_UNDERSTOOD;
+ pkt[2] = req->ctrl;
+
+ send(sk, pkt, sizeof(pkt), 0);
+
+ return FALSE;
+ }
+
+ if (req->type != BNEP_CONTROL || req->ctrl != BNEP_SETUP_CONN_REQ)
+ return FALSE;
+
+ rsp = bnep_setup_decode(req, &dst_role, &src_role);
+ if (rsp)
+ goto reply;
+
+ rsp = bnep_setup_chk(dst_role, src_role);
+ if (rsp)
+ goto reply;
+
+ ns = find_server(na->servers, dst_role);
+ if (!ns) {
+ error("Server unavailable: (0x%x)", dst_role);
+ goto reply;
+ }
+
+ if (!ns->record_id) {
+ error("Service record not available");
+ goto reply;
+ }
+
+ if (!ns->bridge) {
+ error("Bridge interface not configured");
+ goto reply;
+ }
+
+ if (server_connadd(ns, na->setup, dst_role) < 0)
+ goto reply;
+
+ na->setup = NULL;
+
+ rsp = BNEP_SUCCESS;
+
+reply:
+ send_bnep_ctrl_rsp(sk, rsp);
+
+ return FALSE;
+}
+
+static void connect_event(GIOChannel *chan, GError *err, gpointer user_data)
+{
+ struct network_adapter *na = user_data;
+
+ if (err) {
+ error("%s", err->message);
+ setup_destroy(na);
+ return;
+ }
+
+ g_io_channel_set_close_on_unref(chan, TRUE);
+
+ na->setup->watch = g_io_add_watch_full(chan, G_PRIORITY_DEFAULT,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ bnep_setup, na, setup_destroy);
+}
+
+static void auth_cb(DBusError *derr, void *user_data)
+{
+ struct network_adapter *na = user_data;
+ GError *err = NULL;
+
+ if (derr) {
+ error("Access denied: %s", derr->message);
+ goto reject;
+ }
+
+ if (!bt_io_accept(na->setup->io, connect_event, na, NULL,
+ &err)) {
+ error("bt_io_accept: %s", err->message);
+ g_error_free(err);
+ goto reject;
+ }
+
+ return;
+
+reject:
+ g_io_channel_shutdown(na->setup->io, TRUE, NULL);
+ setup_destroy(na);
+}
+
+static void confirm_event(GIOChannel *chan, gpointer user_data)
+{
+ struct network_adapter *na = user_data;
+ struct network_server *ns;
+ int perr;
+ bdaddr_t src, dst;
+ char address[18];
+ GError *err = NULL;
+
+ bt_io_get(chan, BT_IO_L2CAP, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &src,
+ BT_IO_OPT_DEST_BDADDR, &dst,
+ BT_IO_OPT_DEST, address,
+ BT_IO_OPT_INVALID);
+ if (err) {
+ error("%s", err->message);
+ g_error_free(err);
+ goto drop;
+ }
+
+ DBG("BNEP: incoming connect from %s", address);
+
+ if (na->setup) {
+ error("Refusing connect from %s: setup in progress", address);
+ goto drop;
+ }
+
+ ns = find_server(na->servers, BNEP_SVC_NAP);
+ if (!ns)
+ goto drop;
+
+ if (!ns->record_id)
+ goto drop;
+
+ if (!ns->bridge)
+ goto drop;
+
+ na->setup = g_new0(struct network_session, 1);
+ bacpy(&na->setup->dst, &dst);
+ na->setup->io = g_io_channel_ref(chan);
+
+ perr = btd_request_authorization(&src, &dst, BNEP_SVC_UUID,
+ auth_cb, na);
+ if (perr < 0) {
+ error("Refusing connect from %s: %s (%d)", address,
+ strerror(-perr), -perr);
+ setup_destroy(na);
+ goto drop;
+ }
+
+ return;
+
+drop:
+ g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+int server_init(DBusConnection *conn, gboolean secure)
+{
+ security = secure;
+ connection = dbus_connection_ref(conn);
+
+ return 0;
+}
+
+void server_exit(void)
+{
+ dbus_connection_unref(connection);
+ connection = NULL;
+}
+
+static uint32_t register_server_record(struct network_server *ns)
+{
+ sdp_record_t *record;
+
+ record = server_record_new(ns->name, ns->id);
+ if (!record) {
+ error("Unable to allocate new service record");
+ return 0;
+ }
+
+ if (add_record_to_server(&ns->src, record) < 0) {
+ error("Failed to register service record");
+ sdp_record_free(record);
+ return 0;
+ }
+
+ DBG("got record id 0x%x", record->handle);
+
+ return record->handle;
+}
+
+static void server_disconnect(DBusConnection *conn, void *user_data)
+{
+ struct network_server *ns = user_data;
+
+ ns->watch_id = 0;
+
+ if (ns->record_id) {
+ remove_record_from_server(ns->record_id);
+ ns->record_id = 0;
+ }
+
+ g_free(ns->bridge);
+ ns->bridge = NULL;
+}
+
+static DBusMessage *register_server(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct network_server *ns = data;
+ DBusMessage *reply;
+ const char *uuid, *bridge;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &uuid,
+ DBUS_TYPE_STRING, &bridge, DBUS_TYPE_INVALID))
+ return NULL;
+
+ if (g_strcmp0(uuid, "nap"))
+ return btd_error_failed(msg, "Invalid UUID");
+
+ if (ns->record_id)
+ return btd_error_already_exists(msg);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ ns->record_id = register_server_record(ns);
+ if (!ns->record_id)
+ return btd_error_failed(msg, "SDP record registration failed");
+
+ g_free(ns->bridge);
+ ns->bridge = g_strdup(bridge);
+
+ ns->watch_id = g_dbus_add_disconnect_watch(conn,
+ dbus_message_get_sender(msg),
+ server_disconnect, ns, NULL);
+
+ return reply;
+}
+
+static DBusMessage *unregister_server(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct network_server *ns = data;
+ DBusMessage *reply;
+ const char *uuid;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &uuid,
+ DBUS_TYPE_INVALID))
+ return NULL;
+
+ if (g_strcmp0(uuid, "nap"))
+ return btd_error_failed(msg, "Invalid UUID");
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ g_dbus_remove_watch(conn, ns->watch_id);
+
+ server_disconnect(conn, ns);
+
+ return reply;
+}
+
+static void adapter_free(struct network_adapter *na)
+{
+ if (na->io != NULL) {
+ g_io_channel_shutdown(na->io, TRUE, NULL);
+ g_io_channel_unref(na->io);
+ }
+
+ setup_destroy(na);
+ btd_adapter_unref(na->adapter);
+ g_free(na);
+}
+
+static void server_free(struct network_server *ns)
+{
+ if (!ns)
+ return;
+
+ /* FIXME: Missing release/free all bnepX interfaces */
+ if (ns->record_id)
+ remove_record_from_server(ns->record_id);
+
+ g_free(ns->iface);
+ g_free(ns->name);
+ g_free(ns->bridge);
+
+ if (ns->sessions) {
+ g_slist_foreach(ns->sessions, (GFunc) session_free, NULL);
+ g_slist_free(ns->sessions);
+ }
+
+ g_free(ns);
+}
+
+static void path_unregister(void *data)
+{
+ struct network_server *ns = data;
+ struct network_adapter *na = ns->na;
+
+ DBG("Unregistered interface %s on path %s",
+ ns->iface, adapter_get_path(na->adapter));
+
+ na->servers = g_slist_remove(na->servers, ns);
+ server_free(ns);
+
+ if (na->servers)
+ return;
+
+ adapters = g_slist_remove(adapters, na);
+ adapter_free(na);
+}
+
+static GDBusMethodTable server_methods[] = {
+ { "Register", "ss", "", register_server },
+ { "Unregister", "s", "", unregister_server },
+ { }
+};
+
+static struct network_adapter *create_adapter(struct btd_adapter *adapter)
+{
+ struct network_adapter *na;
+ GError *err = NULL;
+ bdaddr_t src;
+
+ na = g_new0(struct network_adapter, 1);
+ na->adapter = btd_adapter_ref(adapter);
+
+ adapter_get_address(adapter, &src);
+
+ na->io = bt_io_listen(BT_IO_L2CAP, NULL, confirm_event, na,
+ NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &src,
+ BT_IO_OPT_PSM, BNEP_PSM,
+ BT_IO_OPT_OMTU, BNEP_MTU,
+ BT_IO_OPT_IMTU, BNEP_MTU,
+ BT_IO_OPT_SEC_LEVEL,
+ security ? BT_IO_SEC_MEDIUM : BT_IO_SEC_LOW,
+ BT_IO_OPT_INVALID);
+ if (!na->io) {
+ error("%s", err->message);
+ g_error_free(err);
+ adapter_free(na);
+ return NULL;
+ }
+
+ return na;
+}
+
+int server_register(struct btd_adapter *adapter)
+{
+ struct network_adapter *na;
+ struct network_server *ns;
+ const char *path;
+
+ na = find_adapter(adapters, adapter);
+ if (!na) {
+ na = create_adapter(adapter);
+ if (!na)
+ return -EINVAL;
+ adapters = g_slist_append(adapters, na);
+ }
+
+ ns = find_server(na->servers, BNEP_SVC_NAP);
+ if (ns)
+ return 0;
+
+ ns = g_new0(struct network_server, 1);
+
+ ns->iface = g_strdup(NETWORK_SERVER_INTERFACE);
+ ns->name = g_strdup("Network service");
+
+ path = adapter_get_path(adapter);
+
+ if (!g_dbus_register_interface(connection, path, ns->iface,
+ server_methods, NULL, NULL,
+ ns, path_unregister)) {
+ error("D-Bus failed to register %s interface",
+ ns->iface);
+ server_free(ns);
+ return -1;
+ }
+
+ adapter_get_address(adapter, &ns->src);
+ ns->id = BNEP_SVC_NAP;
+ ns->na = na;
+ ns->record_id = 0;
+ na->servers = g_slist_append(na->servers, ns);
+
+ DBG("Registered interface %s on path %s", ns->iface, path);
+
+ return 0;
+}
+
+int server_unregister(struct btd_adapter *adapter)
+{
+ struct network_adapter *na;
+ struct network_server *ns;
+ uint16_t id = BNEP_SVC_NAP;
+
+ na = find_adapter(adapters, adapter);
+ if (!na)
+ return -EINVAL;
+
+ ns = find_server(na->servers, id);
+ if (!ns)
+ return -EINVAL;
+
+ g_dbus_unregister_interface(connection, adapter_get_path(adapter),
+ ns->iface);
+
+ return 0;
+}
diff --git a/network/server.h b/network/server.h
new file mode 100644
index 0000000..d88acab
--- /dev/null
+++ b/network/server.h
@@ -0,0 +1,29 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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
+ *
+ */
+
+int server_init(DBusConnection *conn, gboolean secure);
+void server_exit(void);
+int server_register(struct btd_adapter *adapter);
+int server_unregister(struct btd_adapter *adapter);
+
+int server_find_data(const char *path, const char *pattern);
diff --git a/plugins/dbusoob.c b/plugins/dbusoob.c
new file mode 100644
index 0000000..2c03780
--- /dev/null
+++ b/plugins/dbusoob.c
@@ -0,0 +1,234 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 ST-Ericsson SA
+ *
+ * Author: Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson
+ *
+ *
+ * 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 <errno.h>
+#include <gdbus.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/sdp.h>
+
+#include "plugin.h"
+#include "log.h"
+#include "adapter.h"
+#include "device.h"
+#include "manager.h"
+#include "dbus-common.h"
+#include "event.h"
+#include "error.h"
+#include "oob.h"
+
+#define OOB_INTERFACE "org.bluez.OutOfBand"
+
+struct oob_request {
+ struct btd_adapter *adapter;
+ DBusMessage *msg;
+};
+
+static GSList *oob_requests = NULL;
+static DBusConnection *connection = NULL;
+
+static gint oob_request_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct oob_request *data = a;
+ const struct btd_adapter *adapter = b;
+
+ return data->adapter != adapter;
+}
+
+static struct oob_request *find_oob_request(struct btd_adapter *adapter)
+{
+ GSList *match;
+
+ match = g_slist_find_custom(oob_requests, adapter, oob_request_cmp);
+
+ if (match)
+ return match->data;
+
+ return NULL;
+}
+
+static void read_local_data_complete(struct btd_adapter *adapter, uint8_t *hash,
+ uint8_t *randomizer)
+{
+ struct DBusMessage *reply;
+ struct oob_request *oob_request;
+
+ oob_request = find_oob_request(adapter);
+ if (!oob_request)
+ return;
+
+ if (hash && randomizer)
+ reply = g_dbus_create_reply(oob_request->msg,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &hash, 16,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &randomizer, 16,
+ DBUS_TYPE_INVALID);
+ else
+ reply = btd_error_failed(oob_request->msg,
+ "Failed to read local OOB data.");
+
+ oob_requests = g_slist_remove(oob_requests, oob_request);
+ dbus_message_unref(oob_request->msg);
+ g_free(oob_request);
+
+ if (!reply) {
+ error("Couldn't allocate D-Bus message");
+ return;
+ }
+
+ if (!g_dbus_send_message(connection, reply))
+ error("D-Bus send failed");
+}
+
+static DBusMessage *read_local_data(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct btd_adapter *adapter = data;
+ struct oob_request *oob_request;
+
+ if (find_oob_request(adapter))
+ return btd_error_in_progress(msg);
+
+ if (btd_adapter_read_local_oob_data(adapter))
+ return btd_error_failed(msg, "Request failed.");
+
+ oob_request = g_new(struct oob_request, 1);
+ oob_request->adapter = adapter;
+ oob_requests = g_slist_append(oob_requests, oob_request);
+ oob_request->msg = dbus_message_ref(msg);
+
+ return NULL;
+}
+
+static DBusMessage *add_remote_data(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct btd_adapter *adapter = data;
+ uint8_t *hash, *randomizer;
+ int32_t hlen, rlen;
+ const char *addr;
+ bdaddr_t bdaddr;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &addr,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &hash, &hlen,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &randomizer, &rlen,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ if (hlen != 16 || rlen != 16 || bachk(addr))
+ return btd_error_invalid_args(msg);
+
+ str2ba(addr, &bdaddr);
+
+ if (btd_adapter_add_remote_oob_data(adapter, &bdaddr, hash, randomizer))
+ return btd_error_failed(msg, "Request failed");
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *remove_remote_data(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct btd_adapter *adapter = data;
+ const char *addr;
+ bdaddr_t bdaddr;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &addr,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ if (bachk(addr))
+ return btd_error_invalid_args(msg);
+
+ str2ba(addr, &bdaddr);
+
+ if (btd_adapter_remove_remote_oob_data(adapter, &bdaddr))
+ return btd_error_failed(msg, "Request failed");
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static GDBusMethodTable oob_methods[] = {
+ {"AddRemoteData", "sayay", "", add_remote_data},
+ {"RemoveRemoteData", "s", "", remove_remote_data},
+ {"ReadLocalData", "", "ayay", read_local_data,
+ G_DBUS_METHOD_FLAG_ASYNC},
+ {}
+};
+
+static int oob_probe(struct btd_adapter *adapter)
+{
+ const char *path = adapter_get_path(adapter);
+
+ if (!g_dbus_register_interface(connection, path, OOB_INTERFACE,
+ oob_methods, NULL, NULL, adapter, NULL)) {
+ error("OOB interface init failed on path %s", path);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void oob_remove(struct btd_adapter *adapter)
+{
+ read_local_data_complete(adapter, NULL, NULL);
+
+ g_dbus_unregister_interface(connection, adapter_get_path(adapter),
+ OOB_INTERFACE);
+}
+
+static struct btd_adapter_driver oob_driver = {
+ .name = "oob",
+ .probe = oob_probe,
+ .remove = oob_remove,
+};
+
+static int dbusoob_init(void)
+{
+ DBG("Setup dbusoob plugin");
+
+ connection = get_dbus_connection();
+
+ oob_register_cb(read_local_data_complete);
+
+ return btd_register_adapter_driver(&oob_driver);
+}
+
+static void dbusoob_exit(void)
+{
+ DBG("Cleanup dbusoob plugin");
+
+ manager_foreach_adapter((adapter_cb) oob_remove, NULL);
+
+ btd_unregister_adapter_driver(&oob_driver);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(dbusoob, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+ dbusoob_init, dbusoob_exit)
diff --git a/plugins/echo.c b/plugins/echo.c
new file mode 100644
index 0000000..23f6e49
--- /dev/null
+++ b/plugins/echo.c
@@ -0,0 +1,167 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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 <errno.h>
+#include <unistd.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+
+#include <glib.h>
+
+#include <gdbus.h>
+
+#include "plugin.h"
+#include "adapter.h"
+#include "log.h"
+
+static gboolean session_event(GIOChannel *chan,
+ GIOCondition cond, gpointer data)
+{
+ unsigned char buf[672];
+ gsize len, written;
+ GIOError err;
+
+ if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL))
+ return FALSE;
+
+ err = g_io_channel_read(chan, (gchar *) buf, sizeof(buf), &len);
+ if (err == G_IO_ERROR_AGAIN)
+ return TRUE;
+
+ g_io_channel_write(chan, (const gchar *) buf, len, &written);
+
+ return TRUE;
+}
+
+static gboolean connect_event(GIOChannel *chan,
+ GIOCondition cond, gpointer data)
+{
+ GIOChannel *io;
+ struct sockaddr_rc addr;
+ socklen_t optlen;
+ char address[18];
+ int sk, nsk;
+
+ sk = g_io_channel_unix_get_fd(chan);
+
+ memset(&addr, 0, sizeof(addr));
+ optlen = sizeof(addr);
+
+ nsk = accept(sk, (struct sockaddr *) &addr, &optlen);
+ if (nsk < 0)
+ return TRUE;
+
+ io = g_io_channel_unix_new(nsk);
+ g_io_channel_set_close_on_unref(io, TRUE);
+
+ ba2str(&addr.rc_bdaddr, address);
+
+ g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ session_event, NULL);
+
+ return TRUE;
+}
+
+static GIOChannel *setup_rfcomm(uint8_t channel)
+{
+ GIOChannel *io;
+ struct sockaddr_rc addr;
+ int sk;
+
+ sk = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+ if (sk < 0)
+ return NULL;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.rc_family = AF_BLUETOOTH;
+ bacpy(&addr.rc_bdaddr, BDADDR_ANY);
+ addr.rc_channel = channel;
+
+ if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ close(sk);
+ return NULL;
+ }
+
+ if (listen(sk, 10) < 0) {
+ close(sk);
+ return NULL;
+ }
+
+ io = g_io_channel_unix_new(sk);
+ g_io_channel_set_close_on_unref(io, TRUE);
+
+ g_io_add_watch(io, G_IO_IN, connect_event, NULL);
+
+ return io;
+}
+
+static GIOChannel *chan = NULL;
+
+static int echo_probe(struct btd_adapter *adapter)
+{
+ const char *path = adapter_get_path(adapter);
+
+ DBG("path %s", path);
+
+ chan = setup_rfcomm(23);
+
+ return 0;
+}
+
+static void echo_remove(struct btd_adapter *adapter)
+{
+ const char *path = adapter_get_path(adapter);
+
+ DBG("path %s", path);
+
+ g_io_channel_unref(chan);
+}
+
+static struct btd_adapter_driver echo_server = {
+ .name = "echo-server",
+ .probe = echo_probe,
+ .remove = echo_remove,
+};
+
+static int echo_init(void)
+{
+ DBG("Setup echo plugin");
+
+ return btd_register_adapter_driver(&echo_server);
+}
+
+static void echo_exit(void)
+{
+ DBG("Cleanup echo plugin");
+
+ btd_unregister_adapter_driver(&echo_server);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(echo, VERSION,
+ BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, echo_init, echo_exit)
diff --git a/plugins/formfactor.c b/plugins/formfactor.c
new file mode 100644
index 0000000..758d481
--- /dev/null
+++ b/plugins/formfactor.c
@@ -0,0 +1,148 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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 <stdlib.h>
+#include <errno.h>
+
+#include <bluetooth/bluetooth.h>
+
+#include "plugin.h"
+#include "adapter.h"
+#include "log.h"
+
+#define DMI_CHASSIS_FILE "/sys/class/dmi/id/chassis_type"
+#define DMI_CHASSIS_FILE_FALLBACK "/sys/devices/virtual/dmi/id/chassis_type"
+
+/* Map the chassis type from chassis_type to a sensible type used in hal
+ *
+ * See also 3.3.4.1 of the "System Management BIOS Reference Specification,
+ * Version 2.6.1" (Preliminary Standard) document, available from
+ * http://www.dmtf.org/standards/smbios.
+ *
+ * TODO: figure out WTF the mapping should be; "Lunch Box"? Give me a break :-)
+ *
+ * Copied from hal/hald/linux/osspec.c
+ */
+static const char *chassis_map[] = {
+ "Other", "unknown", /* 0x01 */
+ "Unknown", "unknown",
+ "Desktop", "desktop",
+ "Low Profile Desktop", "desktop",
+ "Pizza Box", "server",
+ "Mini Tower", "desktop",
+ "Tower", "desktop",
+ "Portable", "laptop",
+ "Laptop", "laptop",
+ "Notebook", "laptop",
+ "Hand Held", "handheld",
+ "Docking Station", "laptop",
+ "All In One", "unknown",
+ "Sub Notebook", "laptop",
+ "Space-saving", "desktop",
+ "Lunch Box", "unknown",
+ "Main Server Chassis", "server",
+ "Expansion Chassis", "unknown",
+ "Sub Chassis", "unknown",
+ "Bus Expansion Chassis", "unknown",
+ "Peripheral Chassis", "unknown",
+ "RAID Chassis", "unknown",
+ "Rack Mount Chassis", "unknown",
+ "Sealed-case PC", "unknown",
+ "Multi-system", "unknown",
+ "CompactPCI", "unknonw",
+ "AdvancedTCA", "unknown",
+ "Blade", "server",
+ "Blade Enclosure" "unknown", /* 0x1D */
+ NULL
+};
+
+static int formfactor_probe(struct btd_adapter *adapter)
+{
+ int chassis_type;
+ uint8_t minor = 0;
+ const char *formfactor;
+ char *contents;
+
+ if (g_file_get_contents(DMI_CHASSIS_FILE,
+ &contents, NULL, NULL) == FALSE) {
+ if (g_file_get_contents(DMI_CHASSIS_FILE_FALLBACK,
+ &contents, NULL, NULL) == FALSE) {
+ error("Could not get the contents of DMI chassis type");
+ return 0;
+ }
+ }
+
+ chassis_type = atoi(contents);
+ g_free (contents);
+
+ if (chassis_type > 0x1D || chassis_type <= 0) {
+ error ("Chassis type is not a known chassis type");
+ return 0;
+ }
+
+ formfactor = chassis_map[chassis_type * 2];
+ if (formfactor != NULL) {
+ if (g_str_equal(formfactor, "laptop") == TRUE)
+ minor |= (1 << 2) | (1 << 3);
+ else if (g_str_equal(formfactor, "desktop") == TRUE)
+ minor |= 1 << 2;
+ else if (g_str_equal(formfactor, "server") == TRUE)
+ minor |= 1 << 3;
+ else if (g_str_equal(formfactor, "handheld") == TRUE)
+ minor += 1 << 4;
+ }
+
+ /* Computer major class */
+ DBG("Setting 0x%06x for major/minor device class", (1 << 8) | minor);
+
+ btd_adapter_set_class(adapter, 0x01, minor);
+
+ return 0;
+}
+
+static void formfactor_remove(struct btd_adapter *adapter)
+{
+}
+
+static struct btd_adapter_driver formfactor_driver = {
+ .name = "formfactor",
+ .probe = formfactor_probe,
+ .remove = formfactor_remove,
+};
+
+static int formfactor_init(void)
+{
+ return btd_register_adapter_driver(&formfactor_driver);
+}
+
+static void formfactor_exit(void)
+{
+ btd_unregister_adapter_driver(&formfactor_driver);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(formfactor, VERSION,
+ BLUETOOTH_PLUGIN_PRIORITY_LOW, formfactor_init, formfactor_exit)
diff --git a/plugins/hal.c b/plugins/hal.c
new file mode 100644
index 0000000..21d7688
--- /dev/null
+++ b/plugins/hal.c
@@ -0,0 +1,144 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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 <errno.h>
+#include <dbus/dbus.h>
+
+#include <bluetooth/bluetooth.h>
+
+#include "plugin.h"
+#include "adapter.h"
+#include "log.h"
+
+static void formfactor_reply(DBusPendingCall *call, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ const char *formfactor = NULL;
+ DBusMessage *reply;
+ uint8_t minor = 0;
+
+ reply = dbus_pending_call_steal_reply(call);
+
+ if (dbus_set_error_from_message(NULL, reply) == TRUE) {
+ error("Failed to access HAL");
+ dbus_message_unref(reply);
+ return;
+ }
+
+ if (dbus_message_get_args(reply, NULL, DBUS_TYPE_STRING, &formfactor,
+ DBUS_TYPE_INVALID) == FALSE) {
+ error("Wrong formfactor arguments");
+ dbus_message_unref(reply);
+ return;
+ }
+
+ DBG("Computer is classified as %s", formfactor);
+
+ if (formfactor != NULL) {
+ if (g_str_equal(formfactor, "laptop") == TRUE)
+ minor |= (1 << 2) | (1 << 3);
+ else if (g_str_equal(formfactor, "desktop") == TRUE)
+ minor |= 1 << 2;
+ else if (g_str_equal(formfactor, "server") == TRUE)
+ minor |= 1 << 3;
+ else if (g_str_equal(formfactor, "handheld") == TRUE)
+ minor += 1 << 4;
+ }
+
+ dbus_message_unref(reply);
+
+ /* Computer major class */
+ DBG("Setting 0x%06x for major/minor device class", (1 << 8) | minor);
+
+ btd_adapter_set_class(adapter, 0x01, minor);
+}
+
+static DBusConnection *connection;
+
+static int hal_probe(struct btd_adapter *adapter)
+{
+ const char *property = "system.formfactor";
+ DBusMessage *message;
+ DBusPendingCall *call;
+
+ connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+ if (connection == NULL)
+ return -ENOMEM;
+
+ message = dbus_message_new_method_call("org.freedesktop.Hal",
+ "/org/freedesktop/Hal/devices/computer",
+ "org.freedesktop.Hal.Device",
+ "GetPropertyString");
+ if (message == NULL) {
+ error("Failed to create formfactor request");
+ dbus_connection_unref(connection);
+ return -ENOMEM;
+ }
+
+ dbus_message_append_args(message, DBUS_TYPE_STRING, &property,
+ DBUS_TYPE_INVALID);
+
+ if (dbus_connection_send_with_reply(connection, message,
+ &call, -1) == FALSE) {
+ error("Failed to send formfactor request");
+ dbus_message_unref(message);
+ dbus_connection_unref(connection);
+ return -EIO;
+ }
+
+ dbus_pending_call_set_notify(call, formfactor_reply, adapter, NULL);
+
+ dbus_pending_call_unref(call);
+
+ dbus_message_unref(message);
+
+ return 0;
+}
+
+static void hal_remove(struct btd_adapter *adapter)
+{
+ dbus_connection_unref(connection);
+}
+
+static struct btd_adapter_driver hal_driver = {
+ .name = "hal",
+ .probe = hal_probe,
+ .remove = hal_remove,
+};
+
+static int hal_init(void)
+{
+ return btd_register_adapter_driver(&hal_driver);
+}
+
+static void hal_exit(void)
+{
+ btd_unregister_adapter_driver(&hal_driver);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(hal, VERSION,
+ BLUETOOTH_PLUGIN_PRIORITY_LOW, hal_init, hal_exit)
diff --git a/plugins/hciops.c b/plugins/hciops.c
new file mode 100644
index 0000000..93f6f21
--- /dev/null
+++ b/plugins/hciops.c
@@ -0,0 +1,3744 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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 <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.h>
+
+#include "hcid.h"
+#include "sdpd.h"
+#include "btio.h"
+#include "adapter.h"
+#include "device.h"
+#include "plugin.h"
+#include "log.h"
+#include "storage.h"
+#include "event.h"
+#include "manager.h"
+#include "oob.h"
+
+static int child_pipe[2] = { -1, -1 };
+
+static guint child_io_id = 0;
+static guint ctl_io_id = 0;
+
+/* Commands sent by kernel on starting an adapter */
+enum {
+ PENDING_BDADDR,
+ PENDING_VERSION,
+ PENDING_FEATURES,
+ PENDING_NAME,
+};
+
+struct uuid_info {
+ uuid_t uuid;
+ uint8_t svc_hint;
+};
+
+struct bt_conn {
+ struct dev_info *dev;
+ bdaddr_t bdaddr;
+ uint16_t handle;
+ uint8_t loc_cap;
+ uint8_t loc_auth;
+ uint8_t rem_cap;
+ uint8_t rem_auth;
+ uint8_t rem_oob_data;
+ gboolean bonding_initiator;
+ gboolean secmode3;
+ GIOChannel *io; /* For raw L2CAP socket (bonding) */
+};
+
+struct oob_data {
+ bdaddr_t bdaddr;
+ uint8_t hash[16];
+ uint8_t randomizer[16];
+};
+
+static int max_dev = -1;
+static struct dev_info {
+ int id;
+ int sk;
+ bdaddr_t bdaddr;
+ char name[249];
+ uint8_t eir[240];
+ uint8_t features[8];
+ uint8_t ssp_mode;
+
+ int8_t tx_power;
+
+ uint32_t current_cod;
+ uint32_t wanted_cod;
+ uint32_t pending_cod;
+ gboolean cache_enable;
+ gboolean already_up;
+ gboolean registered;
+ gboolean pairable;
+
+ uint8_t io_capability;
+
+ struct hci_version ver;
+
+ uint16_t did_vendor;
+ uint16_t did_product;
+ uint16_t did_version;
+
+ gboolean up;
+ uint32_t pending;
+
+ GIOChannel *io;
+ guint watch_id;
+
+ gboolean debug_keys;
+ GSList *keys;
+ uint8_t pin_length;
+
+ GSList *oob_data;
+
+ GSList *uuids;
+
+ GSList *connections;
+} *devs = NULL;
+
+static int ignore_device(struct hci_dev_info *di)
+{
+ return hci_test_bit(HCI_RAW, &di->flags) || di->type >> 4 != HCI_BREDR;
+}
+
+static struct dev_info *init_dev_info(int index, int sk, gboolean registered,
+ gboolean already_up)
+{
+ struct dev_info *dev = &devs[index];
+
+ memset(dev, 0, sizeof(*dev));
+
+ dev->id = index;
+ dev->sk = sk;
+ dev->cache_enable = TRUE;
+ dev->registered = registered;
+ dev->already_up = already_up;
+ dev->io_capability = 0x03; /* No Input No Output */
+
+ return dev;
+}
+
+/* Async HCI command handling with callback support */
+
+struct hci_cmd_data {
+ bt_hci_result_t cb;
+ uint16_t handle;
+ uint16_t ocf;
+ gpointer caller_data;
+};
+
+static gboolean hci_event_watch(GIOChannel *io,
+ GIOCondition cond, gpointer user_data)
+{
+ unsigned char buf[HCI_MAX_EVENT_SIZE], *body;
+ struct hci_cmd_data *cmd = user_data;
+ evt_cmd_status *evt_status;
+ evt_auth_complete *evt_auth;
+ evt_encrypt_change *evt_enc;
+ hci_event_hdr *hdr;
+ set_conn_encrypt_cp cp;
+ int dd;
+ uint16_t ocf;
+ uint8_t status = HCI_OE_POWER_OFF;
+
+ if (cond & G_IO_NVAL) {
+ cmd->cb(status, cmd->caller_data);
+ return FALSE;
+ }
+
+ if (cond & (G_IO_ERR | G_IO_HUP))
+ goto failed;
+
+ dd = g_io_channel_unix_get_fd(io);
+
+ if (read(dd, buf, sizeof(buf)) < 0)
+ goto failed;
+
+ hdr = (hci_event_hdr *) (buf + 1);
+ body = buf + (1 + HCI_EVENT_HDR_SIZE);
+
+ switch (hdr->evt) {
+ case EVT_CMD_STATUS:
+ evt_status = (evt_cmd_status *) body;
+ ocf = cmd_opcode_ocf(evt_status->opcode);
+ if (ocf != cmd->ocf)
+ return TRUE;
+ switch (ocf) {
+ case OCF_AUTH_REQUESTED:
+ case OCF_SET_CONN_ENCRYPT:
+ if (evt_status->status != 0) {
+ /* Baseband rejected command */
+ status = evt_status->status;
+ goto failed;
+ }
+ break;
+ default:
+ return TRUE;
+ }
+ /* Wait for the next event */
+ return TRUE;
+ case EVT_AUTH_COMPLETE:
+ evt_auth = (evt_auth_complete *) body;
+ if (evt_auth->handle != cmd->handle) {
+ /* Skipping */
+ return TRUE;
+ }
+
+ if (evt_auth->status != 0x00) {
+ status = evt_auth->status;
+ /* Abort encryption */
+ goto failed;
+ }
+
+ memset(&cp, 0, sizeof(cp));
+ cp.handle = cmd->handle;
+ cp.encrypt = 1;
+
+ cmd->ocf = OCF_SET_CONN_ENCRYPT;
+
+ if (hci_send_cmd(dd, OGF_LINK_CTL, OCF_SET_CONN_ENCRYPT,
+ SET_CONN_ENCRYPT_CP_SIZE, &cp) < 0) {
+ status = HCI_COMMAND_DISALLOWED;
+ goto failed;
+ }
+ /* Wait for encrypt change event */
+ return TRUE;
+ case EVT_ENCRYPT_CHANGE:
+ evt_enc = (evt_encrypt_change *) body;
+ if (evt_enc->handle != cmd->handle)
+ return TRUE;
+
+ /* Procedure finished: reporting status */
+ status = evt_enc->status;
+ break;
+ default:
+ /* Skipping */
+ return TRUE;
+ }
+
+failed:
+ cmd->cb(status, cmd->caller_data);
+ g_io_channel_shutdown(io, TRUE, NULL);
+
+ return FALSE;
+}
+
+static int write_inq_mode(int index, uint8_t mode)
+{
+ struct dev_info *dev = &devs[index];
+ write_inquiry_mode_cp cp;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.mode = mode;
+
+ if (hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_WRITE_INQUIRY_MODE,
+ WRITE_INQUIRY_MODE_CP_SIZE, &cp) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static uint8_t get_inquiry_mode(int index)
+{
+ struct dev_info *dev = &devs[index];
+
+ if (dev->features[6] & LMP_EXT_INQ)
+ return 2;
+
+ if (dev->features[3] & LMP_RSSI_INQ)
+ return 1;
+
+ if (dev->ver.manufacturer == 11 && dev->ver.hci_rev == 0x00 &&
+ dev->ver.lmp_subver == 0x0757)
+ return 1;
+
+ if (dev->ver.manufacturer == 15) {
+ if (dev->ver.hci_rev == 0x03 &&
+ dev->ver.lmp_subver == 0x6963)
+ return 1;
+ if (dev->ver.hci_rev == 0x09 &&
+ dev->ver.lmp_subver == 0x6963)
+ return 1;
+ if (dev->ver.hci_rev == 0x00 &&
+ dev->ver.lmp_subver == 0x6965)
+ return 1;
+ }
+
+ if (dev->ver.manufacturer == 31 && dev->ver.hci_rev == 0x2005 &&
+ dev->ver.lmp_subver == 0x1805)
+ return 1;
+
+ return 0;
+}
+
+static int init_ssp_mode(int index)
+{
+ struct dev_info *dev = &devs[index];
+ write_simple_pairing_mode_cp cp;
+
+ if (ioctl(dev->sk, HCIGETAUTHINFO, NULL) < 0 && errno == EINVAL)
+ return 0;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.mode = 0x01;
+
+ if (hci_send_cmd(dev->sk, OGF_HOST_CTL,
+ OCF_WRITE_SIMPLE_PAIRING_MODE,
+ WRITE_SIMPLE_PAIRING_MODE_CP_SIZE, &cp) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int hciops_set_discoverable(int index, gboolean discoverable)
+{
+ struct dev_info *dev = &devs[index];
+ uint8_t mode;
+
+ if (discoverable)
+ mode = (SCAN_PAGE | SCAN_INQUIRY);
+ else
+ mode = SCAN_PAGE;
+
+ DBG("hci%d discoverable %d", index, discoverable);
+
+ if (hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_WRITE_SCAN_ENABLE,
+ 1, &mode) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int hciops_set_pairable(int index, gboolean pairable)
+{
+ struct btd_adapter *adapter;
+
+ DBG("hci%d pairable %d", index, pairable);
+
+ adapter = manager_find_adapter(&devs[index].bdaddr);
+ if (adapter)
+ btd_adapter_pairable_changed(adapter, pairable);
+
+ devs[index].pairable = pairable;
+
+ return 0;
+}
+
+static int hciops_power_off(int index)
+{
+ struct dev_info *dev = &devs[index];
+
+ DBG("hci%d", index);
+
+ if (ioctl(dev->sk, HCIDEVDOWN, index) < 0 && errno != EALREADY)
+ return -errno;
+
+ return 0;
+}
+
+static void set_event_mask(int index)
+{
+ struct dev_info *dev = &devs[index];
+ /* The second byte is 0xff instead of 0x9f (two reserved bits
+ * disabled) since a Broadcom 1.2 dongle doesn't respond to the
+ * command otherwise */
+ uint8_t events[8] = { 0xff, 0xff, 0xfb, 0xff, 0x00, 0x00, 0x00, 0x00 };
+
+ /* Events for 1.2 and newer controllers */
+ if (dev->ver.lmp_ver > 1) {
+ events[4] |= 0x01; /* Flow Specification Complete */
+ events[4] |= 0x02; /* Inquiry Result with RSSI */
+ events[4] |= 0x04; /* Read Remote Extended Features Complete */
+ events[5] |= 0x08; /* Synchronous Connection Complete */
+ events[5] |= 0x10; /* Synchronous Connection Changed */
+ }
+
+ if (dev->features[3] & LMP_RSSI_INQ)
+ events[4] |= 0x04; /* Inquiry Result with RSSI */
+
+ if (dev->features[5] & LMP_SNIFF_SUBR)
+ events[5] |= 0x20; /* Sniff Subrating */
+
+ if (dev->features[5] & LMP_PAUSE_ENC)
+ events[5] |= 0x80; /* Encryption Key Refresh Complete */
+
+ if (dev->features[6] & LMP_EXT_INQ)
+ events[5] |= 0x40; /* Extended Inquiry Result */
+
+ if (dev->features[6] & LMP_NFLUSH_PKTS)
+ events[7] |= 0x01; /* Enhanced Flush Complete */
+
+ if (dev->features[7] & LMP_LSTO)
+ events[6] |= 0x80; /* Link Supervision Timeout Changed */
+
+ if (dev->features[6] & LMP_SIMPLE_PAIR) {
+ events[6] |= 0x01; /* IO Capability Request */
+ events[6] |= 0x02; /* IO Capability Response */
+ events[6] |= 0x04; /* User Confirmation Request */
+ events[6] |= 0x08; /* User Passkey Request */
+ events[6] |= 0x10; /* Remote OOB Data Request */
+ events[6] |= 0x20; /* Simple Pairing Complete */
+ events[7] |= 0x04; /* User Passkey Notification */
+ events[7] |= 0x08; /* Keypress Notification */
+ events[7] |= 0x10; /* Remote Host Supported
+ * Features Notification */
+ }
+
+ if (dev->features[4] & LMP_LE)
+ events[7] |= 0x20; /* LE Meta-Event */
+
+ hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_SET_EVENT_MASK,
+ sizeof(events), events);
+}
+
+static void start_adapter(int index)
+{
+ struct dev_info *dev = &devs[index];
+ uint8_t inqmode;
+ uint16_t link_policy;
+
+ set_event_mask(index);
+
+ if (dev->features[6] & LMP_SIMPLE_PAIR)
+ init_ssp_mode(index);
+
+ inqmode = get_inquiry_mode(index);
+ if (inqmode)
+ write_inq_mode(index, inqmode);
+
+ if (dev->features[7] & LMP_INQ_TX_PWR)
+ hci_send_cmd(dev->sk, OGF_HOST_CTL,
+ OCF_READ_INQ_RESPONSE_TX_POWER_LEVEL, 0, NULL);
+
+ /* Set default link policy */
+ link_policy = main_opts.link_policy;
+
+ if (!(dev->features[0] & LMP_RSWITCH))
+ link_policy &= ~HCI_LP_RSWITCH;
+ if (!(dev->features[0] & LMP_HOLD))
+ link_policy &= ~HCI_LP_HOLD;
+ if (!(dev->features[0] & LMP_SNIFF))
+ link_policy &= ~HCI_LP_SNIFF;
+ if (!(dev->features[1] & LMP_PARK))
+ link_policy &= ~HCI_LP_PARK;
+
+ link_policy = htobs(link_policy);
+ hci_send_cmd(dev->sk, OGF_LINK_POLICY, OCF_WRITE_DEFAULT_LINK_POLICY,
+ sizeof(link_policy), &link_policy);
+
+ dev->current_cod = 0;
+ memset(dev->eir, 0, sizeof(dev->eir));
+}
+
+static int hciops_stop_inquiry(int index)
+{
+ struct dev_info *dev = &devs[index];
+ struct hci_dev_info di;
+ int err;
+
+ DBG("hci%d", index);
+
+ if (hci_devinfo(index, &di) < 0)
+ return -errno;
+
+ if (hci_test_bit(HCI_INQUIRY, &di.flags))
+ err = hci_send_cmd(dev->sk, OGF_LINK_CTL,
+ OCF_INQUIRY_CANCEL, 0, 0);
+ else
+ err = hci_send_cmd(dev->sk, OGF_LINK_CTL,
+ OCF_EXIT_PERIODIC_INQUIRY, 0, 0);
+ if (err < 0)
+ err = -errno;
+
+ return err;
+}
+
+static gboolean init_adapter(int index)
+{
+ struct dev_info *dev = &devs[index];
+ struct btd_adapter *adapter = NULL;
+ gboolean existing_adapter = dev->registered;
+ uint8_t mode, on_mode;
+ gboolean pairable, discoverable;
+
+ if (!dev->registered) {
+ adapter = btd_manager_register_adapter(index);
+ if (adapter)
+ dev->registered = TRUE;
+ } else {
+ adapter = manager_find_adapter(&dev->bdaddr);
+ /* FIXME: manager_find_adapter should return a new ref */
+ btd_adapter_ref(adapter);
+ }
+
+ if (adapter == NULL)
+ return FALSE;
+
+ btd_adapter_get_mode(adapter, &mode, &on_mode, &pairable);
+
+ if (existing_adapter)
+ mode = on_mode;
+
+ if (mode == MODE_OFF) {
+ hciops_power_off(index);
+ goto done;
+ }
+
+ start_adapter(index);
+ btd_adapter_start(adapter);
+
+ discoverable = (mode == MODE_DISCOVERABLE);
+
+ hciops_set_discoverable(index, discoverable);
+ hciops_set_pairable(index, pairable);
+
+ if (dev->already_up)
+ hciops_stop_inquiry(index);
+
+done:
+ btd_adapter_unref(adapter);
+ return TRUE;
+}
+
+static int hciops_encrypt_link(int index, bdaddr_t *dst, bt_hci_result_t cb,
+ gpointer user_data)
+{
+ GIOChannel *io;
+ struct hci_cmd_data *cmd;
+ struct hci_conn_info_req *cr;
+ auth_requested_cp cp;
+ struct hci_filter nf;
+ int dd, err;
+ uint32_t link_mode;
+ uint16_t handle;
+
+ dd = hci_open_dev(index);
+ if (dd < 0)
+ return -errno;
+
+ cr = g_malloc0(sizeof(*cr) + sizeof(struct hci_conn_info));
+ cr->type = ACL_LINK;
+ bacpy(&cr->bdaddr, dst);
+
+ err = ioctl(dd, HCIGETCONNINFO, cr);
+ link_mode = cr->conn_info->link_mode;
+ handle = cr->conn_info->handle;
+ g_free(cr);
+
+ if (err < 0) {
+ err = -errno;
+ goto fail;
+ }
+
+ if (link_mode & HCI_LM_ENCRYPT) {
+ err = -EALREADY;
+ goto fail;
+ }
+
+ memset(&cp, 0, sizeof(cp));
+ cp.handle = htobs(handle);
+
+ if (hci_send_cmd(dd, OGF_LINK_CTL, OCF_AUTH_REQUESTED,
+ AUTH_REQUESTED_CP_SIZE, &cp) < 0) {
+ err = -errno;
+ goto fail;
+ }
+
+ cmd = g_new0(struct hci_cmd_data, 1);
+ cmd->handle = handle;
+ cmd->ocf = OCF_AUTH_REQUESTED;
+ cmd->cb = cb;
+ cmd->caller_data = user_data;
+
+ hci_filter_clear(&nf);
+ hci_filter_set_ptype(HCI_EVENT_PKT, &nf);
+ hci_filter_set_event(EVT_CMD_STATUS, &nf);
+ hci_filter_set_event(EVT_AUTH_COMPLETE, &nf);
+ hci_filter_set_event(EVT_ENCRYPT_CHANGE, &nf);
+
+ if (setsockopt(dd, SOL_HCI, HCI_FILTER, &nf, sizeof(nf)) < 0) {
+ err = -errno;
+ g_free(cmd);
+ goto fail;
+ }
+
+ io = g_io_channel_unix_new(dd);
+ g_io_channel_set_close_on_unref(io, FALSE);
+ g_io_add_watch_full(io, G_PRIORITY_DEFAULT,
+ G_IO_HUP | G_IO_ERR | G_IO_NVAL | G_IO_IN,
+ hci_event_watch, cmd, g_free);
+ g_io_channel_unref(io);
+
+ return 0;
+
+fail:
+ close(dd);
+ return err;
+}
+
+static int hciops_set_did(int index, uint16_t vendor, uint16_t product,
+ uint16_t version)
+{
+ struct dev_info *dev = &devs[index];
+
+ dev->did_vendor = vendor;
+ dev->did_product = product;
+ dev->did_version = version;
+
+ return 0;
+}
+
+/* End async HCI command handling */
+
+/* Start of HCI event callbacks */
+
+static gint conn_handle_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct bt_conn *conn = a;
+ uint16_t handle = *((const uint16_t *) b);
+
+ return (int) conn->handle - (int) handle;
+}
+
+static struct bt_conn *find_conn_by_handle(struct dev_info *dev,
+ uint16_t handle)
+{
+ GSList *match;
+
+ match = g_slist_find_custom(dev->connections, &handle,
+ conn_handle_cmp);
+ if (match)
+ return match->data;
+
+ return NULL;
+}
+
+static gint conn_bdaddr_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct bt_conn *conn = a;
+ const bdaddr_t *bdaddr = b;
+
+ return bacmp(&conn->bdaddr, bdaddr);
+}
+
+static struct bt_conn *find_connection(struct dev_info *dev, bdaddr_t *bdaddr)
+{
+ GSList *match;
+
+ match = g_slist_find_custom(dev->connections, bdaddr, conn_bdaddr_cmp);
+ if (match)
+ return match->data;
+
+ return NULL;
+}
+
+static struct bt_conn *get_connection(struct dev_info *dev, bdaddr_t *bdaddr)
+{
+ struct bt_conn *conn;
+
+ conn = find_connection(dev, bdaddr);
+ if (conn)
+ return conn;
+
+ conn = g_new0(struct bt_conn, 1);
+
+ conn->dev = dev;
+ conn->loc_cap = dev->io_capability;
+ conn->loc_auth = 0xff;
+ conn->rem_auth = 0xff;
+ bacpy(&conn->bdaddr, bdaddr);
+
+ dev->connections = g_slist_append(dev->connections, conn);
+
+ return conn;
+}
+
+static int get_handle(int index, bdaddr_t *bdaddr, uint16_t *handle)
+{
+ struct dev_info *dev = &devs[index];
+ struct bt_conn *conn;
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+ DBG("hci%d dba %s", index, addr);
+
+ conn = find_connection(dev, bdaddr);
+ if (conn == NULL)
+ return -ENOENT;
+
+ *handle = conn->handle;
+
+ return 0;
+}
+
+static int disconnect_addr(int index, bdaddr_t *dba, uint8_t reason)
+{
+ disconnect_cp cp;
+ uint16_t handle;
+ int err;
+
+ err = get_handle(index, dba, &handle);
+ if (err < 0)
+ return err;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.handle = htobs(handle);
+ cp.reason = reason;
+
+ if (hci_send_cmd(devs[index].sk, OGF_LINK_CTL, OCF_DISCONNECT,
+ DISCONNECT_CP_SIZE, &cp) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static void bonding_complete(struct dev_info *dev, struct bt_conn *conn,
+ uint8_t status)
+{
+ DBG("status 0x%02x", status);
+
+ if (conn->io != NULL) {
+ /* bonding_connect_cb takes care of the successul case */
+ if (status != 0)
+ g_io_channel_shutdown(conn->io, TRUE, NULL);
+ g_io_channel_unref(conn->io);
+ conn->io = NULL;
+ }
+
+ conn->bonding_initiator = FALSE;
+
+ btd_event_bonding_complete(&dev->bdaddr, &conn->bdaddr, status);
+}
+
+static int get_auth_info(int index, bdaddr_t *bdaddr, uint8_t *auth)
+{
+ struct dev_info *dev = &devs[index];
+ struct hci_auth_info_req req;
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+ DBG("hci%d dba %s", index, addr);
+
+ memset(&req, 0, sizeof(req));
+ bacpy(&req.bdaddr, bdaddr);
+
+ if (ioctl(dev->sk, HCIGETAUTHINFO, (unsigned long) &req) < 0)
+ return -errno;
+
+ if (auth)
+ *auth = req.type;
+
+ return 0;
+}
+
+/* Link Key handling */
+
+static void link_key_request(int index, bdaddr_t *dba)
+{
+ struct dev_info *dev = &devs[index];
+ struct link_key_info *key_info;
+ struct bt_conn *conn;
+ GSList *match;
+ char da[18];
+
+ ba2str(dba, da);
+ DBG("hci%d dba %s", index, da);
+
+ conn = get_connection(dev, dba);
+ if (conn->handle == 0)
+ conn->secmode3 = TRUE;
+
+ get_auth_info(index, dba, &conn->loc_auth);
+
+ DBG("kernel auth requirements = 0x%02x", conn->loc_auth);
+
+ match = g_slist_find_custom(dev->keys, dba, (GCompareFunc) bacmp);
+ if (match)
+ key_info = match->data;
+ else
+ key_info = NULL;
+
+ DBG("Matching key %s", key_info ? "found" : "not found");
+
+ if (key_info == NULL || (!dev->debug_keys && key_info->type == 0x03)) {
+ /* Link key not found */
+ hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_LINK_KEY_NEG_REPLY,
+ 6, dba);
+ return;
+ }
+
+ /* Link key found */
+
+ DBG("link key type 0x%02x", key_info->type);
+
+ /* Don't use unauthenticated combination keys if MITM is
+ * required */
+ if (key_info->type == 0x04 && conn->loc_auth != 0xff &&
+ (conn->loc_auth & 0x01))
+ hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_LINK_KEY_NEG_REPLY,
+ 6, dba);
+ else {
+ link_key_reply_cp lr;
+
+ memcpy(lr.link_key, key_info->key, 16);
+ bacpy(&lr.bdaddr, dba);
+
+ hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_LINK_KEY_REPLY,
+ LINK_KEY_REPLY_CP_SIZE, &lr);
+ }
+}
+
+static void link_key_notify(int index, void *ptr)
+{
+ struct dev_info *dev = &devs[index];
+ evt_link_key_notify *evt = ptr;
+ bdaddr_t *dba = &evt->bdaddr;
+ struct link_key_info *key_info;
+ uint8_t old_key_type, key_type;
+ struct bt_conn *conn;
+ GSList *match;
+ char da[18];
+ uint8_t status = 0;
+
+ ba2str(dba, da);
+ DBG("hci%d dba %s type %d", index, da, evt->key_type);
+
+ conn = get_connection(dev, &evt->bdaddr);
+
+ match = g_slist_find_custom(dev->keys, dba, (GCompareFunc) bacmp);
+ if (match)
+ key_info = match->data;
+ else
+ key_info = NULL;
+
+ if (key_info == NULL) {
+ key_info = g_new0(struct link_key_info, 1);
+ bacpy(&key_info->bdaddr, &evt->bdaddr);
+ old_key_type = 0xff;
+ } else {
+ dev->keys = g_slist_remove(dev->keys, key_info);
+ old_key_type = key_info->type;
+ }
+
+ memcpy(key_info->key, evt->link_key, sizeof(evt->link_key));
+ key_info->type = evt->key_type;
+ key_info->pin_len = dev->pin_length;
+
+ key_type = evt->key_type;
+
+ DBG("key type 0x%02x old key type 0x%02x", key_type, old_key_type);
+ DBG("local auth 0x%02x and remote auth 0x%02x",
+ conn->loc_auth, conn->rem_auth);
+
+ if (key_type == 0x06) {
+ /* Some buggy controller combinations generate a changed
+ * combination key for legacy pairing even when there's no
+ * previous key */
+ if ((!conn || conn->rem_auth == 0xff) && old_key_type == 0xff)
+ key_type = 0x00;
+ else if (old_key_type != 0xff)
+ key_type = old_key_type;
+ else
+ /* This is Changed Combination Link Key for
+ * a temporary link key.*/
+ goto done;
+ }
+
+ key_info->type = key_type;
+
+ /* Skip the storage check if this is a debug key */
+ if (key_type == 0x03)
+ goto done;
+
+ /* Store the link key persistently if one of the following is true:
+ * 1. this is a legacy link key
+ * 2. this is a changed combination key and there was a previously
+ * stored one
+ * 3. neither local nor remote side had no-bonding as a requirement
+ * 4. the local side had dedicated bonding as a requirement
+ * 5. the remote side is using dedicated bonding since in that case
+ * also the local requirements are set to dedicated bonding
+ * If none of the above match only keep the link key around for
+ * this connection and set the temporary flag for the device.
+ */
+ if (key_type < 0x03 || (key_type == 0x06 && old_key_type != 0xff) ||
+ (conn->loc_auth > 0x01 && conn->rem_auth > 0x01) ||
+ (conn->loc_auth == 0x02 || conn->loc_auth == 0x03) ||
+ (conn->rem_auth == 0x02 || conn->rem_auth == 0x03)) {
+ int err;
+
+ err = btd_event_link_key_notify(&dev->bdaddr, dba,
+ evt->link_key, key_type,
+ dev->pin_length);
+
+ if (err == -ENODEV)
+ status = HCI_OE_LOW_RESOURCES;
+ else if (err < 0)
+ status = HCI_MEMORY_FULL;
+
+ goto done;
+ }
+
+done:
+ dev->pin_length = 0;
+
+ if (status != 0) {
+ g_free(key_info);
+ bonding_complete(dev, conn, status);
+ disconnect_addr(index, dba, status);
+ return;
+ }
+
+ dev->keys = g_slist_prepend(dev->keys, key_info);
+
+ /* If we're connected and not dedicated bonding initiators we're
+ * done with the bonding process */
+ if (!conn->bonding_initiator && conn->handle != 0)
+ bonding_complete(dev, conn, 0);
+}
+
+static void return_link_keys(int index, void *ptr)
+{
+ struct dev_info *dev = &devs[index];
+ evt_return_link_keys *evt = ptr;
+ uint8_t num = evt->num_keys;
+ unsigned char key[16];
+ char da[18];
+ bdaddr_t dba;
+ int i;
+
+ DBG("hci%d num_keys %u", index, num);
+
+ ptr++;
+
+ for (i = 0; i < num; i++) {
+ bacpy(&dba, ptr); ba2str(&dba, da);
+ memcpy(key, ptr + 6, 16);
+
+ DBG("hci%d returned key for %s", index, da);
+
+ btd_event_returned_link_key(&dev->bdaddr, &dba);
+
+ ptr += 22;
+ }
+}
+
+/* Simple Pairing handling */
+
+static int hciops_confirm_reply(int index, bdaddr_t *bdaddr, gboolean success)
+{
+ struct dev_info *dev = &devs[index];
+ user_confirm_reply_cp cp;
+ char addr[18];
+ int err;
+
+ ba2str(bdaddr, addr);
+ DBG("hci%d dba %s success %d", index, addr, success);
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.bdaddr, bdaddr);
+
+ if (success)
+ err = hci_send_cmd(dev->sk, OGF_LINK_CTL,
+ OCF_USER_CONFIRM_REPLY,
+ USER_CONFIRM_REPLY_CP_SIZE, &cp);
+ else
+ err = hci_send_cmd(dev->sk, OGF_LINK_CTL,
+ OCF_USER_CONFIRM_NEG_REPLY,
+ USER_CONFIRM_REPLY_CP_SIZE, &cp);
+
+ if (err < 0)
+ err = -errno;
+
+ return err;
+}
+
+static void user_confirm_request(int index, void *ptr)
+{
+ struct dev_info *dev = &devs[index];
+ evt_user_confirm_request *req = ptr;
+ gboolean loc_mitm, rem_mitm;
+ struct bt_conn *conn;
+
+ DBG("hci%d", index);
+
+ conn = find_connection(dev, &req->bdaddr);
+ if (conn == NULL)
+ return;
+
+ loc_mitm = (conn->loc_auth & 0x01) ? TRUE : FALSE;
+ rem_mitm = (conn->rem_auth & 0x01) ? TRUE : FALSE;
+
+ /* If we require MITM but the remote device can't provide that
+ * (it has NoInputNoOutput) then reject the confirmation
+ * request. The only exception is when we're dedicated bonding
+ * initiators since then we always have the MITM bit set. */
+ if (!conn->bonding_initiator && loc_mitm && conn->rem_cap == 0x03) {
+ error("Rejecting request: remote device can't provide MITM");
+ goto fail;
+ }
+
+ /* If no side requires MITM protection; auto-accept */
+ if ((conn->loc_auth == 0xff || !loc_mitm || conn->rem_cap == 0x03) &&
+ (!rem_mitm || conn->loc_cap == 0x03)) {
+ DBG("auto accept of confirmation");
+
+ /* Wait 5 milliseconds before doing auto-accept */
+ usleep(5000);
+
+ if (hciops_confirm_reply(index, &req->bdaddr, TRUE) < 0)
+ goto fail;
+
+ return;
+ }
+
+ if (btd_event_user_confirm(&dev->bdaddr, &req->bdaddr,
+ btohl(req->passkey)) == 0)
+ return;
+
+fail:
+ hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_USER_CONFIRM_NEG_REPLY,
+ 6, ptr);
+}
+
+static void user_passkey_request(int index, void *ptr)
+{
+ struct dev_info *dev = &devs[index];
+ evt_user_passkey_request *req = ptr;
+
+ DBG("hci%d", index);
+
+ if (btd_event_user_passkey(&dev->bdaddr, &req->bdaddr) < 0)
+ hci_send_cmd(dev->sk, OGF_LINK_CTL,
+ OCF_USER_PASSKEY_NEG_REPLY, 6, ptr);
+}
+
+static void user_passkey_notify(int index, void *ptr)
+{
+ struct dev_info *dev = &devs[index];
+ evt_user_passkey_notify *req = ptr;
+
+ DBG("hci%d", index);
+
+ btd_event_user_notify(&dev->bdaddr, &req->bdaddr,
+ btohl(req->passkey));
+}
+
+static gint oob_bdaddr_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct oob_data *data = a;
+ const bdaddr_t *bdaddr = b;
+
+ return bacmp(&data->bdaddr, bdaddr);
+}
+
+static void remote_oob_data_request(int index, bdaddr_t *bdaddr)
+{
+ struct dev_info *dev = &devs[index];
+ GSList *match;
+
+ DBG("hci%d", index);
+
+ match = g_slist_find_custom(dev->oob_data, bdaddr, oob_bdaddr_cmp);
+
+ if (match) {
+ struct oob_data *data;
+ remote_oob_data_reply_cp cp;
+
+ data = match->data;
+
+ bacpy(&cp.bdaddr, &data->bdaddr);
+ memcpy(cp.hash, data->hash, sizeof(cp.hash));
+ memcpy(cp.randomizer, data->randomizer, sizeof(cp.randomizer));
+
+ dev->oob_data = g_slist_delete_link(dev->oob_data, match);
+
+ hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_REMOTE_OOB_DATA_REPLY,
+ REMOTE_OOB_DATA_REPLY_CP_SIZE, &cp);
+
+ } else {
+ hci_send_cmd(dev->sk, OGF_LINK_CTL,
+ OCF_REMOTE_OOB_DATA_NEG_REPLY, 6, bdaddr);
+ }
+}
+
+static int get_io_cap(int index, bdaddr_t *bdaddr, uint8_t *cap, uint8_t *auth)
+{
+ struct dev_info *dev = &devs[index];
+ struct bt_conn *conn;
+ int err;
+
+ conn = find_connection(dev, bdaddr);
+ if (conn == NULL)
+ return -ENOENT;
+
+ err = get_auth_info(index, bdaddr, &conn->loc_auth);
+ if (err < 0)
+ return err;
+
+ DBG("initial authentication requirement is 0x%02x", conn->loc_auth);
+
+ if (!dev->pairable && !conn->bonding_initiator) {
+ if (conn->rem_auth < 0x02) {
+ DBG("Allowing no bonding in non-bondable mode");
+ /* Kernel defaults to general bonding and so
+ * overwrite for this special case. Otherwise
+ * non-pairable test cases will fail. */
+ conn->loc_auth = conn->rem_auth;
+ goto done;
+ }
+
+ return -EPERM;
+ }
+
+ /* If the kernel doesn't know the local requirement just mirror
+ * the remote one */
+ if (conn->loc_auth == 0xff)
+ conn->loc_auth = conn->rem_auth;
+
+ if (conn->loc_auth == 0x00 || conn->loc_auth == 0x04) {
+ /* If remote requests dedicated bonding follow that lead */
+ if (conn->rem_auth == 0x02 || conn->rem_auth == 0x03) {
+
+ /* If both remote and local IO capabilities allow MITM
+ * then require it, otherwise don't */
+ if (conn->rem_cap == 0x03 || conn->loc_cap == 0x03)
+ conn->loc_auth = 0x02;
+ else
+ conn->loc_auth = 0x03;
+ }
+
+ /* If remote indicates no bonding then follow that. This
+ * is important since the kernel might give general bonding
+ * as default. */
+ if (conn->rem_auth == 0x00 || conn->rem_auth == 0x01)
+ conn->loc_auth = 0x00;
+
+ /* If remote requires MITM then also require it, unless
+ * our IO capability is NoInputNoOutput (so some
+ * just-works security cases can be tested) */
+ if (conn->rem_auth != 0xff && (conn->rem_auth & 0x01) &&
+ conn->loc_cap != 0x03)
+ conn->loc_auth |= 0x01;
+ }
+
+done:
+ *cap = conn->loc_cap;
+ *auth = conn->loc_auth;
+
+ DBG("final authentication requirement is 0x%02x", *auth);
+
+ return 0;
+}
+
+static void io_capa_request(int index, void *ptr)
+{
+ struct dev_info *dev = &devs[index];
+ bdaddr_t *dba = ptr;
+ uint8_t cap, auth = 0xff;
+ char da[18];
+ int err;
+
+ ba2str(dba, da);
+ DBG("hci%d IO capability request for %s", index, da);
+
+ err = get_io_cap(index, dba, &cap, &auth);
+ if (err < 0) {
+ io_capability_neg_reply_cp cp;
+
+ error("Getting IO capability failed: %s (%d)",
+ strerror(-err), -err);
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.bdaddr, dba);
+ cp.reason = HCI_PAIRING_NOT_ALLOWED;
+ hci_send_cmd(dev->sk, OGF_LINK_CTL,
+ OCF_IO_CAPABILITY_NEG_REPLY,
+ IO_CAPABILITY_NEG_REPLY_CP_SIZE, &cp);
+ } else {
+ io_capability_reply_cp cp;
+ struct bt_conn *conn;
+ GSList *match;
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.bdaddr, dba);
+ cp.capability = cap;
+ cp.authentication = auth;
+
+ conn = find_connection(dev, dba);
+ match = g_slist_find_custom(dev->oob_data, dba, oob_bdaddr_cmp);
+
+ if ((conn->bonding_initiator || conn->rem_oob_data == 0x01) &&
+ match)
+ cp.oob_data = 0x01;
+ else
+ cp.oob_data = 0x00;
+
+ hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_IO_CAPABILITY_REPLY,
+ IO_CAPABILITY_REPLY_CP_SIZE, &cp);
+ }
+}
+
+static void io_capa_response(int index, void *ptr)
+{
+ struct dev_info *dev = &devs[index];
+ evt_io_capability_response *evt = ptr;
+ struct bt_conn *conn;
+ char da[18];
+
+ ba2str(&evt->bdaddr, da);
+ DBG("hci%d IO capability response from %s", index, da);
+
+ conn = find_connection(dev, &evt->bdaddr);
+ if (conn) {
+ conn->rem_cap = evt->capability;
+ conn->rem_auth = evt->authentication;
+ conn->rem_oob_data = evt->oob_data;
+ }
+}
+
+/* PIN code handling */
+
+static void pin_code_request(int index, bdaddr_t *dba)
+{
+ struct dev_info *dev = &devs[index];
+ struct bt_conn *conn;
+ char addr[18];
+ int err;
+
+ ba2str(dba, addr);
+ DBG("hci%d PIN request for %s", index, addr);
+
+ conn = get_connection(dev, dba);
+ if (conn->handle == 0)
+ conn->secmode3 = TRUE;
+
+ /* Check if the adapter is not pairable and if there isn't a bonding in
+ * progress */
+ if (!dev->pairable && !conn->bonding_initiator) {
+ DBG("Rejecting PIN request in non-pairable mode");
+ goto reject;
+ }
+
+ err = btd_event_request_pin(&dev->bdaddr, dba);
+ if (err < 0) {
+ error("PIN code negative reply: %s", strerror(-err));
+ goto reject;
+ }
+
+ return;
+
+reject:
+ hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_PIN_CODE_NEG_REPLY, 6, dba);
+}
+
+static void start_inquiry(bdaddr_t *local, uint8_t status, gboolean periodic)
+{
+ struct btd_adapter *adapter;
+ int state;
+
+ /* Don't send the signal if the cmd failed */
+ if (status) {
+ error("Inquiry Failed with status 0x%02x", status);
+ return;
+ }
+
+ adapter = manager_find_adapter(local);
+ if (!adapter) {
+ error("Unable to find matching adapter");
+ return;
+ }
+
+ state = adapter_get_state(adapter);
+
+ if (periodic)
+ state |= STATE_PINQ;
+ else
+ state |= STATE_STDINQ;
+
+ adapter_set_state(adapter, state);
+}
+
+static void inquiry_complete(bdaddr_t *local, uint8_t status,
+ gboolean periodic)
+{
+ struct btd_adapter *adapter;
+ int state;
+
+ /* Don't send the signal if the cmd failed */
+ if (status) {
+ error("Inquiry Failed with status 0x%02x", status);
+ return;
+ }
+
+ adapter = manager_find_adapter(local);
+ if (!adapter) {
+ error("Unable to find matching adapter");
+ return;
+ }
+
+ state = adapter_get_state(adapter);
+ state &= ~(STATE_STDINQ | STATE_PINQ);
+ adapter_set_state(adapter, state);
+}
+
+static inline void remote_features_notify(int index, void *ptr)
+{
+ struct dev_info *dev = &devs[index];
+ evt_remote_host_features_notify *evt = ptr;
+
+ if (evt->features[0] & 0x01)
+ btd_event_set_legacy_pairing(&dev->bdaddr, &evt->bdaddr,
+ FALSE);
+ else
+ btd_event_set_legacy_pairing(&dev->bdaddr, &evt->bdaddr,
+ TRUE);
+
+ write_features_info(&dev->bdaddr, &evt->bdaddr, NULL, evt->features);
+}
+
+static void write_le_host_complete(int index, uint8_t status)
+{
+ struct dev_info *dev = &devs[index];
+ uint8_t page_num = 0x01;
+
+ if (status)
+ return;
+
+ if (hci_send_cmd(dev->sk, OGF_INFO_PARAM,
+ OCF_READ_LOCAL_EXT_FEATURES, 1, &page_num) < 0)
+ error("Unable to read extended local features: %s (%d)",
+ strerror(errno), errno);
+}
+
+static void read_local_version_complete(int index,
+ const read_local_version_rp *rp)
+{
+ struct dev_info *dev = &devs[index];
+
+ if (rp->status)
+ return;
+
+ dev->ver.manufacturer = btohs(bt_get_unaligned(&rp->manufacturer));
+ dev->ver.hci_ver = rp->hci_ver;
+ dev->ver.hci_rev = btohs(bt_get_unaligned(&rp->hci_rev));
+ dev->ver.lmp_ver = rp->lmp_ver;
+ dev->ver.lmp_subver = btohs(bt_get_unaligned(&rp->lmp_subver));
+
+ if (!dev->pending)
+ return;
+
+ hci_clear_bit(PENDING_VERSION, &dev->pending);
+
+ DBG("Got version for hci%d", index);
+
+ if (!dev->pending && dev->up)
+ init_adapter(index);
+}
+
+static void read_local_features_complete(int index,
+ const read_local_features_rp *rp)
+{
+ struct dev_info *dev = &devs[index];
+
+ if (rp->status)
+ return;
+
+ memcpy(dev->features, rp->features, 8);
+
+ if (!dev->pending)
+ return;
+
+ hci_clear_bit(PENDING_FEATURES, &dev->pending);
+
+ DBG("Got features for hci%d", index);
+
+ if (!dev->pending && dev->up)
+ init_adapter(index);
+}
+
+#define SIZEOF_UUID128 16
+
+static void eir_generate_uuid128(GSList *list, uint8_t *ptr, uint16_t *eir_len)
+{
+ int i, k, uuid_count = 0;
+ uint16_t len = *eir_len;
+ uint8_t *uuid128;
+ gboolean truncated = FALSE;
+
+ /* Store UUIDs in place, skip 2 bytes to write type and length later */
+ uuid128 = ptr + 2;
+
+ for (; list; list = list->next) {
+ struct uuid_info *uuid = list->data;
+ uint8_t *uuid128_data = uuid->uuid.value.uuid128.data;
+
+ if (uuid->uuid.type != SDP_UUID128)
+ continue;
+
+ /* Stop if not enough space to put next UUID128 */
+ if ((len + 2 + SIZEOF_UUID128) > EIR_DATA_LENGTH) {
+ truncated = TRUE;
+ break;
+ }
+
+ /* Check for duplicates, EIR data is Little Endian */
+ for (i = 0; i < uuid_count; i++) {
+ for (k = 0; k < SIZEOF_UUID128; k++) {
+ if (uuid128[i * SIZEOF_UUID128 + k] !=
+ uuid128_data[SIZEOF_UUID128 - 1 - k])
+ break;
+ }
+ if (k == SIZEOF_UUID128)
+ break;
+ }
+
+ if (i < uuid_count)
+ continue;
+
+ /* EIR data is Little Endian */
+ for (k = 0; k < SIZEOF_UUID128; k++)
+ uuid128[uuid_count * SIZEOF_UUID128 + k] =
+ uuid128_data[SIZEOF_UUID128 - 1 - k];
+
+ len += SIZEOF_UUID128;
+ uuid_count++;
+ }
+
+ if (uuid_count > 0 || truncated) {
+ /* EIR Data length */
+ ptr[0] = (uuid_count * SIZEOF_UUID128) + 1;
+ /* EIR Data type */
+ ptr[1] = truncated ? EIR_UUID128_SOME : EIR_UUID128_ALL;
+ len += 2;
+ *eir_len = len;
+ }
+}
+
+static void create_ext_inquiry_response(int index, uint8_t *data)
+{
+ struct dev_info *dev = &devs[index];
+ GSList *l;
+ uint8_t *ptr = data;
+ uint16_t eir_len = 0;
+ uint16_t uuid16[EIR_DATA_LENGTH / 2];
+ int i, uuid_count = 0;
+ gboolean truncated = FALSE;
+ size_t name_len;
+
+ name_len = strlen(dev->name);
+
+ if (name_len > 0) {
+ /* EIR Data type */
+ if (name_len > 48) {
+ name_len = 48;
+ ptr[1] = EIR_NAME_SHORT;
+ } else
+ ptr[1] = EIR_NAME_COMPLETE;
+
+ /* EIR Data length */
+ ptr[0] = name_len + 1;
+
+ memcpy(ptr + 2, dev->name, name_len);
+
+ eir_len += (name_len + 2);
+ ptr += (name_len + 2);
+ }
+
+ if (dev->tx_power != 0) {
+ *ptr++ = 2;
+ *ptr++ = EIR_TX_POWER;
+ *ptr++ = (uint8_t) dev->tx_power;
+ eir_len += 3;
+ }
+
+ if (dev->did_vendor != 0x0000) {
+ uint16_t source = 0x0002;
+ *ptr++ = 9;
+ *ptr++ = EIR_DEVICE_ID;
+ *ptr++ = (source & 0x00ff);
+ *ptr++ = (source & 0xff00) >> 8;
+ *ptr++ = (dev->did_vendor & 0x00ff);
+ *ptr++ = (dev->did_vendor & 0xff00) >> 8;
+ *ptr++ = (dev->did_product & 0x00ff);
+ *ptr++ = (dev->did_product & 0xff00) >> 8;
+ *ptr++ = (dev->did_version & 0x00ff);
+ *ptr++ = (dev->did_version & 0xff00) >> 8;
+ eir_len += 10;
+ }
+
+ /* Group all UUID16 types */
+ for (l = dev->uuids; l != NULL; l = g_slist_next(l)) {
+ struct uuid_info *uuid = l->data;
+
+ if (uuid->uuid.type != SDP_UUID16)
+ continue;
+
+ if (uuid->uuid.value.uuid16 < 0x1100)
+ continue;
+
+ if (uuid->uuid.value.uuid16 == PNP_INFO_SVCLASS_ID)
+ continue;
+
+ /* Stop if not enough space to put next UUID16 */
+ if ((eir_len + 2 + sizeof(uint16_t)) > EIR_DATA_LENGTH) {
+ truncated = TRUE;
+ break;
+ }
+
+ /* Check for duplicates */
+ for (i = 0; i < uuid_count; i++)
+ if (uuid16[i] == uuid->uuid.value.uuid16)
+ break;
+
+ if (i < uuid_count)
+ continue;
+
+ uuid16[uuid_count++] = uuid->uuid.value.uuid16;
+ eir_len += sizeof(uint16_t);
+ }
+
+ if (uuid_count > 0) {
+ /* EIR Data length */
+ ptr[0] = (uuid_count * sizeof(uint16_t)) + 1;
+ /* EIR Data type */
+ ptr[1] = truncated ? EIR_UUID16_SOME : EIR_UUID16_ALL;
+
+ ptr += 2;
+ eir_len += 2;
+
+ for (i = 0; i < uuid_count; i++) {
+ *ptr++ = (uuid16[i] & 0x00ff);
+ *ptr++ = (uuid16[i] & 0xff00) >> 8;
+ }
+ }
+
+ /* Group all UUID128 types */
+ if (eir_len <= EIR_DATA_LENGTH - 2)
+ eir_generate_uuid128(dev->uuids, ptr, &eir_len);
+}
+
+static void update_ext_inquiry_response(int index)
+{
+ struct dev_info *dev = &devs[index];
+ write_ext_inquiry_response_cp cp;
+
+ DBG("hci%d", index);
+
+ if (!(dev->features[6] & LMP_EXT_INQ))
+ return;
+
+ if (dev->ssp_mode == 0)
+ return;
+
+ if (dev->cache_enable)
+ return;
+
+ memset(&cp, 0, sizeof(cp));
+
+ create_ext_inquiry_response(index, cp.data);
+
+ if (memcmp(cp.data, dev->eir, sizeof(cp.data)) == 0)
+ return;
+
+ memcpy(dev->eir, cp.data, sizeof(cp.data));
+
+ if (hci_send_cmd(dev->sk, OGF_HOST_CTL,
+ OCF_WRITE_EXT_INQUIRY_RESPONSE,
+ WRITE_EXT_INQUIRY_RESPONSE_CP_SIZE, &cp) < 0)
+ error("Unable to write EIR data: %s (%d)",
+ strerror(errno), errno);
+}
+
+static void update_name(int index, const char *name)
+{
+ struct btd_adapter *adapter;
+
+ adapter = manager_find_adapter_by_id(index);
+ if (adapter)
+ adapter_update_local_name(adapter, name);
+
+ update_ext_inquiry_response(index);
+}
+
+static void read_local_name_complete(int index, read_local_name_rp *rp)
+{
+ struct dev_info *dev = &devs[index];
+
+ DBG("hci%d status %u", index, rp->status);
+
+ if (rp->status)
+ return;
+
+ memcpy(dev->name, rp->name, 248);
+
+ if (!dev->pending) {
+ update_name(index, (char *) rp->name);
+ return;
+ }
+
+ hci_clear_bit(PENDING_NAME, &dev->pending);
+
+ DBG("Got name for hci%d", index);
+
+ /* Even though it shouldn't happen (assuming the kernel behaves
+ * properly) it seems like we might miss the very first
+ * initialization commands that the kernel sends. So check for
+ * it here (since read_local_name is one of the last init
+ * commands) and resend the first ones if we haven't seen
+ * their results yet */
+
+ if (hci_test_bit(PENDING_FEATURES, &dev->pending))
+ hci_send_cmd(dev->sk, OGF_INFO_PARAM,
+ OCF_READ_LOCAL_FEATURES, 0, NULL);
+
+ if (hci_test_bit(PENDING_VERSION, &dev->pending))
+ hci_send_cmd(dev->sk, OGF_INFO_PARAM,
+ OCF_READ_LOCAL_VERSION, 0, NULL);
+
+ if (!dev->pending && dev->up)
+ init_adapter(index);
+}
+
+static void read_tx_power_complete(int index, void *ptr)
+{
+ struct dev_info *dev = &devs[index];
+
+ read_inq_response_tx_power_level_rp *rp = ptr;
+
+ DBG("hci%d status %u", index, rp->status);
+
+ if (rp->status)
+ return;
+
+ dev->tx_power = rp->level;
+ update_ext_inquiry_response(index);
+}
+
+static void read_simple_pairing_mode_complete(int index, void *ptr)
+{
+ struct dev_info *dev = &devs[index];
+ read_simple_pairing_mode_rp *rp = ptr;
+ struct btd_adapter *adapter;
+
+ DBG("hci%d status %u", index, rp->status);
+
+ if (rp->status)
+ return;
+
+ dev->ssp_mode = rp->mode;
+ update_ext_inquiry_response(index);
+
+ adapter = manager_find_adapter(&dev->bdaddr);
+ if (!adapter) {
+ error("No matching adapter found");
+ return;
+ }
+
+ adapter_update_ssp_mode(adapter, rp->mode);
+}
+
+static void read_local_ext_features_complete(int index,
+ const read_local_ext_features_rp *rp)
+{
+ struct btd_adapter *adapter;
+
+ DBG("hci%d status %u", index, rp->status);
+
+ if (rp->status)
+ return;
+
+ adapter = manager_find_adapter_by_id(index);
+ if (!adapter) {
+ error("No matching adapter found");
+ return;
+ }
+
+ /* Local Extended feature page number is 1 */
+ if (rp->page_num != 1)
+ return;
+
+ btd_adapter_update_local_ext_features(adapter, rp->features);
+}
+
+static void read_bd_addr_complete(int index, read_bd_addr_rp *rp)
+{
+ struct dev_info *dev = &devs[index];
+
+ DBG("hci%d status %u", index, rp->status);
+
+ if (rp->status)
+ return;
+
+ bacpy(&dev->bdaddr, &rp->bdaddr);
+
+ if (!dev->pending)
+ return;
+
+ hci_clear_bit(PENDING_BDADDR, &dev->pending);
+
+ DBG("Got bdaddr for hci%d", index);
+
+ if (!dev->pending && dev->up)
+ init_adapter(index);
+}
+
+static inline void cmd_status(int index, void *ptr)
+{
+ struct dev_info *dev = &devs[index];
+ evt_cmd_status *evt = ptr;
+ uint16_t opcode = btohs(evt->opcode);
+
+ if (opcode == cmd_opcode_pack(OGF_LINK_CTL, OCF_INQUIRY))
+ start_inquiry(&dev->bdaddr, evt->status, FALSE);
+}
+
+static void read_scan_complete(int index, uint8_t status, void *ptr)
+{
+ struct btd_adapter *adapter;
+ read_scan_enable_rp *rp = ptr;
+
+ DBG("hci%d status %u", index, status);
+
+ adapter = manager_find_adapter_by_id(index);
+ if (!adapter) {
+ error("Unable to find matching adapter");
+ return;
+ }
+
+ adapter_mode_changed(adapter, rp->enable);
+}
+
+static int write_class(int index, uint32_t class)
+{
+ struct dev_info *dev = &devs[index];
+ write_class_of_dev_cp cp;
+
+ DBG("hci%d class 0x%06x", index, class);
+
+ memcpy(cp.dev_class, &class, 3);
+
+ if (hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_WRITE_CLASS_OF_DEV,
+ WRITE_CLASS_OF_DEV_CP_SIZE, &cp) < 0)
+ return -errno;
+
+ dev->pending_cod = class;
+
+ return 0;
+}
+
+/* Limited Discoverable bit mask in CoD */
+#define LIMITED_BIT 0x002000
+
+static int hciops_set_limited_discoverable(int index, gboolean limited)
+{
+ struct dev_info *dev = &devs[index];
+ int num = (limited ? 2 : 1);
+ uint8_t lap[] = { 0x33, 0x8b, 0x9e, 0x00, 0x8b, 0x9e };
+ write_current_iac_lap_cp cp;
+
+ DBG("hci%d limited %d", index, limited);
+
+ /* Check if limited bit needs to be set/reset */
+ if (limited)
+ dev->wanted_cod |= LIMITED_BIT;
+ else
+ dev->wanted_cod &= ~LIMITED_BIT;
+
+ /* If we dont need the toggling, save an unnecessary CoD write */
+ if (dev->pending_cod || dev->wanted_cod == dev->current_cod)
+ return 0;
+
+ /*
+ * 1: giac
+ * 2: giac + liac
+ */
+ memset(&cp, 0, sizeof(cp));
+ cp.num_current_iac = num;
+ memcpy(&cp.lap, lap, num * 3);
+
+ if (hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_WRITE_CURRENT_IAC_LAP,
+ (num * 3 + 1), &cp) < 0)
+ return -errno;
+
+ return write_class(index, dev->wanted_cod);
+}
+
+static void write_class_complete(int index, uint8_t status)
+{
+ struct dev_info *dev = &devs[index];
+ struct btd_adapter *adapter;
+
+ if (status)
+ return;
+
+ if (dev->pending_cod == 0)
+ return;
+
+ dev->current_cod = dev->pending_cod;
+ dev->pending_cod = 0;
+
+ adapter = manager_find_adapter(&dev->bdaddr);
+ if (adapter)
+ btd_adapter_class_changed(adapter, dev->current_cod);
+
+ update_ext_inquiry_response(index);
+
+ if (dev->wanted_cod == dev->current_cod)
+ return;
+
+ if (dev->wanted_cod & LIMITED_BIT &&
+ !(dev->current_cod & LIMITED_BIT))
+ hciops_set_limited_discoverable(index, TRUE);
+ else if (!(dev->wanted_cod & LIMITED_BIT) &&
+ (dev->current_cod & LIMITED_BIT))
+ hciops_set_limited_discoverable(index, FALSE);
+ else
+ write_class(index, dev->wanted_cod);
+}
+
+static void read_local_oob_data_complete(int index, uint8_t status,
+ read_local_oob_data_rp *rp)
+{
+ struct btd_adapter *adapter = manager_find_adapter_by_id(index);
+
+ if (!adapter)
+ return;
+
+ if (status)
+ oob_read_local_data_complete(adapter, NULL, NULL);
+ else
+ oob_read_local_data_complete(adapter, rp->hash, rp->randomizer);
+}
+
+static inline void cmd_complete(int index, void *ptr)
+{
+ struct dev_info *dev = &devs[index];
+ evt_cmd_complete *evt = ptr;
+ uint16_t opcode = btohs(evt->opcode);
+ uint8_t status = *((uint8_t *) ptr + EVT_CMD_COMPLETE_SIZE);
+
+ switch (opcode) {
+ case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_VERSION):
+ ptr += sizeof(evt_cmd_complete);
+ read_local_version_complete(index, ptr);
+ break;
+ case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_FEATURES):
+ ptr += sizeof(evt_cmd_complete);
+ read_local_features_complete(index, ptr);
+ break;
+ case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_EXT_FEATURES):
+ ptr += sizeof(evt_cmd_complete);
+ read_local_ext_features_complete(index, ptr);
+ break;
+ case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_BD_ADDR):
+ ptr += sizeof(evt_cmd_complete);
+ read_bd_addr_complete(index, ptr);
+ break;
+ case cmd_opcode_pack(OGF_LINK_CTL, OCF_PERIODIC_INQUIRY):
+ start_inquiry(&dev->bdaddr, status, TRUE);
+ break;
+ case cmd_opcode_pack(OGF_LINK_CTL, OCF_EXIT_PERIODIC_INQUIRY):
+ inquiry_complete(&dev->bdaddr, status, TRUE);
+ break;
+ case cmd_opcode_pack(OGF_LINK_CTL, OCF_INQUIRY_CANCEL):
+ inquiry_complete(&dev->bdaddr, status, FALSE);
+ break;
+ case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_LE_HOST_SUPPORTED):
+ write_le_host_complete(index, status);
+ break;
+ case cmd_opcode_pack(OGF_LE_CTL, OCF_LE_SET_SCAN_ENABLE):
+ btd_event_le_set_scan_enable_complete(&dev->bdaddr, status);
+ break;
+ case cmd_opcode_pack(OGF_HOST_CTL, OCF_CHANGE_LOCAL_NAME):
+ if (!status)
+ hci_send_cmd(dev->sk, OGF_HOST_CTL,
+ OCF_READ_LOCAL_NAME, 0, 0);
+ break;
+ case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_SCAN_ENABLE):
+ hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_READ_SCAN_ENABLE,
+ 0, NULL);
+ break;
+ case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_SCAN_ENABLE):
+ ptr += sizeof(evt_cmd_complete);
+ read_scan_complete(index, status, ptr);
+ break;
+ case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_CLASS_OF_DEV):
+ write_class_complete(index, status);
+ break;
+ case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_SIMPLE_PAIRING_MODE):
+ if (!status)
+ hci_send_cmd(dev->sk, OGF_HOST_CTL,
+ OCF_READ_SIMPLE_PAIRING_MODE, 0, NULL);
+ break;
+ case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_SIMPLE_PAIRING_MODE):
+ ptr += sizeof(evt_cmd_complete);
+ read_simple_pairing_mode_complete(index, ptr);
+ break;
+ case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_LOCAL_NAME):
+ ptr += sizeof(evt_cmd_complete);
+ read_local_name_complete(index, ptr);
+ break;
+ case cmd_opcode_pack(OGF_HOST_CTL,
+ OCF_READ_INQ_RESPONSE_TX_POWER_LEVEL):
+ ptr += sizeof(evt_cmd_complete);
+ read_tx_power_complete(index, ptr);
+ break;
+ case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_LOCAL_OOB_DATA):
+ ptr += sizeof(evt_cmd_complete);
+ read_local_oob_data_complete(index, status, ptr);
+ break;
+ };
+}
+
+static inline void remote_name_information(int index, void *ptr)
+{
+ struct dev_info *dev = &devs[index];
+ evt_remote_name_req_complete *evt = ptr;
+ char name[MAX_NAME_LENGTH + 1];
+
+ DBG("hci%d status %u", index, evt->status);
+
+ memset(name, 0, sizeof(name));
+
+ if (!evt->status)
+ memcpy(name, evt->name, MAX_NAME_LENGTH);
+
+ btd_event_remote_name(&dev->bdaddr, &evt->bdaddr, evt->status, name);
+}
+
+static inline void remote_version_information(int index, void *ptr)
+{
+ struct dev_info *dev = &devs[index];
+ evt_read_remote_version_complete *evt = ptr;
+ struct bt_conn *conn;
+
+ DBG("hci%d status %u", index, evt->status);
+
+ if (evt->status)
+ return;
+
+ conn = find_conn_by_handle(dev, btohs(evt->handle));
+ if (conn == NULL)
+ return;
+
+ write_version_info(&dev->bdaddr, &conn->bdaddr,
+ btohs(evt->manufacturer), evt->lmp_ver,
+ btohs(evt->lmp_subver));
+}
+
+static inline void inquiry_result(int index, int plen, void *ptr)
+{
+ struct dev_info *dev = &devs[index];
+ uint8_t num = *(uint8_t *) ptr++;
+ int i;
+
+ for (i = 0; i < num; i++) {
+ inquiry_info *info = ptr;
+ uint32_t class = info->dev_class[0] |
+ (info->dev_class[1] << 8) |
+ (info->dev_class[2] << 16);
+
+ btd_event_device_found(&dev->bdaddr, &info->bdaddr, class,
+ 0, NULL);
+ ptr += INQUIRY_INFO_SIZE;
+ }
+}
+
+static inline void inquiry_result_with_rssi(int index, int plen, void *ptr)
+{
+ struct dev_info *dev = &devs[index];
+ uint8_t num = *(uint8_t *) ptr++;
+ int i;
+
+ if (!num)
+ return;
+
+ if ((plen - 1) / num == INQUIRY_INFO_WITH_RSSI_AND_PSCAN_MODE_SIZE) {
+ for (i = 0; i < num; i++) {
+ inquiry_info_with_rssi_and_pscan_mode *info = ptr;
+ uint32_t class = info->dev_class[0]
+ | (info->dev_class[1] << 8)
+ | (info->dev_class[2] << 16);
+
+ btd_event_device_found(&dev->bdaddr, &info->bdaddr,
+ class, info->rssi, NULL);
+ ptr += INQUIRY_INFO_WITH_RSSI_AND_PSCAN_MODE_SIZE;
+ }
+ } else {
+ for (i = 0; i < num; i++) {
+ inquiry_info_with_rssi *info = ptr;
+ uint32_t class = info->dev_class[0]
+ | (info->dev_class[1] << 8)
+ | (info->dev_class[2] << 16);
+
+ btd_event_device_found(&dev->bdaddr, &info->bdaddr,
+ class, info->rssi, NULL);
+ ptr += INQUIRY_INFO_WITH_RSSI_SIZE;
+ }
+ }
+}
+
+static inline void extended_inquiry_result(int index, int plen, void *ptr)
+{
+ struct dev_info *dev = &devs[index];
+ uint8_t num = *(uint8_t *) ptr++;
+ int i;
+
+ for (i = 0; i < num; i++) {
+ extended_inquiry_info *info = ptr;
+ uint32_t class = info->dev_class[0]
+ | (info->dev_class[1] << 8)
+ | (info->dev_class[2] << 16);
+
+ btd_event_device_found(&dev->bdaddr, &info->bdaddr, class,
+ info->rssi, info->data);
+ ptr += EXTENDED_INQUIRY_INFO_SIZE;
+ }
+}
+
+static inline void remote_features_information(int index, void *ptr)
+{
+ struct dev_info *dev = &devs[index];
+ evt_read_remote_features_complete *evt = ptr;
+ struct bt_conn *conn;
+
+ DBG("hci%d status %u", index, evt->status);
+
+ if (evt->status)
+ return;
+
+ conn = find_conn_by_handle(dev, btohs(evt->handle));
+ if (conn == NULL)
+ return;
+
+ write_features_info(&dev->bdaddr, &conn->bdaddr, evt->features, NULL);
+}
+
+struct remote_version_req {
+ int index;
+ uint16_t handle;
+};
+
+static gboolean __get_remote_version(gpointer user_data)
+{
+ struct remote_version_req *req = user_data;
+ struct dev_info *dev = &devs[req->index];
+ read_remote_version_cp cp;
+
+ DBG("hci%d handle %u", req->index, req->handle);
+
+ memset(&cp, 0, sizeof(cp));
+ cp.handle = htobs(req->handle);
+
+ hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_READ_REMOTE_VERSION,
+ READ_REMOTE_VERSION_CP_SIZE, &cp);
+
+ return FALSE;
+}
+
+static void get_remote_version(int index, uint16_t handle)
+{
+ struct remote_version_req *req;
+
+ req = g_new0(struct remote_version_req, 1);
+ req->handle = handle;
+ req->index = index;
+
+ g_timeout_add_seconds_full(G_PRIORITY_DEFAULT, 1, __get_remote_version,
+ req, g_free);
+}
+
+static void conn_free(struct bt_conn *conn)
+{
+ if (conn->io != NULL) {
+ g_io_channel_shutdown(conn->io, TRUE, NULL);
+ g_io_channel_unref(conn->io);
+ }
+
+ g_free(conn);
+}
+
+static inline void conn_failed(int index, bdaddr_t *bdaddr, uint8_t status)
+{
+ struct dev_info *dev = &devs[index];
+ struct bt_conn *conn;
+
+ btd_event_conn_failed(&dev->bdaddr, bdaddr, status);
+
+ conn = find_connection(dev, bdaddr);
+ if (conn == NULL)
+ return;
+
+ bonding_complete(dev, conn, status);
+
+ dev->connections = g_slist_remove(dev->connections, conn);
+ conn_free(conn);
+}
+
+static inline void conn_complete(int index, void *ptr)
+{
+ struct dev_info *dev = &devs[index];
+ evt_conn_complete *evt = ptr;
+ char filename[PATH_MAX];
+ char local_addr[18], peer_addr[18], *str;
+ struct bt_conn *conn;
+
+ if (evt->link_type != ACL_LINK)
+ return;
+
+ DBG("status 0x%02x", evt->status);
+
+ if (evt->status != 0) {
+ conn_failed(index, &evt->bdaddr, evt->status);
+ return;
+ }
+
+ conn = get_connection(dev, &evt->bdaddr);
+ conn->handle = btohs(evt->handle);
+
+ btd_event_conn_complete(&dev->bdaddr, &evt->bdaddr);
+
+ if (conn->secmode3)
+ bonding_complete(dev, conn, 0);
+
+ /* check if the remote version needs be requested */
+ ba2str(&dev->bdaddr, local_addr);
+ ba2str(&evt->bdaddr, peer_addr);
+
+ create_name(filename, sizeof(filename), STORAGEDIR, local_addr,
+ "manufacturers");
+
+ str = textfile_get(filename, peer_addr);
+ if (!str)
+ get_remote_version(index, btohs(evt->handle));
+ else
+ free(str);
+}
+
+static inline void le_conn_complete(int index, void *ptr)
+{
+ struct dev_info *dev = &devs[index];
+ evt_le_connection_complete *evt = ptr;
+ char filename[PATH_MAX];
+ char local_addr[18], peer_addr[18], *str;
+ struct bt_conn *conn;
+
+ if (evt->status) {
+ btd_event_conn_failed(&dev->bdaddr, &evt->peer_bdaddr,
+ evt->status);
+ return;
+ }
+
+ conn = get_connection(dev, &evt->peer_bdaddr);
+ conn->handle = btohs(evt->handle);
+
+ btd_event_conn_complete(&dev->bdaddr, &evt->peer_bdaddr);
+
+ /* check if the remote version needs be requested */
+ ba2str(&dev->bdaddr, local_addr);
+ ba2str(&evt->peer_bdaddr, peer_addr);
+
+ create_name(filename, sizeof(filename), STORAGEDIR, local_addr,
+ "manufacturers");
+
+ str = textfile_get(filename, peer_addr);
+ if (!str)
+ get_remote_version(index, btohs(evt->handle));
+ else
+ free(str);
+}
+
+static inline void disconn_complete(int index, void *ptr)
+{
+ struct dev_info *dev = &devs[index];
+ evt_disconn_complete *evt = ptr;
+ struct bt_conn *conn;
+
+ DBG("handle %u status 0x%02x", btohs(evt->handle), evt->status);
+
+ if (evt->status != 0)
+ return;
+
+ conn = find_conn_by_handle(dev, btohs(evt->handle));
+ if (conn == NULL)
+ return;
+
+ dev->connections = g_slist_remove(dev->connections, conn);
+
+ btd_event_disconn_complete(&dev->bdaddr, &conn->bdaddr);
+
+ conn_free(conn);
+}
+
+static inline void auth_complete(int index, void *ptr)
+{
+ struct dev_info *dev = &devs[index];
+ evt_auth_complete *evt = ptr;
+ struct bt_conn *conn;
+
+ DBG("hci%d status %u", index, evt->status);
+
+ conn = find_conn_by_handle(dev, btohs(evt->handle));
+ if (conn == NULL)
+ return;
+
+ bonding_complete(dev, conn, evt->status);
+}
+
+static inline void simple_pairing_complete(int index, void *ptr)
+{
+ struct dev_info *dev = &devs[index];
+ evt_simple_pairing_complete *evt = ptr;
+
+ DBG("hci%d status %u", index, evt->status);
+
+ btd_event_simple_pairing_complete(&dev->bdaddr, &evt->bdaddr,
+ evt->status);
+}
+
+static inline void conn_request(int index, void *ptr)
+{
+ struct dev_info *dev = &devs[index];
+ evt_conn_request *evt = ptr;
+ uint32_t class = evt->dev_class[0] | (evt->dev_class[1] << 8)
+ | (evt->dev_class[2] << 16);
+
+ btd_event_remote_class(&dev->bdaddr, &evt->bdaddr, class);
+}
+
+static inline void le_advertising_report(int index, evt_le_meta_event *meta)
+{
+ struct dev_info *dev = &devs[index];
+ le_advertising_info *info;
+ uint8_t num_reports;
+ const uint8_t RSSI_SIZE = 1;
+
+ num_reports = meta->data[0];
+
+ info = (le_advertising_info *) &meta->data[1];
+ btd_event_advertising_report(&dev->bdaddr, info);
+ num_reports--;
+
+ while (num_reports--) {
+ info = (le_advertising_info *) (info->data + info->length +
+ RSSI_SIZE);
+ btd_event_advertising_report(&dev->bdaddr, info);
+ }
+}
+
+static inline void le_metaevent(int index, void *ptr)
+{
+ evt_le_meta_event *meta = ptr;
+
+ DBG("hci%d LE Meta Event %u", index, meta->subevent);
+
+ switch (meta->subevent) {
+ case EVT_LE_ADVERTISING_REPORT:
+ le_advertising_report(index, meta);
+ break;
+
+ case EVT_LE_CONN_COMPLETE:
+ le_conn_complete(index, meta->data);
+ break;
+ }
+}
+
+static void stop_hci_dev(int index)
+{
+ struct dev_info *dev = &devs[index];
+
+ if (dev->sk < 0)
+ return;
+
+ info("Stopping hci%d event socket", index);
+
+ if (dev->watch_id > 0)
+ g_source_remove(dev->watch_id);
+
+ if (dev->io != NULL)
+ g_io_channel_unref(dev->io);
+
+ hci_close_dev(dev->sk);
+
+ g_slist_foreach(dev->keys, (GFunc) g_free, NULL);
+ g_slist_free(dev->keys);
+
+ g_slist_foreach(dev->uuids, (GFunc) g_free, NULL);
+ g_slist_free(dev->uuids);
+
+ g_slist_foreach(dev->connections, (GFunc) conn_free, NULL);
+ g_slist_free(dev->connections);
+
+ init_dev_info(index, -1, dev->registered, dev->already_up);
+}
+
+static gboolean io_security_event(GIOChannel *chan, GIOCondition cond,
+ gpointer data)
+{
+ unsigned char buf[HCI_MAX_EVENT_SIZE], *ptr = buf;
+ int type, index = GPOINTER_TO_INT(data);
+ struct dev_info *dev = &devs[index];
+ struct hci_dev_info di;
+ ssize_t len;
+ hci_event_hdr *eh;
+ evt_cmd_status *evt;
+ int fd;
+
+ if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) {
+ stop_hci_dev(index);
+ return FALSE;
+ }
+
+ fd = g_io_channel_unix_get_fd(chan);
+
+ len = read(fd, buf, sizeof(buf));
+ if (len < 0) {
+ if (errno == EAGAIN)
+ return TRUE;
+ stop_hci_dev(index);
+ return FALSE;
+ }
+
+ type = *ptr++;
+
+ if (type != HCI_EVENT_PKT)
+ return TRUE;
+
+ eh = (hci_event_hdr *) ptr;
+ ptr += HCI_EVENT_HDR_SIZE;
+
+ memset(&di, 0, sizeof(di));
+ if (hci_devinfo(index, &di) == 0) {
+ bacpy(&dev->bdaddr, &di.bdaddr);
+
+ if (ignore_device(&di))
+ return TRUE;
+ }
+
+ switch (eh->evt) {
+ case EVT_CMD_STATUS:
+ cmd_status(index, ptr);
+ break;
+
+ case EVT_CMD_COMPLETE:
+ cmd_complete(index, ptr);
+ break;
+
+ case EVT_REMOTE_NAME_REQ_COMPLETE:
+ remote_name_information(index, ptr);
+ break;
+
+ case EVT_READ_REMOTE_VERSION_COMPLETE:
+ remote_version_information(index, ptr);
+ break;
+
+ case EVT_READ_REMOTE_FEATURES_COMPLETE:
+ remote_features_information(index, ptr);
+ break;
+
+ case EVT_REMOTE_HOST_FEATURES_NOTIFY:
+ remote_features_notify(index, ptr);
+ break;
+
+ case EVT_INQUIRY_COMPLETE:
+ evt = (evt_cmd_status *) ptr;
+ inquiry_complete(&dev->bdaddr, evt->status, FALSE);
+ break;
+
+ case EVT_INQUIRY_RESULT:
+ inquiry_result(index, eh->plen, ptr);
+ break;
+
+ case EVT_INQUIRY_RESULT_WITH_RSSI:
+ inquiry_result_with_rssi(index, eh->plen, ptr);
+ break;
+
+ case EVT_EXTENDED_INQUIRY_RESULT:
+ extended_inquiry_result(index, eh->plen, ptr);
+ break;
+
+ case EVT_CONN_COMPLETE:
+ conn_complete(index, ptr);
+ break;
+
+ case EVT_DISCONN_COMPLETE:
+ disconn_complete(index, ptr);
+ break;
+
+ case EVT_AUTH_COMPLETE:
+ auth_complete(index, ptr);
+ break;
+
+ case EVT_SIMPLE_PAIRING_COMPLETE:
+ simple_pairing_complete(index, ptr);
+ break;
+
+ case EVT_CONN_REQUEST:
+ conn_request(index, ptr);
+ break;
+ case EVT_LE_META_EVENT:
+ le_metaevent(index, ptr);
+ break;
+ case EVT_PIN_CODE_REQ:
+ pin_code_request(index, (bdaddr_t *) ptr);
+ break;
+
+ case EVT_LINK_KEY_REQ:
+ link_key_request(index, (bdaddr_t *) ptr);
+ break;
+
+ case EVT_LINK_KEY_NOTIFY:
+ link_key_notify(index, ptr);
+ break;
+
+ case EVT_RETURN_LINK_KEYS:
+ return_link_keys(index, ptr);
+ break;
+
+ case EVT_IO_CAPABILITY_REQUEST:
+ io_capa_request(index, ptr);
+ break;
+
+ case EVT_IO_CAPABILITY_RESPONSE:
+ io_capa_response(index, ptr);
+ break;
+
+ case EVT_USER_CONFIRM_REQUEST:
+ user_confirm_request(index, ptr);
+ break;
+
+ case EVT_USER_PASSKEY_REQUEST:
+ user_passkey_request(index, ptr);
+ break;
+
+ case EVT_USER_PASSKEY_NOTIFY:
+ user_passkey_notify(index, ptr);
+ break;
+
+ case EVT_REMOTE_OOB_DATA_REQUEST:
+ remote_oob_data_request(index, (bdaddr_t *) ptr);
+ break;
+ }
+
+ return TRUE;
+}
+
+static void start_hci_dev(int index)
+{
+ struct dev_info *dev = &devs[index];
+ GIOChannel *chan = dev->io;
+ GIOCondition cond;
+ struct hci_filter flt;
+
+ if (chan)
+ return;
+
+ info("Listening for HCI events on hci%d", index);
+
+ /* Set filter */
+ hci_filter_clear(&flt);
+ hci_filter_set_ptype(HCI_EVENT_PKT, &flt);
+ hci_filter_set_event(EVT_CMD_STATUS, &flt);
+ hci_filter_set_event(EVT_CMD_COMPLETE, &flt);
+ hci_filter_set_event(EVT_PIN_CODE_REQ, &flt);
+ hci_filter_set_event(EVT_LINK_KEY_REQ, &flt);
+ hci_filter_set_event(EVT_LINK_KEY_NOTIFY, &flt);
+ hci_filter_set_event(EVT_RETURN_LINK_KEYS, &flt);
+ hci_filter_set_event(EVT_IO_CAPABILITY_REQUEST, &flt);
+ hci_filter_set_event(EVT_IO_CAPABILITY_RESPONSE, &flt);
+ hci_filter_set_event(EVT_USER_CONFIRM_REQUEST, &flt);
+ hci_filter_set_event(EVT_USER_PASSKEY_REQUEST, &flt);
+ hci_filter_set_event(EVT_REMOTE_OOB_DATA_REQUEST, &flt);
+ hci_filter_set_event(EVT_USER_PASSKEY_NOTIFY, &flt);
+ hci_filter_set_event(EVT_KEYPRESS_NOTIFY, &flt);
+ hci_filter_set_event(EVT_SIMPLE_PAIRING_COMPLETE, &flt);
+ hci_filter_set_event(EVT_AUTH_COMPLETE, &flt);
+ hci_filter_set_event(EVT_REMOTE_NAME_REQ_COMPLETE, &flt);
+ hci_filter_set_event(EVT_READ_REMOTE_VERSION_COMPLETE, &flt);
+ hci_filter_set_event(EVT_READ_REMOTE_FEATURES_COMPLETE, &flt);
+ hci_filter_set_event(EVT_REMOTE_HOST_FEATURES_NOTIFY, &flt);
+ hci_filter_set_event(EVT_INQUIRY_COMPLETE, &flt);
+ hci_filter_set_event(EVT_INQUIRY_RESULT, &flt);
+ hci_filter_set_event(EVT_INQUIRY_RESULT_WITH_RSSI, &flt);
+ hci_filter_set_event(EVT_EXTENDED_INQUIRY_RESULT, &flt);
+ hci_filter_set_event(EVT_CONN_REQUEST, &flt);
+ hci_filter_set_event(EVT_CONN_COMPLETE, &flt);
+ hci_filter_set_event(EVT_DISCONN_COMPLETE, &flt);
+ hci_filter_set_event(EVT_LE_META_EVENT, &flt);
+ if (setsockopt(dev->sk, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
+ error("Can't set filter on hci%d: %s (%d)",
+ index, strerror(errno), errno);
+ return;
+ }
+
+ chan = g_io_channel_unix_new(dev->sk);
+ cond = G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR;
+ dev->watch_id = g_io_add_watch_full(chan, G_PRIORITY_LOW, cond,
+ io_security_event,
+ GINT_TO_POINTER(index), NULL);
+ dev->io = chan;
+ dev->pin_length = 0;
+
+}
+
+/* End of HCI event callbacks */
+
+static gboolean child_exit(GIOChannel *io, GIOCondition cond, void *user_data)
+{
+ int status, fd = g_io_channel_unix_get_fd(io);
+ pid_t child_pid;
+
+ if (read(fd, &child_pid, sizeof(child_pid)) != sizeof(child_pid)) {
+ error("child_exit: unable to read child pid from pipe");
+ return TRUE;
+ }
+
+ if (waitpid(child_pid, &status, 0) != child_pid)
+ error("waitpid(%d) failed", child_pid);
+ else
+ DBG("child %d exited", child_pid);
+
+ return TRUE;
+}
+
+static void at_child_exit(void)
+{
+ pid_t pid = getpid();
+
+ if (write(child_pipe[1], &pid, sizeof(pid)) != sizeof(pid))
+ error("unable to write to child pipe");
+}
+
+static void device_devup_setup(int index)
+{
+ struct dev_info *dev = &devs[index];
+ struct hci_dev_info di;
+ read_stored_link_key_cp cp;
+
+ DBG("hci%d", index);
+
+ if (hci_devinfo(index, &di) < 0)
+ return;
+
+ if (ignore_device(&di))
+ return;
+
+ bacpy(&dev->bdaddr, &di.bdaddr);
+ memcpy(dev->features, di.features, 8);
+
+ /* Set page timeout */
+ if ((main_opts.flags & (1 << HCID_SET_PAGETO))) {
+ write_page_timeout_cp cp;
+
+ cp.timeout = htobs(main_opts.pageto);
+ hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_WRITE_PAGE_TIMEOUT,
+ WRITE_PAGE_TIMEOUT_CP_SIZE, &cp);
+ }
+
+ bacpy(&cp.bdaddr, BDADDR_ANY);
+ cp.read_all = 1;
+ hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_READ_STORED_LINK_KEY,
+ READ_STORED_LINK_KEY_CP_SIZE, &cp);
+
+ if (!dev->pending)
+ init_adapter(index);
+}
+
+static void init_pending(int index)
+{
+ struct dev_info *dev = &devs[index];
+
+ hci_set_bit(PENDING_BDADDR, &dev->pending);
+ hci_set_bit(PENDING_VERSION, &dev->pending);
+ hci_set_bit(PENDING_FEATURES, &dev->pending);
+ hci_set_bit(PENDING_NAME, &dev->pending);
+}
+
+static struct dev_info *init_device(int index, gboolean already_up)
+{
+ struct dev_info *dev;
+ struct hci_dev_req dr;
+ int dd;
+ pid_t pid;
+
+ DBG("hci%d", index);
+
+ dd = hci_open_dev(index);
+ if (dd < 0) {
+ error("Unable to open hci%d: %s (%d)", index,
+ strerror(errno), errno);
+ return NULL;
+ }
+
+ if (index > max_dev) {
+ max_dev = index;
+ devs = g_realloc(devs, sizeof(devs[0]) * (max_dev + 1));
+ }
+
+ dev = init_dev_info(index, dd, FALSE, already_up);
+ init_pending(index);
+ start_hci_dev(index);
+
+ /* Avoid forking if nothing else has to be done */
+ if (already_up)
+ return dev;
+
+ /* Do initialization in the separate process */
+ pid = fork();
+ switch (pid) {
+ case 0:
+ atexit(at_child_exit);
+ break;
+ case -1:
+ error("Fork failed. Can't init device hci%d: %s (%d)",
+ index, strerror(errno), errno);
+ default:
+ DBG("child %d forked", pid);
+ return dev;
+ }
+
+ memset(&dr, 0, sizeof(dr));
+ dr.dev_id = index;
+
+ /* Set link mode */
+ dr.dev_opt = main_opts.link_mode;
+ if (ioctl(dd, HCISETLINKMODE, (unsigned long) &dr) < 0)
+ error("Can't set link mode on hci%d: %s (%d)",
+ index, strerror(errno), errno);
+
+ /* Start HCI device */
+ if (ioctl(dd, HCIDEVUP, index) < 0 && errno != EALREADY) {
+ error("Can't init device hci%d: %s (%d)",
+ index, strerror(errno), errno);
+ goto fail;
+ }
+
+ hci_close_dev(dd);
+ exit(0);
+
+fail:
+ hci_close_dev(dd);
+ exit(1);
+}
+
+static void init_conn_list(int index)
+{
+ struct dev_info *dev = &devs[index];
+ struct hci_conn_list_req *cl;
+ struct hci_conn_info *ci;
+ int err, i;
+
+ DBG("hci%d", index);
+
+ cl = g_malloc0(10 * sizeof(*ci) + sizeof(*cl));
+
+ cl->dev_id = index;
+ cl->conn_num = 10;
+ ci = cl->conn_info;
+
+ if (ioctl(dev->sk, HCIGETCONNLIST, cl) < 0) {
+ error("Unable to get connection list: %s (%d)",
+ strerror(errno), errno);
+ goto failed;
+ }
+
+ for (i = 0; i < cl->conn_num; i++, ci++) {
+ struct bt_conn *conn;
+
+ if (ci->type != ACL_LINK)
+ continue;
+
+ conn = get_connection(dev, &ci->bdaddr);
+ conn->handle = ci->handle;
+ }
+
+ err = 0;
+
+failed:
+ g_free(cl);
+}
+
+static void device_event(int event, int index)
+{
+ switch (event) {
+ case HCI_DEV_REG:
+ info("HCI dev %d registered", index);
+ init_device(index, FALSE);
+ break;
+
+ case HCI_DEV_UNREG:
+ info("HCI dev %d unregistered", index);
+ stop_hci_dev(index);
+ if (devs[index].registered)
+ btd_manager_unregister_adapter(index);
+ break;
+
+ case HCI_DEV_UP:
+ info("HCI dev %d up", index);
+ devs[index].up = TRUE;
+ device_devup_setup(index);
+ break;
+
+ case HCI_DEV_DOWN:
+ info("HCI dev %d down", index);
+ devs[index].up = FALSE;
+ devs[index].pending_cod = 0;
+ devs[index].cache_enable = TRUE;
+ if (!devs[index].pending) {
+ struct btd_adapter *adapter;
+
+ adapter = manager_find_adapter_by_id(index);
+ if (adapter)
+ btd_adapter_stop(adapter);
+
+ init_pending(index);
+ }
+ break;
+ }
+}
+
+static gboolean init_known_adapters(gpointer user_data)
+{
+ struct hci_dev_list_req *dl;
+ struct hci_dev_req *dr;
+ int i, err, ctl = GPOINTER_TO_INT(user_data);
+ size_t req_size;
+
+ DBG("");
+
+ req_size = HCI_MAX_DEV * sizeof(struct hci_dev_req) + sizeof(uint16_t);
+
+ dl = g_try_malloc0(req_size);
+ if (!dl) {
+ error("Can't allocate devlist buffer");
+ return FALSE;
+ }
+
+ dl->dev_num = HCI_MAX_DEV;
+ dr = dl->dev_req;
+
+ if (ioctl(ctl, HCIGETDEVLIST, dl) < 0) {
+ err = -errno;
+ error("Can't get device list: %s (%d)", strerror(-err), -err);
+ g_free(dl);
+ return FALSE;
+ }
+
+ for (i = 0; i < dl->dev_num; i++, dr++) {
+ struct dev_info *dev;
+ gboolean already_up;
+
+ already_up = hci_test_bit(HCI_UP, &dr->dev_opt);
+
+ dev = init_device(dr->dev_id, already_up);
+ if (dev == NULL)
+ continue;
+
+ if (!dev->already_up)
+ continue;
+
+ init_conn_list(dr->dev_id);
+
+ dev->pending = 0;
+ hci_set_bit(PENDING_VERSION, &dev->pending);
+ hci_send_cmd(dev->sk, OGF_INFO_PARAM,
+ OCF_READ_LOCAL_VERSION, 0, NULL);
+ device_event(HCI_DEV_UP, dr->dev_id);
+ }
+
+ g_free(dl);
+
+ return FALSE;
+}
+
+static gboolean io_stack_event(GIOChannel *chan, GIOCondition cond,
+ gpointer data)
+{
+ unsigned char buf[HCI_MAX_FRAME_SIZE], *ptr;
+ evt_stack_internal *si;
+ evt_si_device *sd;
+ hci_event_hdr *eh;
+ int type, fd;
+ ssize_t len;
+
+ ptr = buf;
+
+ fd = g_io_channel_unix_get_fd(chan);
+
+ len = read(fd, buf, sizeof(buf));
+ if (len < 0) {
+ if (errno == EAGAIN)
+ return TRUE;
+
+ error("Read from control socket failed: %s (%d)",
+ strerror(errno), errno);
+ return FALSE;
+ }
+
+ type = *ptr++;
+
+ if (type != HCI_EVENT_PKT)
+ return TRUE;
+
+ eh = (hci_event_hdr *) ptr;
+ if (eh->evt != EVT_STACK_INTERNAL)
+ return TRUE;
+
+ ptr += HCI_EVENT_HDR_SIZE;
+
+ si = (evt_stack_internal *) ptr;
+ switch (si->type) {
+ case EVT_SI_DEVICE:
+ sd = (void *) &si->data;
+ device_event(sd->event, sd->dev_id);
+ break;
+ }
+
+ return TRUE;
+}
+
+static int hciops_setup(void)
+{
+ struct sockaddr_hci addr;
+ struct hci_filter flt;
+ GIOChannel *ctl_io, *child_io;
+ int sock, err;
+
+ DBG("");
+
+ if (child_pipe[0] != -1)
+ return -EALREADY;
+
+ if (pipe(child_pipe) < 0) {
+ err = -errno;
+ error("pipe(): %s (%d)", strerror(-err), -err);
+ return err;
+ }
+
+ child_io = g_io_channel_unix_new(child_pipe[0]);
+ g_io_channel_set_close_on_unref(child_io, TRUE);
+ child_io_id = g_io_add_watch(child_io,
+ G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ child_exit, NULL);
+ g_io_channel_unref(child_io);
+
+ /* Create and bind HCI socket */
+ sock = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
+ if (sock < 0) {
+ err = -errno;
+ error("Can't open HCI socket: %s (%d)", strerror(-err),
+ -err);
+ return err;
+ }
+
+ /* Set filter */
+ hci_filter_clear(&flt);
+ hci_filter_set_ptype(HCI_EVENT_PKT, &flt);
+ hci_filter_set_event(EVT_STACK_INTERNAL, &flt);
+ if (setsockopt(sock, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
+ err = -errno;
+ error("Can't set filter: %s (%d)", strerror(-err), -err);
+ return err;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.hci_family = AF_BLUETOOTH;
+ addr.hci_dev = HCI_DEV_NONE;
+ if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ err = -errno;
+ error("Can't bind HCI socket: %s (%d)", strerror(-err), -err);
+ return err;
+ }
+
+ ctl_io = g_io_channel_unix_new(sock);
+ g_io_channel_set_close_on_unref(ctl_io, TRUE);
+
+ ctl_io_id = g_io_add_watch(ctl_io, G_IO_IN, io_stack_event, NULL);
+
+ g_io_channel_unref(ctl_io);
+
+ g_idle_add(init_known_adapters, GINT_TO_POINTER(sock));
+
+ return 0;
+}
+
+static void hciops_cleanup(void)
+{
+ int i;
+
+ DBG("");
+
+ for (i = 0; i <= max_dev; i++)
+ stop_hci_dev(i);
+
+ g_free(devs);
+ devs = NULL;
+ max_dev = -1;
+
+ if (child_io_id) {
+ g_source_remove(child_io_id);
+ child_io_id = 0;
+ }
+
+ if (ctl_io_id) {
+ g_source_remove(ctl_io_id);
+ ctl_io_id = 0;
+ }
+
+ if (child_pipe[0] >= 0) {
+ close(child_pipe[0]);
+ child_pipe[0] = -1;
+ }
+
+ if (child_pipe[1] >= 0) {
+ close(child_pipe[1]);
+ child_pipe[1] = -1;
+ }
+}
+
+static int hciops_set_powered(int index, gboolean powered)
+{
+ struct dev_info *dev = &devs[index];
+ int err;
+
+ DBG("hci%d powered %d", index, powered);
+
+ if (powered == FALSE)
+ return hciops_power_off(index);
+
+ if (ioctl(dev->sk, HCIDEVUP, index) == 0)
+ return 0;
+
+ if (errno == EALREADY)
+ return 0;
+
+ err = -errno;
+ error("Can't init device hci%d: %s (%d)",
+ index, strerror(-err), -err);
+
+ return err;
+}
+
+static int hciops_set_dev_class(int index, uint8_t major, uint8_t minor)
+{
+ struct dev_info *dev = &devs[index];
+ int err;
+
+ DBG("hci%d major %u minor %u", index, major, minor);
+
+ /* Update only the major and minor class bits keeping remaining bits
+ * intact*/
+ dev->wanted_cod &= 0xffe000;
+ dev->wanted_cod |= ((major & 0x1f) << 8) | minor;
+
+ if (dev->wanted_cod == dev->current_cod ||
+ dev->cache_enable || dev->pending_cod)
+ return 0;
+
+ DBG("Changing Major/Minor class to 0x%06x", dev->wanted_cod);
+
+ err = write_class(index, dev->wanted_cod);
+ if (err < 0)
+ error("Adapter class update failed: %s (%d)",
+ strerror(-err), -err);
+
+ return err;
+}
+
+static int hciops_start_inquiry(int index, uint8_t length, gboolean periodic)
+{
+ struct dev_info *dev = &devs[index];
+ uint8_t lap[3] = { 0x33, 0x8b, 0x9e };
+ int err;
+
+ DBG("hci%d length %u periodic %d", index, length, periodic);
+
+ if (periodic) {
+ periodic_inquiry_cp cp;
+
+ memset(&cp, 0, sizeof(cp));
+ memcpy(&cp.lap, lap, 3);
+ cp.max_period = htobs(24);
+ cp.min_period = htobs(16);
+ cp.length = length;
+ cp.num_rsp = 0x00;
+
+ err = hci_send_cmd(dev->sk, OGF_LINK_CTL,
+ OCF_PERIODIC_INQUIRY,
+ PERIODIC_INQUIRY_CP_SIZE, &cp);
+ } else {
+ inquiry_cp inq_cp;
+
+ memset(&inq_cp, 0, sizeof(inq_cp));
+ memcpy(&inq_cp.lap, lap, 3);
+ inq_cp.length = length;
+ inq_cp.num_rsp = 0x00;
+
+ err = hci_send_cmd(dev->sk, OGF_LINK_CTL,
+ OCF_INQUIRY, INQUIRY_CP_SIZE, &inq_cp);
+ }
+
+ if (err < 0)
+ err = -errno;
+
+ return err;
+}
+
+static int le_set_scan_enable(int index, uint8_t enable)
+{
+ struct dev_info *dev = &devs[index];
+ le_set_scan_enable_cp cp;
+
+ DBG("hci%d enable %u", index, enable);
+
+ memset(&cp, 0, sizeof(cp));
+ cp.enable = enable;
+ cp.filter_dup = 0;
+
+ if (hci_send_cmd(dev->sk, OGF_LE_CTL, OCF_LE_SET_SCAN_ENABLE,
+ LE_SET_SCAN_ENABLE_CP_SIZE, &cp) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int hciops_start_scanning(int index)
+{
+ struct dev_info *dev = &devs[index];
+ le_set_scan_parameters_cp cp;
+
+ DBG("hci%d", index);
+
+ memset(&cp, 0, sizeof(cp));
+ cp.type = 0x01; /* Active scanning */
+ /* The recommended value for scan interval and window is 11.25 msec.
+ * It is calculated by: time = n * 0.625 msec */
+ cp.interval = htobs(0x0012);
+ cp.window = htobs(0x0012);
+ cp.own_bdaddr_type = 0; /* Public address */
+ cp.filter = 0; /* Accept all adv packets */
+
+ if (hci_send_cmd(dev->sk, OGF_LE_CTL, OCF_LE_SET_SCAN_PARAMETERS,
+ LE_SET_SCAN_PARAMETERS_CP_SIZE, &cp) < 0)
+ return -errno;
+
+ return le_set_scan_enable(index, 1);
+}
+
+static int hciops_stop_scanning(int index)
+{
+ DBG("hci%d", index);
+
+ return le_set_scan_enable(index, 0);
+}
+
+static int hciops_resolve_name(int index, bdaddr_t *bdaddr)
+{
+ struct dev_info *dev = &devs[index];
+ remote_name_req_cp cp;
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+ DBG("hci%d dba %s", index, addr);
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.bdaddr, bdaddr);
+ cp.pscan_rep_mode = 0x02;
+
+ if (hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_REMOTE_NAME_REQ,
+ REMOTE_NAME_REQ_CP_SIZE, &cp) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int hciops_set_name(int index, const char *name)
+{
+ struct dev_info *dev = &devs[index];
+ change_local_name_cp cp;
+
+ DBG("hci%d, name %s", index, name);
+
+ memset(&cp, 0, sizeof(cp));
+ strncpy((char *) cp.name, name, sizeof(cp.name));
+
+ if (hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_CHANGE_LOCAL_NAME,
+ CHANGE_LOCAL_NAME_CP_SIZE, &cp) < 0)
+ return -errno;
+
+ memcpy(dev->name, cp.name, 248);
+ update_ext_inquiry_response(index);
+
+ return 0;
+}
+
+static int hciops_cancel_resolve_name(int index, bdaddr_t *bdaddr)
+{
+ struct dev_info *dev = &devs[index];
+ remote_name_req_cancel_cp cp;
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+ DBG("hci%d dba %s", index, addr);
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.bdaddr, bdaddr);
+
+ if (hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_REMOTE_NAME_REQ_CANCEL,
+ REMOTE_NAME_REQ_CANCEL_CP_SIZE, &cp) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int hciops_fast_connectable(int index, gboolean enable)
+{
+ struct dev_info *dev = &devs[index];
+ write_page_activity_cp cp;
+ uint8_t type;
+
+ DBG("hci%d enable %d", index, enable);
+
+ if (enable) {
+ type = PAGE_SCAN_TYPE_INTERLACED;
+ cp.interval = 0x0024; /* 22.5 msec page scan interval */
+ } else {
+ type = PAGE_SCAN_TYPE_STANDARD; /* default */
+ cp.interval = 0x0800; /* default 1.28 sec page scan */
+ }
+
+ cp.window = 0x0012; /* default 11.25 msec page scan window */
+
+ if (hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_WRITE_PAGE_ACTIVITY,
+ WRITE_PAGE_ACTIVITY_CP_SIZE, &cp) < 0)
+ return -errno;
+ else if (hci_send_cmd(dev->sk, OGF_HOST_CTL,
+ OCF_WRITE_PAGE_SCAN_TYPE, 1, &type) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int hciops_read_clock(int index, bdaddr_t *bdaddr, int which,
+ int timeout, uint32_t *clock,
+ uint16_t *accuracy)
+{
+ struct dev_info *dev = &devs[index];
+ uint16_t handle = 0;
+ char addr[18];
+ int ret;
+
+ ba2str(bdaddr, addr);
+ DBG("hci%d addr %s which %d timeout %d", index, addr, which, timeout);
+
+ ret = get_handle(index, bdaddr, &handle);
+ if (ret < 0)
+ return ret;
+
+ if (hci_read_clock(dev->sk, htobs(handle), which, clock, accuracy,
+ timeout) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int hciops_read_bdaddr(int index, bdaddr_t *bdaddr)
+{
+ struct dev_info *dev = &devs[index];
+
+ DBG("hci%d", index);
+
+ bacpy(bdaddr, &dev->bdaddr);
+
+ return 0;
+}
+
+static int hciops_block_device(int index, bdaddr_t *bdaddr)
+{
+ struct dev_info *dev = &devs[index];
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+ DBG("hci%d dba %s", index, addr);
+
+ if (ioctl(dev->sk, HCIBLOCKADDR, bdaddr) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int hciops_unblock_device(int index, bdaddr_t *bdaddr)
+{
+ struct dev_info *dev = &devs[index];
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+ DBG("hci%d dba %s", index, addr);
+
+ if (ioctl(dev->sk, HCIUNBLOCKADDR, bdaddr) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int hciops_get_conn_list(int index, GSList **conns)
+{
+ struct dev_info *dev = &devs[index];
+ GSList *l;
+
+ DBG("hci%d", index);
+
+ *conns = NULL;
+
+ for (l = dev->connections; l != NULL; l = g_slist_next(l)) {
+ struct bt_conn *conn = l->data;
+
+ *conns = g_slist_append(*conns,
+ g_memdup(&conn->bdaddr, sizeof(bdaddr_t)));
+ }
+
+ return 0;
+}
+
+static int hciops_read_local_version(int index, struct hci_version *ver)
+{
+ struct dev_info *dev = &devs[index];
+
+ DBG("hci%d", index);
+
+ memcpy(ver, &dev->ver, sizeof(*ver));
+
+ return 0;
+}
+
+static int hciops_read_local_features(int index, uint8_t *features)
+{
+ struct dev_info *dev = &devs[index];
+
+ DBG("hci%d", index);
+
+ memcpy(features, dev->features, 8);
+
+ return 0;
+}
+
+static int hciops_disconnect(int index, bdaddr_t *bdaddr)
+{
+ DBG("hci%d", index);
+
+ return disconnect_addr(index, bdaddr, HCI_OE_USER_ENDED_CONNECTION);
+}
+
+static int hciops_remove_bonding(int index, bdaddr_t *bdaddr)
+{
+ struct dev_info *dev = &devs[index];
+ delete_stored_link_key_cp cp;
+ GSList *match;
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+ DBG("hci%d dba %s", index, addr);
+
+ match = g_slist_find_custom(dev->keys, bdaddr, (GCompareFunc) bacmp);
+ if (match) {
+ g_free(match->data);
+ dev->keys = g_slist_delete_link(dev->keys, match);
+ }
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.bdaddr, bdaddr);
+
+ /* Delete the link key from the Bluetooth chip */
+ if (hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_DELETE_STORED_LINK_KEY,
+ DELETE_STORED_LINK_KEY_CP_SIZE, &cp) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int hciops_pincode_reply(int index, bdaddr_t *bdaddr, const char *pin)
+{
+ struct dev_info *dev = &devs[index];
+ char addr[18];
+ int err;
+
+ ba2str(bdaddr, addr);
+ DBG("hci%d dba %s", index, addr);
+
+ if (pin) {
+ pin_code_reply_cp pr;
+ size_t len = strlen(pin);
+
+ dev->pin_length = len;
+
+ memset(&pr, 0, sizeof(pr));
+ bacpy(&pr.bdaddr, bdaddr);
+ memcpy(pr.pin_code, pin, len);
+ pr.pin_len = len;
+ err = hci_send_cmd(dev->sk, OGF_LINK_CTL,
+ OCF_PIN_CODE_REPLY,
+ PIN_CODE_REPLY_CP_SIZE, &pr);
+ } else
+ err = hci_send_cmd(dev->sk, OGF_LINK_CTL,
+ OCF_PIN_CODE_NEG_REPLY, 6, bdaddr);
+
+ if (err < 0)
+ err = -errno;
+
+ return err;
+}
+
+static int hciops_passkey_reply(int index, bdaddr_t *bdaddr, uint32_t passkey)
+{
+ struct dev_info *dev = &devs[index];
+ char addr[18];
+ int err;
+
+ ba2str(bdaddr, addr);
+ DBG("hci%d dba %s", index, addr);
+
+ if (passkey != INVALID_PASSKEY) {
+ user_passkey_reply_cp cp;
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.bdaddr, bdaddr);
+ cp.passkey = passkey;
+
+ err = hci_send_cmd(dev->sk, OGF_LINK_CTL,
+ OCF_USER_PASSKEY_REPLY,
+ USER_PASSKEY_REPLY_CP_SIZE, &cp);
+ } else
+ err = hci_send_cmd(dev->sk, OGF_LINK_CTL,
+ OCF_USER_PASSKEY_NEG_REPLY, 6, bdaddr);
+
+ if (err < 0)
+ err = -errno;
+
+ return err;
+}
+
+static int hciops_enable_le(int index)
+{
+ struct dev_info *dev = &devs[index];
+ write_le_host_supported_cp cp;
+
+ DBG("hci%d", index);
+
+ if (!(dev->features[4] & LMP_LE))
+ return -ENOTSUP;
+
+ cp.le = 0x01;
+ cp.simul = (dev->features[6] & LMP_LE_BREDR) ? 0x01 : 0x00;
+
+ if (hci_send_cmd(dev->sk, OGF_HOST_CTL,
+ OCF_WRITE_LE_HOST_SUPPORTED,
+ WRITE_LE_HOST_SUPPORTED_CP_SIZE, &cp) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static uint8_t generate_service_class(int index)
+{
+ struct dev_info *dev = &devs[index];
+ GSList *l;
+ uint8_t val = 0;
+
+ for (l = dev->uuids; l != NULL; l = g_slist_next(l)) {
+ struct uuid_info *uuid = l->data;
+
+ val |= uuid->svc_hint;
+ }
+
+ return val;
+}
+
+static int update_service_classes(int index)
+{
+ struct dev_info *dev = &devs[index];
+ uint8_t value;
+ int err;
+
+ value = generate_service_class(index);
+
+ DBG("hci%d value %u", index, value);
+
+ /* Update only the service class, keep the limited bit,
+ * major/minor class bits intact */
+ dev->wanted_cod &= 0x00ffff;
+ dev->wanted_cod |= (value << 16);
+
+ /* If the cache is enabled or an existing CoD write is in progress
+ * just bail out */
+ if (dev->cache_enable || dev->pending_cod)
+ return 0;
+
+ /* If we already have the CoD we want, update EIR and return */
+ if (dev->current_cod == dev->wanted_cod) {
+ update_ext_inquiry_response(index);
+ return 0;
+ }
+
+ DBG("Changing service classes to 0x%06x", dev->wanted_cod);
+
+ err = write_class(index, dev->wanted_cod);
+ if (err < 0)
+ error("Adapter class update failed: %s (%d)",
+ strerror(-err), -err);
+
+ return err;
+}
+
+static int hciops_add_uuid(int index, uuid_t *uuid, uint8_t svc_hint)
+{
+ struct dev_info *dev = &devs[index];
+ struct uuid_info *info;
+
+ DBG("hci%d", index);
+
+ info = g_new0(struct uuid_info, 1);
+ memcpy(&info->uuid, uuid, sizeof(*uuid));
+ info->svc_hint = svc_hint;
+
+ dev->uuids = g_slist_append(dev->uuids, info);
+
+ return update_service_classes(index);
+}
+
+static int hciops_remove_uuid(int index, uuid_t *uuid)
+{
+ struct dev_info *dev = &devs[index];
+ GSList *match;
+
+ match = g_slist_find_custom(dev->uuids, uuid, sdp_uuid_cmp);
+ if (match) {
+ g_free(match->data);
+ dev->uuids = g_slist_delete_link(dev->uuids, match);
+ }
+
+ DBG("hci%d", index);
+
+ return update_service_classes(index);
+}
+
+static int hciops_disable_cod_cache(int index)
+{
+ struct dev_info *dev = &devs[index];
+
+ DBG("hci%d cache_enable %d", index, dev->cache_enable);
+
+ if (!dev->cache_enable)
+ return 0;
+
+ DBG("hci%d current_cod 0x%06x wanted_cod 0x%06x", index,
+ dev->current_cod, dev->wanted_cod);
+
+ /* Disable and flush svc cache. All successive service class
+ * updates * will be written to the device */
+ dev->cache_enable = FALSE;
+
+ if (dev->current_cod == dev->wanted_cod) {
+ update_ext_inquiry_response(index);
+ return 0;
+ }
+
+ return write_class(index, dev->wanted_cod);
+}
+
+static int hciops_restore_powered(int index)
+{
+ struct dev_info *dev = &devs[index];
+
+ if (!dev->already_up && dev->up)
+ return hciops_power_off(index);
+
+ return 0;
+}
+
+static int hciops_load_keys(int index, GSList *keys, gboolean debug_keys)
+{
+ struct dev_info *dev = &devs[index];
+
+ DBG("hci%d keys %d debug_keys %d", index, g_slist_length(keys),
+ debug_keys);
+
+ if (dev->keys != NULL)
+ return -EEXIST;
+
+ dev->keys = keys;
+ dev->debug_keys = debug_keys;
+
+ return 0;
+}
+
+static int hciops_set_io_capability(int index, uint8_t io_capability)
+{
+ struct dev_info *dev = &devs[index];
+
+ dev->io_capability = io_capability;
+
+ return 0;
+}
+
+static int request_authentication(int index, bdaddr_t *bdaddr)
+{
+ struct dev_info *dev = &devs[index];
+ auth_requested_cp cp;
+ uint16_t handle;
+ int err;
+
+ DBG("hci%d", index);
+
+ err = get_handle(index, bdaddr, &handle);
+ if (err < 0)
+ return err;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.handle = htobs(handle);
+
+ if (hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_AUTH_REQUESTED,
+ AUTH_REQUESTED_CP_SIZE, &cp) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static void bonding_connect_cb(GIOChannel *io, GError *err, gpointer user_data)
+{
+ struct bt_conn *conn = user_data;
+ struct dev_info *dev = conn->dev;
+
+ if (!conn->io) {
+ if (!err)
+ g_io_channel_shutdown(io, TRUE, NULL);
+ return;
+ }
+
+ if (err)
+ /* Wait proper error to be propagated by bonding complete */
+ return;
+
+ if (request_authentication(dev->id, &conn->bdaddr) < 0)
+ goto failed;
+
+ return;
+
+failed:
+ bonding_complete(dev, conn, HCI_UNSPECIFIED_ERROR);
+}
+
+static int hciops_create_bonding(int index, bdaddr_t *bdaddr, uint8_t io_cap)
+{
+ struct dev_info *dev = &devs[index];
+ BtIOSecLevel sec_level;
+ struct bt_conn *conn;
+ GError *err = NULL;
+
+ conn = get_connection(dev, bdaddr);
+
+ if (conn->io != NULL)
+ return -EBUSY;
+
+ conn->loc_cap = io_cap;
+
+ /* If our IO capability is NoInputNoOutput use medium security
+ * level (i.e. don't require MITM protection) else use high
+ * security level */
+ if (io_cap == 0x03)
+ sec_level = BT_IO_SEC_MEDIUM;
+ else
+ sec_level = BT_IO_SEC_HIGH;
+
+ conn->io = bt_io_connect(BT_IO_L2RAW, bonding_connect_cb, conn,
+ NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &dev->bdaddr,
+ BT_IO_OPT_DEST_BDADDR, bdaddr,
+ BT_IO_OPT_SEC_LEVEL, sec_level,
+ BT_IO_OPT_INVALID);
+ if (conn->io == NULL) {
+ error("bt_io_connect: %s", err->message);
+ g_error_free(err);
+ return -EIO;
+ }
+
+ conn->bonding_initiator = TRUE;
+
+ return 0;
+}
+
+static int hciops_cancel_bonding(int index, bdaddr_t *bdaddr)
+{
+ struct dev_info *dev = &devs[index];
+ struct bt_conn *conn;
+
+ DBG("hci%d", index);
+
+ conn = find_connection(dev, bdaddr);
+ if (conn == NULL || conn->io == NULL)
+ return -ENOTCONN;
+
+ g_io_channel_shutdown(conn->io, TRUE, NULL);
+ g_io_channel_unref(conn->io);
+ conn->io = NULL;
+
+ return 0;
+}
+
+static int hciops_read_local_oob_data(int index)
+{
+ struct dev_info *dev = &devs[index];
+
+ DBG("hci%d", index);
+
+ if (hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_READ_LOCAL_OOB_DATA, 0, 0)
+ < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int hciops_add_remote_oob_data(int index, bdaddr_t *bdaddr,
+ uint8_t *hash, uint8_t *randomizer)
+{
+ char addr[18];
+ struct dev_info *dev = &devs[index];
+ GSList *match;
+ struct oob_data *data;
+
+ ba2str(bdaddr, addr);
+ DBG("hci%d bdaddr %s", index, addr);
+
+ match = g_slist_find_custom(dev->oob_data, &bdaddr, oob_bdaddr_cmp);
+
+ if (match) {
+ data = match->data;
+ } else {
+ data = g_new(struct oob_data, 1);
+ bacpy(&data->bdaddr, bdaddr);
+ dev->oob_data = g_slist_prepend(dev->oob_data, data);
+ }
+
+ memcpy(data->hash, hash, sizeof(data->hash));
+ memcpy(data->randomizer, randomizer, sizeof(data->randomizer));
+
+ return 0;
+}
+
+static int hciops_remove_remote_oob_data(int index, bdaddr_t *bdaddr)
+{
+ char addr[18];
+ struct dev_info *dev = &devs[index];
+ GSList *match;
+
+ ba2str(bdaddr, addr);
+ DBG("hci%d bdaddr %s", index, addr);
+
+ match = g_slist_find_custom(dev->oob_data, &bdaddr, oob_bdaddr_cmp);
+
+ if (!match)
+ return -ENOENT;
+
+ g_free(match->data);
+ dev->oob_data = g_slist_delete_link(dev->oob_data, match);
+
+ return 0;
+}
+
+static struct btd_adapter_ops hci_ops = {
+ .setup = hciops_setup,
+ .cleanup = hciops_cleanup,
+ .set_powered = hciops_set_powered,
+ .set_discoverable = hciops_set_discoverable,
+ .set_pairable = hciops_set_pairable,
+ .set_limited_discoverable = hciops_set_limited_discoverable,
+ .start_inquiry = hciops_start_inquiry,
+ .stop_inquiry = hciops_stop_inquiry,
+ .start_scanning = hciops_start_scanning,
+ .stop_scanning = hciops_stop_scanning,
+ .resolve_name = hciops_resolve_name,
+ .cancel_resolve_name = hciops_cancel_resolve_name,
+ .set_name = hciops_set_name,
+ .set_dev_class = hciops_set_dev_class,
+ .set_fast_connectable = hciops_fast_connectable,
+ .read_clock = hciops_read_clock,
+ .read_bdaddr = hciops_read_bdaddr,
+ .block_device = hciops_block_device,
+ .unblock_device = hciops_unblock_device,
+ .get_conn_list = hciops_get_conn_list,
+ .read_local_version = hciops_read_local_version,
+ .read_local_features = hciops_read_local_features,
+ .disconnect = hciops_disconnect,
+ .remove_bonding = hciops_remove_bonding,
+ .pincode_reply = hciops_pincode_reply,
+ .confirm_reply = hciops_confirm_reply,
+ .passkey_reply = hciops_passkey_reply,
+ .enable_le = hciops_enable_le,
+ .encrypt_link = hciops_encrypt_link,
+ .set_did = hciops_set_did,
+ .add_uuid = hciops_add_uuid,
+ .remove_uuid = hciops_remove_uuid,
+ .disable_cod_cache = hciops_disable_cod_cache,
+ .restore_powered = hciops_restore_powered,
+ .load_keys = hciops_load_keys,
+ .set_io_capability = hciops_set_io_capability,
+ .create_bonding = hciops_create_bonding,
+ .cancel_bonding = hciops_cancel_bonding,
+ .read_local_oob_data = hciops_read_local_oob_data,
+ .add_remote_oob_data = hciops_add_remote_oob_data,
+ .remove_remote_oob_data = hciops_remove_remote_oob_data,
+};
+
+static int hciops_init(void)
+{
+ DBG("");
+ return btd_register_adapter_ops(&hci_ops, FALSE);
+}
+
+static void hciops_exit(void)
+{
+ DBG("");
+ btd_adapter_cleanup_ops(&hci_ops);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(hciops, VERSION,
+ BLUETOOTH_PLUGIN_PRIORITY_LOW, hciops_init, hciops_exit)
diff --git a/plugins/maemo6.c b/plugins/maemo6.c
new file mode 100644
index 0000000..56f2664
--- /dev/null
+++ b/plugins/maemo6.c
@@ -0,0 +1,252 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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 <glib.h>
+#include <dbus/dbus.h>
+
+#include "adapter.h"
+#include "plugin.h"
+#include "log.h"
+#include "gdbus.h"
+
+/* from mce/mode-names.h */
+#define MCE_RADIO_STATE_BLUETOOTH (1 << 3)
+
+/* from mce/dbus-names.h */
+#define MCE_SERVICE "com.nokia.mce"
+#define MCE_REQUEST_IF "com.nokia.mce.request"
+#define MCE_SIGNAL_IF "com.nokia.mce.signal"
+#define MCE_REQUEST_PATH "/com/nokia/mce/request"
+#define MCE_SIGNAL_PATH "/com/nokia/mce/signal"
+#define MCE_RADIO_STATES_CHANGE_REQ "req_radio_states_change"
+#define MCE_RADIO_STATES_GET "get_radio_states"
+#define MCE_RADIO_STATES_SIG "radio_states_ind"
+
+static guint watch_id;
+static DBusConnection *conn = NULL;
+static gboolean mce_bt_set = FALSE;
+static gboolean collision = FALSE;
+
+static gboolean mce_signal_callback(DBusConnection *connection,
+ DBusMessage *message, void *user_data)
+{
+ DBusMessageIter args;
+ uint32_t sigvalue;
+ struct btd_adapter *adapter = user_data;
+
+ DBG("received mce signal");
+
+ if (!dbus_message_iter_init(message, &args))
+ error("message has no arguments");
+ else if (DBUS_TYPE_UINT32 != dbus_message_iter_get_arg_type(&args))
+ error("argument is not uint32");
+ else {
+ dbus_message_iter_get_basic(&args, &sigvalue);
+ DBG("got signal with value %u", sigvalue);
+
+ /* set the adapter according to the mce signal
+ and remember the value */
+ mce_bt_set = sigvalue & MCE_RADIO_STATE_BLUETOOTH ?
+ TRUE : FALSE;
+
+ if (mce_bt_set)
+ btd_adapter_switch_online(adapter);
+ else
+ btd_adapter_switch_offline(adapter);
+ }
+
+ return TRUE;
+}
+
+static void read_radio_states_cb(DBusPendingCall *call, void *user_data)
+{
+ DBusError err;
+ DBusMessage *reply;
+ dbus_uint32_t radio_states;
+ struct btd_adapter *adapter = user_data;
+
+ reply = dbus_pending_call_steal_reply(call);
+
+ dbus_error_init(&err);
+ if (dbus_set_error_from_message(&err, reply)) {
+ error("mce replied with an error: %s, %s",
+ err.name, err.message);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ dbus_error_init(&err);
+ if (dbus_message_get_args(reply, &err,
+ DBUS_TYPE_UINT32, &radio_states,
+ DBUS_TYPE_INVALID) == FALSE) {
+ error("unable to parse get_radio_states reply: %s, %s",
+ err.name, err.message);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ DBG("radio_states: %d", radio_states);
+
+ mce_bt_set = radio_states & MCE_RADIO_STATE_BLUETOOTH ? TRUE : FALSE;
+
+ /* check if the adapter has not completed the initial power
+ * cycle, if so delay action to mce_notify_powered */
+ collision = mce_bt_set && adapter_powering_down(adapter);
+
+ if (collision)
+ goto done;
+
+ if (mce_bt_set)
+ btd_adapter_switch_online(adapter);
+ else
+ btd_adapter_switch_offline(adapter);
+
+done:
+ dbus_message_unref(reply);
+}
+
+static void adapter_powered(struct btd_adapter *adapter, gboolean powered)
+{
+ DBusMessage *msg;
+ dbus_uint32_t radio_states = 0;
+ dbus_uint32_t radio_mask = MCE_RADIO_STATE_BLUETOOTH;
+ static gboolean startup = TRUE;
+
+ DBG("adapter_powered called with %d", powered);
+
+ if (startup) {
+ startup = FALSE;
+ return;
+ }
+
+ /* check if the plugin got the get_radio_states reply from the
+ * mce when the adapter was not yet down during the power
+ * cycling when bluetoothd is started */
+ if (collision) {
+ error("maemo6: powered state collision");
+ collision = FALSE;
+
+ if (mce_bt_set)
+ btd_adapter_switch_online(adapter);
+
+ return;
+ }
+
+ /* nothing to do if the states match */
+ if (mce_bt_set == powered)
+ return;
+
+ /* set the mce value according to the state of the adapter */
+ msg = dbus_message_new_method_call(MCE_SERVICE, MCE_REQUEST_PATH,
+ MCE_REQUEST_IF, MCE_RADIO_STATES_CHANGE_REQ);
+
+ if (powered)
+ radio_states = MCE_RADIO_STATE_BLUETOOTH;
+
+ dbus_message_append_args(msg, DBUS_TYPE_UINT32, &radio_states,
+ DBUS_TYPE_UINT32, &radio_mask,
+ DBUS_TYPE_INVALID);
+
+ if (dbus_connection_send(conn, msg, NULL))
+ mce_bt_set = powered;
+ else
+ error("calling %s failed", MCE_RADIO_STATES_CHANGE_REQ);
+
+ dbus_message_unref(msg);
+}
+
+static int mce_probe(struct btd_adapter *adapter)
+{
+ DBusMessage *msg;
+ DBusPendingCall *call;
+
+ DBG("path %s", adapter_get_path(adapter));
+
+ msg = dbus_message_new_method_call(MCE_SERVICE, MCE_REQUEST_PATH,
+ MCE_REQUEST_IF, MCE_RADIO_STATES_GET);
+
+ if (!dbus_connection_send_with_reply(conn, msg, &call, -1)) {
+ error("calling %s failed", MCE_RADIO_STATES_GET);
+ dbus_message_unref(msg);
+ return -1;
+ }
+
+ dbus_pending_call_set_notify(call, read_radio_states_cb, adapter, NULL);
+ dbus_pending_call_unref(call);
+ dbus_message_unref(msg);
+
+ watch_id = g_dbus_add_signal_watch(conn, NULL, MCE_SIGNAL_PATH,
+ MCE_SIGNAL_IF, MCE_RADIO_STATES_SIG,
+ mce_signal_callback, adapter, NULL);
+
+ btd_adapter_register_powered_callback(adapter, adapter_powered);
+
+ return 0;
+}
+
+static void mce_remove(struct btd_adapter *adapter)
+{
+ DBG("path %s", adapter_get_path(adapter));
+
+ if (watch_id > 0)
+ g_dbus_remove_watch(conn, watch_id);
+
+ btd_adapter_unregister_powered_callback(adapter, adapter_powered);
+}
+
+static struct btd_adapter_driver mce_driver = {
+ .name = "mce",
+ .probe = mce_probe,
+ .remove = mce_remove,
+};
+
+static int maemo6_init(void)
+{
+ DBG("init maemo6 plugin");
+
+ conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+ if (conn == NULL) {
+ error("Unable to connect to D-Bus");
+ return -1;
+ }
+
+ return btd_register_adapter_driver(&mce_driver);
+}
+
+static void maemo6_exit(void)
+{
+ DBG("exit maemo6 plugin");
+
+ if (conn != NULL)
+ dbus_connection_unref(conn);
+
+ btd_unregister_adapter_driver(&mce_driver);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(maemo6, VERSION,
+ BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, maemo6_init, maemo6_exit)
diff --git a/plugins/mgmtops.c b/plugins/mgmtops.c
new file mode 100644
index 0000000..65b32e6
--- /dev/null
+++ b/plugins/mgmtops.c
@@ -0,0 +1,1937 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2010 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 <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+
+#include <glib.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+#include <bluetooth/mgmt.h>
+
+#include "plugin.h"
+#include "log.h"
+#include "adapter.h"
+#include "manager.h"
+#include "device.h"
+#include "event.h"
+#include "oob.h"
+
+#define MGMT_BUF_SIZE 1024
+
+static int max_index = -1;
+static struct controller_info {
+ gboolean valid;
+ gboolean notified;
+ uint8_t type;
+ bdaddr_t bdaddr;
+ uint8_t features[8];
+ uint8_t dev_class[3];
+ uint16_t manufacturer;
+ uint8_t hci_ver;
+ uint16_t hci_rev;
+ gboolean enabled;
+ gboolean connectable;
+ gboolean discoverable;
+ gboolean pairable;
+ uint8_t sec_mode;
+ GSList *connections;
+} *controllers = NULL;
+
+static int mgmt_sock = -1;
+static guint mgmt_watch = 0;
+
+static uint8_t mgmt_version = 0;
+static uint16_t mgmt_revision = 0;
+
+static void read_version_complete(int sk, void *buf, size_t len)
+{
+ struct mgmt_hdr hdr;
+ struct mgmt_rp_read_version *rp = buf;
+
+ if (len < sizeof(*rp)) {
+ error("Too small read version complete event");
+ return;
+ }
+
+ mgmt_revision = btohs(bt_get_unaligned(&rp->revision));
+ mgmt_version = rp->version;
+
+ DBG("version %u revision %u", mgmt_version, mgmt_revision);
+
+ memset(&hdr, 0, sizeof(hdr));
+ hdr.opcode = htobs(MGMT_OP_READ_INDEX_LIST);
+ hdr.index = htobs(MGMT_INDEX_NONE);
+ if (write(sk, &hdr, sizeof(hdr)) < 0)
+ error("Unable to read controller index list: %s (%d)",
+ strerror(errno), errno);
+}
+
+static void add_controller(uint16_t index)
+{
+ if (index > max_index) {
+ size_t size = sizeof(struct controller_info) * (index + 1);
+ max_index = index;
+ controllers = g_realloc(controllers, size);
+ }
+
+ memset(&controllers[index], 0, sizeof(struct controller_info));
+
+ controllers[index].valid = TRUE;
+
+ DBG("Added controller %u", index);
+}
+
+static void read_info(int sk, uint16_t index)
+{
+ struct mgmt_hdr hdr;
+
+ memset(&hdr, 0, sizeof(hdr));
+ hdr.opcode = htobs(MGMT_OP_READ_INFO);
+ hdr.index = htobs(index);
+
+ if (write(sk, &hdr, sizeof(hdr)) < 0)
+ error("Unable to send read_info command: %s (%d)",
+ strerror(errno), errno);
+}
+
+static void get_connections(int sk, uint16_t index)
+{
+ struct mgmt_hdr hdr;
+
+ memset(&hdr, 0, sizeof(hdr));
+ hdr.opcode = htobs(MGMT_OP_GET_CONNECTIONS);
+ hdr.index = htobs(index);
+
+ if (write(sk, &hdr, sizeof(hdr)) < 0)
+ error("Unable to send get_connections command: %s (%d)",
+ strerror(errno), errno);
+}
+
+static void mgmt_index_added(int sk, uint16_t index)
+{
+ add_controller(index);
+ read_info(sk, index);
+}
+
+static void remove_controller(uint16_t index)
+{
+ if (index > max_index)
+ return;
+
+ if (!controllers[index].valid)
+ return;
+
+ btd_manager_unregister_adapter(index);
+
+ memset(&controllers[index], 0, sizeof(struct controller_info));
+
+ DBG("Removed controller %u", index);
+}
+
+static void mgmt_index_removed(int sk, uint16_t index)
+{
+ remove_controller(index);
+}
+
+static int mgmt_set_mode(int index, uint16_t opcode, uint8_t val)
+{
+ char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_mode)];
+ struct mgmt_hdr *hdr = (void *) buf;
+ struct mgmt_mode *cp = (void *) &buf[sizeof(*hdr)];
+
+ memset(buf, 0, sizeof(buf));
+ hdr->opcode = htobs(opcode);
+ hdr->index = htobs(index);
+ hdr->len = htobs(sizeof(*cp));
+
+ cp->val = val;
+
+ if (write(mgmt_sock, buf, sizeof(buf)) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int mgmt_set_connectable(int index, gboolean connectable)
+{
+ DBG("index %d connectable %d", index, connectable);
+ return mgmt_set_mode(index, MGMT_OP_SET_CONNECTABLE, connectable);
+}
+
+static int mgmt_set_discoverable(int index, gboolean discoverable)
+{
+ DBG("index %d discoverable %d", index, discoverable);
+ return mgmt_set_mode(index, MGMT_OP_SET_DISCOVERABLE, discoverable);
+}
+
+static int mgmt_set_pairable(int index, gboolean pairable)
+{
+ DBG("index %d pairable %d", index, pairable);
+ return mgmt_set_mode(index, MGMT_OP_SET_PAIRABLE, pairable);
+}
+
+static int mgmt_update_powered(int index, uint8_t powered)
+{
+ struct controller_info *info;
+ struct btd_adapter *adapter;
+ gboolean pairable, discoverable;
+ uint8_t on_mode;
+
+ if (index > max_index) {
+ error("Unexpected index %u", index);
+ return -ENODEV;
+ }
+
+ info = &controllers[index];
+
+ info->enabled = powered;
+
+ adapter = manager_find_adapter(&info->bdaddr);
+ if (adapter == NULL) {
+ DBG("Adapter not found");
+ return -ENODEV;
+ }
+
+ if (!powered) {
+ info->connectable = FALSE;
+ info->pairable = FALSE;
+ info->discoverable = FALSE;
+
+ btd_adapter_stop(adapter);
+ return 0;
+ }
+
+ btd_adapter_start(adapter);
+
+ btd_adapter_get_mode(adapter, NULL, &on_mode, &pairable);
+
+ discoverable = (on_mode == MODE_DISCOVERABLE);
+
+ if (on_mode == MODE_DISCOVERABLE && !info->discoverable)
+ mgmt_set_discoverable(index, TRUE);
+ else if (on_mode == MODE_CONNECTABLE && !info->connectable)
+ mgmt_set_connectable(index, TRUE);
+ else {
+ uint8_t mode = 0;
+
+ if (info->connectable)
+ mode |= SCAN_PAGE;
+ if (info->discoverable)
+ mode |= SCAN_INQUIRY;
+
+ adapter_mode_changed(adapter, mode);
+ }
+
+ if (info->pairable != pairable)
+ mgmt_set_pairable(index, pairable);
+
+ return 0;
+}
+
+static void mgmt_powered(int sk, uint16_t index, void *buf, size_t len)
+{
+ struct mgmt_mode *ev = buf;
+
+ if (len < sizeof(*ev)) {
+ error("Too small powered event");
+ return;
+ }
+
+ DBG("Controller %u powered %u", index, ev->val);
+
+ mgmt_update_powered(index, ev->val);
+}
+
+static void mgmt_discoverable(int sk, uint16_t index, void *buf, size_t len)
+{
+ struct mgmt_mode *ev = buf;
+ struct controller_info *info;
+ struct btd_adapter *adapter;
+ uint8_t mode;
+
+ if (len < sizeof(*ev)) {
+ error("Too small discoverable event");
+ return;
+ }
+
+ DBG("Controller %u discoverable %u", index, ev->val);
+
+ if (index > max_index) {
+ error("Unexpected index %u in discoverable event", index);
+ return;
+ }
+
+ info = &controllers[index];
+
+ info->discoverable = ev->val ? TRUE : FALSE;
+
+ adapter = manager_find_adapter(&info->bdaddr);
+ if (!adapter)
+ return;
+
+ if (info->connectable)
+ mode = SCAN_PAGE;
+ else
+ mode = 0;
+
+ if (info->discoverable)
+ mode |= SCAN_INQUIRY;
+
+ adapter_mode_changed(adapter, mode);
+}
+
+static void mgmt_connectable(int sk, uint16_t index, void *buf, size_t len)
+{
+ struct mgmt_mode *ev = buf;
+ struct controller_info *info;
+ struct btd_adapter *adapter;
+ uint8_t mode;
+
+ if (len < sizeof(*ev)) {
+ error("Too small connectable event");
+ return;
+ }
+
+ DBG("Controller %u connectable %u", index, ev->val);
+
+ if (index > max_index) {
+ error("Unexpected index %u in connectable event", index);
+ return;
+ }
+
+ info = &controllers[index];
+
+ info->connectable = ev->val ? TRUE : FALSE;
+
+ adapter = manager_find_adapter(&info->bdaddr);
+ if (!adapter)
+ return;
+
+ if (info->discoverable)
+ mode = SCAN_INQUIRY;
+ else
+ mode = 0;
+
+ if (info->connectable)
+ mode |= SCAN_PAGE;
+
+ adapter_mode_changed(adapter, mode);
+}
+
+static void mgmt_pairable(int sk, uint16_t index, void *buf, size_t len)
+{
+ struct mgmt_mode *ev = buf;
+ struct controller_info *info;
+ struct btd_adapter *adapter;
+
+ if (len < sizeof(*ev)) {
+ error("Too small pairable event");
+ return;
+ }
+
+ DBG("Controller %u pairable %u", index, ev->val);
+
+ if (index > max_index) {
+ error("Unexpected index %u in pairable event", index);
+ return;
+ }
+
+ info = &controllers[index];
+
+ info->pairable = ev->val ? TRUE : FALSE;
+
+ adapter = manager_find_adapter(&info->bdaddr);
+ if (!adapter)
+ return;
+
+ btd_adapter_pairable_changed(adapter, info->pairable);
+}
+
+static void mgmt_new_key(int sk, uint16_t index, void *buf, size_t len)
+{
+ struct mgmt_ev_new_key *ev = buf;
+ struct controller_info *info;
+
+ if (len != sizeof(*ev)) {
+ error("new_key event size mismatch (%zu != %zu)",
+ len, sizeof(*ev));
+ return;
+ }
+
+ DBG("Controller %u new key of type %u pin_len %u", index,
+ ev->key.type, ev->key.pin_len);
+
+ if (index > max_index) {
+ error("Unexpected index %u in new_key event", index);
+ return;
+ }
+
+ if (ev->key.pin_len > 16) {
+ error("Invalid PIN length (%u) in new_key event",
+ ev->key.pin_len);
+ return;
+ }
+
+ info = &controllers[index];
+
+ btd_event_link_key_notify(&info->bdaddr, &ev->key.bdaddr,
+ ev->key.val, ev->key.type,
+ ev->key.pin_len);
+
+ btd_event_bonding_complete(&info->bdaddr, &ev->key.bdaddr, 0);
+}
+
+static void mgmt_device_connected(int sk, uint16_t index, void *buf, size_t len)
+{
+ struct mgmt_ev_device_connected *ev = buf;
+ struct controller_info *info;
+ char addr[18];
+
+ if (len < sizeof(*ev)) {
+ error("Too small device_connected event");
+ return;
+ }
+
+ ba2str(&ev->bdaddr, addr);
+
+ DBG("hci%u device %s connected", index, addr);
+
+ if (index > max_index) {
+ error("Unexpected index %u in device_connected event", index);
+ return;
+ }
+
+ info = &controllers[index];
+
+ btd_event_conn_complete(&info->bdaddr, &ev->bdaddr);
+}
+
+static void mgmt_device_disconnected(int sk, uint16_t index, void *buf,
+ size_t len)
+{
+ struct mgmt_ev_device_disconnected *ev = buf;
+ struct controller_info *info;
+ char addr[18];
+
+ if (len < sizeof(*ev)) {
+ error("Too small device_disconnected event");
+ return;
+ }
+
+ ba2str(&ev->bdaddr, addr);
+
+ DBG("hci%u device %s disconnected", index, addr);
+
+ if (index > max_index) {
+ error("Unexpected index %u in device_disconnected event", index);
+ return;
+ }
+
+ info = &controllers[index];
+
+ btd_event_disconn_complete(&info->bdaddr, &ev->bdaddr);
+}
+
+static void mgmt_connect_failed(int sk, uint16_t index, void *buf, size_t len)
+{
+ struct mgmt_ev_connect_failed *ev = buf;
+ struct controller_info *info;
+ char addr[18];
+
+ if (len < sizeof(*ev)) {
+ error("Too small connect_failed event");
+ return;
+ }
+
+ ba2str(&ev->bdaddr, addr);
+
+ DBG("hci%u %s status %u", index, addr, ev->status);
+
+ if (index > max_index) {
+ error("Unexpected index %u in connect_failed event", index);
+ return;
+ }
+
+ info = &controllers[index];
+
+ btd_event_conn_failed(&info->bdaddr, &ev->bdaddr, ev->status);
+
+ /* In the case of security mode 3 devices */
+ btd_event_bonding_complete(&info->bdaddr, &ev->bdaddr, ev->status);
+}
+
+static int mgmt_pincode_reply(int index, bdaddr_t *bdaddr, const char *pin)
+{
+ char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_pin_code_reply)];
+ struct mgmt_hdr *hdr = (void *) buf;
+ size_t buf_len;
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+ DBG("index %d addr %s pin %s", index, addr, pin ? pin : "<none>");
+
+ memset(buf, 0, sizeof(buf));
+
+ if (pin == NULL) {
+ struct mgmt_cp_pin_code_neg_reply *cp;
+
+ hdr->opcode = htobs(MGMT_OP_PIN_CODE_NEG_REPLY);
+ hdr->len = htobs(sizeof(*cp));
+ hdr->index = htobs(index);
+
+ cp = (void *) &buf[sizeof(*hdr)];
+ bacpy(&cp->bdaddr, bdaddr);
+
+ buf_len = sizeof(*hdr) + sizeof(*cp);
+ } else {
+ struct mgmt_cp_pin_code_reply *cp;
+ size_t pin_len;
+
+ pin_len = strlen(pin);
+ if (pin_len > 16)
+ return -EINVAL;
+
+ hdr->opcode = htobs(MGMT_OP_PIN_CODE_REPLY);
+ hdr->len = htobs(sizeof(*cp));
+ hdr->index = htobs(index);
+
+ cp = (void *) &buf[sizeof(*hdr)];
+ bacpy(&cp->bdaddr, bdaddr);
+ cp->pin_len = pin_len;
+ memcpy(cp->pin_code, pin, pin_len);
+
+ buf_len = sizeof(*hdr) + sizeof(*cp);
+ }
+
+ if (write(mgmt_sock, buf, buf_len) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static void mgmt_pin_code_request(int sk, uint16_t index, void *buf, size_t len)
+{
+ struct mgmt_ev_pin_code_request *ev = buf;
+ struct controller_info *info;
+ char addr[18];
+ int err;
+
+ if (len < sizeof(*ev)) {
+ error("Too small pin_code_request event");
+ return;
+ }
+
+ ba2str(&ev->bdaddr, addr);
+
+ DBG("hci%u %s", index, addr);
+
+ if (index > max_index) {
+ error("Unexpected index %u in pin_code_request event", index);
+ return;
+ }
+
+ info = &controllers[index];
+
+ err = btd_event_request_pin(&info->bdaddr, &ev->bdaddr);
+ if (err < 0) {
+ error("btd_event_request_pin: %s", strerror(-err));
+ mgmt_pincode_reply(index, &ev->bdaddr, NULL);
+ }
+}
+
+static int mgmt_confirm_reply(int index, bdaddr_t *bdaddr, gboolean success)
+{
+ char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_user_confirm_reply)];
+ struct mgmt_hdr *hdr = (void *) buf;
+ struct mgmt_cp_user_confirm_reply *cp;
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+ DBG("index %d addr %s success %d", index, addr, success);
+
+ memset(buf, 0, sizeof(buf));
+
+ if (success)
+ hdr->opcode = htobs(MGMT_OP_USER_CONFIRM_REPLY);
+ else
+ hdr->opcode = htobs(MGMT_OP_USER_CONFIRM_NEG_REPLY);
+
+ hdr->len = htobs(sizeof(*cp));
+ hdr->index = htobs(index);
+
+ cp = (void *) &buf[sizeof(*hdr)];
+ bacpy(&cp->bdaddr, bdaddr);
+
+ if (write(mgmt_sock, buf, sizeof(buf)) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static void mgmt_user_confirm_request(int sk, uint16_t index, void *buf,
+ size_t len)
+{
+ struct mgmt_ev_user_confirm_request *ev = buf;
+ struct controller_info *info;
+ char addr[18];
+ int err;
+
+ if (len < sizeof(*ev)) {
+ error("Too small user_confirm_request event");
+ return;
+ }
+
+ ba2str(&ev->bdaddr, addr);
+
+ DBG("hci%u %s", index, addr);
+
+ if (index > max_index) {
+ error("Unexpected index %u in user_confirm_request event",
+ index);
+ return;
+ }
+
+ info = &controllers[index];
+
+ err = btd_event_user_confirm(&info->bdaddr, &ev->bdaddr,
+ btohl(ev->value));
+ if (err < 0) {
+ error("btd_event_user_confirm: %s", strerror(-err));
+ mgmt_confirm_reply(index, &ev->bdaddr, FALSE);
+ }
+}
+
+static void uuid_to_uuid128(uuid_t *uuid128, const uuid_t *uuid)
+{
+ if (uuid->type == SDP_UUID16)
+ sdp_uuid16_to_uuid128(uuid128, uuid);
+ else if (uuid->type == SDP_UUID32)
+ sdp_uuid32_to_uuid128(uuid128, uuid);
+ else
+ memcpy(uuid128, uuid, sizeof(*uuid));
+}
+
+static int mgmt_add_uuid(int index, uuid_t *uuid, uint8_t svc_hint)
+{
+ char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_add_uuid)];
+ struct mgmt_hdr *hdr = (void *) buf;
+ struct mgmt_cp_add_uuid *cp = (void *) &buf[sizeof(*hdr)];
+ uuid_t uuid128;
+ uint128_t uint128;
+
+ DBG("index %d", index);
+
+ uuid_to_uuid128(&uuid128, uuid);
+
+ memset(buf, 0, sizeof(buf));
+ hdr->opcode = htobs(MGMT_OP_ADD_UUID);
+ hdr->len = htobs(sizeof(*cp));
+ hdr->index = htobs(index);
+
+ ntoh128((uint128_t *) uuid128.value.uuid128.data, &uint128);
+ htob128(&uint128, (uint128_t *) cp->uuid);
+
+ cp->svc_hint = svc_hint;
+
+ if (write(mgmt_sock, buf, sizeof(buf)) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int mgmt_remove_uuid(int index, uuid_t *uuid)
+{
+ char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_remove_uuid)];
+ struct mgmt_hdr *hdr = (void *) buf;
+ struct mgmt_cp_remove_uuid *cp = (void *) &buf[sizeof(*hdr)];
+ uuid_t uuid128;
+ uint128_t uint128;
+
+ DBG("index %d", index);
+
+ uuid_to_uuid128(&uuid128, uuid);
+
+ memset(buf, 0, sizeof(buf));
+ hdr->opcode = htobs(MGMT_OP_REMOVE_UUID);
+ hdr->len = htobs(sizeof(*cp));
+ hdr->index = htobs(index);
+
+ ntoh128((uint128_t *) uuid128.value.uuid128.data, &uint128);
+ htob128(&uint128, (uint128_t *) cp->uuid);
+
+ if (write(mgmt_sock, buf, sizeof(buf)) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int clear_uuids(int index)
+{
+ uuid_t uuid_any;
+
+ memset(&uuid_any, 0, sizeof(uuid_any));
+ uuid_any.type = SDP_UUID128;
+
+ return mgmt_remove_uuid(index, &uuid_any);
+}
+
+static void read_index_list_complete(int sk, void *buf, size_t len)
+{
+ struct mgmt_rp_read_index_list *rp = buf;
+ uint16_t num;
+ int i;
+
+ if (len < sizeof(*rp)) {
+ error("Too small read index list complete event");
+ return;
+ }
+
+ num = btohs(bt_get_unaligned(&rp->num_controllers));
+
+ if (num * sizeof(uint16_t) + sizeof(*rp) != len) {
+ error("Incorrect packet size for index list event");
+ return;
+ }
+
+ for (i = 0; i < num; i++) {
+ uint16_t index;
+
+ index = btohs(bt_get_unaligned(&rp->index[i]));
+
+ add_controller(index);
+ get_connections(sk, index);
+ clear_uuids(index);
+ }
+}
+
+static int mgmt_set_powered(int index, gboolean powered)
+{
+ DBG("index %d powered %d", index, powered);
+ return mgmt_set_mode(index, MGMT_OP_SET_POWERED, powered);
+}
+
+static void read_info_complete(int sk, uint16_t index, void *buf, size_t len)
+{
+ struct mgmt_rp_read_info *rp = buf;
+ struct controller_info *info;
+ struct btd_adapter *adapter;
+ uint8_t mode;
+ char addr[18];
+
+ if (len < sizeof(*rp)) {
+ error("Too small read info complete event");
+ return;
+ }
+
+ if (index > max_index) {
+ error("Unexpected index %u in read info complete", index);
+ return;
+ }
+
+ mgmt_set_mode(index, MGMT_OP_SET_SERVICE_CACHE, 1);
+
+ info = &controllers[index];
+ info->type = rp->type;
+ info->enabled = rp->powered;
+ info->connectable = rp->connectable;
+ info->discoverable = rp->discoverable;
+ info->pairable = rp->pairable;
+ info->sec_mode = rp->sec_mode;
+ bacpy(&info->bdaddr, &rp->bdaddr);
+ memcpy(info->dev_class, rp->dev_class, 3);
+ memcpy(info->features, rp->features, 8);
+ info->manufacturer = btohs(bt_get_unaligned(&rp->manufacturer));
+ info->hci_ver = rp->hci_ver;
+ info->hci_rev = btohs(bt_get_unaligned(&rp->hci_rev));
+
+ ba2str(&info->bdaddr, addr);
+ DBG("hci%u type %u addr %s", index, info->type, addr);
+ DBG("hci%u class 0x%02x%02x%02x", index,
+ info->dev_class[2], info->dev_class[1], info->dev_class[0]);
+ DBG("hci%u manufacturer %d HCI ver %d:%d", index, info->manufacturer,
+ info->hci_ver, info->hci_rev);
+ DBG("hci%u enabled %u discoverable %u pairable %u sec_mode %u", index,
+ info->enabled, info->discoverable,
+ info->pairable, info->sec_mode);
+ DBG("hci%u name %s", index, (char *) rp->name);
+
+ adapter = btd_manager_register_adapter(index);
+ if (adapter == NULL) {
+ error("mgmtops: unable to register adapter");
+ return;
+ }
+
+ btd_adapter_get_mode(adapter, &mode, NULL, NULL);
+ if (mode == MODE_OFF) {
+ mgmt_set_powered(index, FALSE);
+ return;
+ }
+
+ if (info->enabled)
+ mgmt_update_powered(index, TRUE);
+ else
+ mgmt_set_powered(index, TRUE);
+
+ adapter_update_local_name(adapter, (char *) rp->name);
+
+ btd_adapter_unref(adapter);
+}
+
+static void set_powered_complete(int sk, uint16_t index, void *buf, size_t len)
+{
+ struct mgmt_mode *rp = buf;
+
+ if (len < sizeof(*rp)) {
+ error("Too small set powered complete event");
+ return;
+ }
+
+ DBG("hci%d powered %u", index, rp->val);
+
+ mgmt_update_powered(index, rp->val);
+}
+
+static void set_discoverable_complete(int sk, uint16_t index, void *buf,
+ size_t len)
+{
+ struct mgmt_mode *rp = buf;
+ struct controller_info *info;
+ struct btd_adapter *adapter;
+ uint8_t mode;
+
+ if (len < sizeof(*rp)) {
+ error("Too small set discoverable complete event");
+ return;
+ }
+
+ DBG("hci%d discoverable %u", index, rp->val);
+
+ if (index > max_index) {
+ error("Unexpected index %u in discoverable complete", index);
+ return;
+ }
+
+ info = &controllers[index];
+
+ info->discoverable = rp->val ? TRUE : FALSE;
+
+ adapter = manager_find_adapter(&info->bdaddr);
+ if (!adapter)
+ return;
+
+ /* set_discoverable will always also change page scanning */
+ mode = SCAN_PAGE;
+
+ if (info->discoverable)
+ mode |= SCAN_INQUIRY;
+
+ adapter_mode_changed(adapter, mode);
+}
+
+static void set_connectable_complete(int sk, uint16_t index, void *buf,
+ size_t len)
+{
+ struct mgmt_mode *rp = buf;
+ struct controller_info *info;
+ struct btd_adapter *adapter;
+
+ if (len < sizeof(*rp)) {
+ error("Too small set connectable complete event");
+ return;
+ }
+
+ DBG("hci%d connectable %u", index, rp->val);
+
+ if (index > max_index) {
+ error("Unexpected index %u in connectable complete", index);
+ return;
+ }
+
+ info = &controllers[index];
+
+ info->connectable = rp->val ? TRUE : FALSE;
+
+ adapter = manager_find_adapter(&info->bdaddr);
+ if (adapter)
+ adapter_mode_changed(adapter, rp->val ? SCAN_PAGE : 0);
+}
+
+static void set_pairable_complete(int sk, uint16_t index, void *buf,
+ size_t len)
+{
+ struct mgmt_mode *rp = buf;
+ struct controller_info *info;
+ struct btd_adapter *adapter;
+
+ if (len < sizeof(*rp)) {
+ error("Too small set pairable complete event");
+ return;
+ }
+
+ DBG("hci%d pairable %u", index, rp->val);
+
+ if (index > max_index) {
+ error("Unexpected index %u in pairable complete", index);
+ return;
+ }
+
+ info = &controllers[index];
+
+ info->pairable = rp->val ? TRUE : FALSE;
+
+ adapter = manager_find_adapter(&info->bdaddr);
+ if (!adapter)
+ return;
+
+ btd_adapter_pairable_changed(adapter, info->pairable);
+}
+
+static void disconnect_complete(int sk, uint16_t index, void *buf, size_t len)
+{
+ struct mgmt_rp_disconnect *rp = buf;
+ struct controller_info *info;
+ char addr[18];
+
+ if (len < sizeof(*rp)) {
+ error("Too small disconnect complete event");
+ return;
+ }
+
+ ba2str(&rp->bdaddr, addr);
+
+ DBG("hci%d %s disconnected", index, addr);
+
+ if (index > max_index) {
+ error("Unexpected index %u in disconnect complete", index);
+ return;
+ }
+
+ info = &controllers[index];
+
+ btd_event_disconn_complete(&info->bdaddr, &rp->bdaddr);
+
+ btd_event_bonding_complete(&info->bdaddr, &rp->bdaddr,
+ HCI_CONNECTION_TERMINATED);
+}
+
+static void pair_device_complete(int sk, uint16_t index, void *buf, size_t len)
+{
+ struct mgmt_rp_pair_device *rp = buf;
+ struct controller_info *info;
+ char addr[18];
+
+ if (len < sizeof(*rp)) {
+ error("Too small pair_device complete event");
+ return;
+ }
+
+ ba2str(&rp->bdaddr, addr);
+
+ DBG("hci%d %s pairing complete status %u", index, addr, rp->status);
+
+ if (index > max_index) {
+ error("Unexpected index %u in pair_device complete", index);
+ return;
+ }
+
+ info = &controllers[index];
+
+ btd_event_bonding_complete(&info->bdaddr, &rp->bdaddr, rp->status);
+}
+
+static void get_connections_complete(int sk, uint16_t index, void *buf,
+ size_t len)
+{
+ struct mgmt_rp_get_connections *rp = buf;
+ struct controller_info *info;
+ int i;
+
+ if (len < sizeof(*rp)) {
+ error("Too small get_connections complete event");
+ return;
+ }
+
+ if (len < (sizeof(*rp) + (rp->conn_count * sizeof(bdaddr_t)))) {
+ error("Too small get_connections complete event");
+ return;
+ }
+
+ if (index > max_index) {
+ error("Unexpected index %u in get_connections complete",
+ index);
+ return;
+ }
+
+ info = &controllers[index];
+
+ for (i = 0; i < rp->conn_count; i++) {
+ bdaddr_t *bdaddr = g_memdup(&rp->conn[i], sizeof(bdaddr_t));
+ info->connections = g_slist_append(info->connections, bdaddr);
+ }
+
+ read_info(sk, index);
+}
+
+static void set_local_name_complete(int sk, uint16_t index, void *buf,
+ size_t len)
+{
+ struct mgmt_cp_set_local_name *rp = buf;
+ struct controller_info *info;
+ struct btd_adapter *adapter;
+
+ if (len < sizeof(*rp)) {
+ error("Too small set_local_name complete event");
+ return;
+ }
+
+ DBG("hci%d name %s", index, (char *) rp->name);
+
+ if (index > max_index) {
+ error("Unexpected index %u in set_local_name complete", index);
+ return;
+ }
+
+ info = &controllers[index];
+
+ adapter = manager_find_adapter(&info->bdaddr);
+ if (adapter == NULL) {
+ DBG("Adapter not found");
+ return;
+ }
+
+ adapter_update_local_name(adapter, (char *) rp->name);
+}
+
+static void read_local_oob_data_complete(int sk, uint16_t index, void *buf,
+ size_t len)
+{
+ struct mgmt_rp_read_local_oob_data *rp = buf;
+ struct btd_adapter *adapter;
+
+ if (len != sizeof(*rp)) {
+ error("Wrong read_local_oob_data_complete event size");
+ return;
+ }
+
+ if (index > max_index) {
+ error("Unexpected index %u in read_local_oob_data_complete",
+ index);
+ return;
+ }
+
+ DBG("hci%u", index);
+
+ adapter = manager_find_adapter_by_id(index);
+
+ if (adapter)
+ oob_read_local_data_complete(adapter, rp->hash, rp->randomizer);
+}
+
+static void read_local_oob_data_failed(int sk, uint16_t index)
+{
+ struct btd_adapter *adapter;
+
+ if (index > max_index) {
+ error("Unexpected index %u in read_local_oob_data_failed",
+ index);
+ return;
+ }
+
+ DBG("hci%u", index);
+
+ adapter = manager_find_adapter_by_id(index);
+
+ if (adapter)
+ oob_read_local_data_complete(adapter, NULL, NULL);
+}
+
+static void mgmt_cmd_complete(int sk, uint16_t index, void *buf, size_t len)
+{
+ struct mgmt_ev_cmd_complete *ev = buf;
+ uint16_t opcode;
+
+ DBG("");
+
+ if (len < sizeof(*ev)) {
+ error("Too small management command complete event packet");
+ return;
+ }
+
+ opcode = btohs(bt_get_unaligned(&ev->opcode));
+
+ len -= sizeof(*ev);
+
+ switch (opcode) {
+ case MGMT_OP_READ_VERSION:
+ read_version_complete(sk, ev->data, len);
+ break;
+ case MGMT_OP_READ_INDEX_LIST:
+ read_index_list_complete(sk, ev->data, len);
+ break;
+ case MGMT_OP_READ_INFO:
+ read_info_complete(sk, index, ev->data, len);
+ break;
+ case MGMT_OP_SET_POWERED:
+ set_powered_complete(sk, index, ev->data, len);
+ break;
+ case MGMT_OP_SET_DISCOVERABLE:
+ set_discoverable_complete(sk, index, ev->data, len);
+ break;
+ case MGMT_OP_SET_CONNECTABLE:
+ set_connectable_complete(sk, index, ev->data, len);
+ break;
+ case MGMT_OP_SET_PAIRABLE:
+ set_pairable_complete(sk, index, ev->data, len);
+ break;
+ case MGMT_OP_ADD_UUID:
+ DBG("add_uuid complete");
+ break;
+ case MGMT_OP_REMOVE_UUID:
+ DBG("remove_uuid complete");
+ break;
+ case MGMT_OP_SET_DEV_CLASS:
+ DBG("set_dev_class complete");
+ break;
+ case MGMT_OP_SET_SERVICE_CACHE:
+ DBG("set_service_cache complete");
+ break;
+ case MGMT_OP_LOAD_KEYS:
+ DBG("load_keys complete");
+ break;
+ case MGMT_OP_REMOVE_KEY:
+ DBG("remove_key complete");
+ break;
+ case MGMT_OP_DISCONNECT:
+ DBG("disconnect complete");
+ disconnect_complete(sk, index, ev->data, len);
+ break;
+ case MGMT_OP_GET_CONNECTIONS:
+ get_connections_complete(sk, index, ev->data, len);
+ break;
+ case MGMT_OP_PIN_CODE_REPLY:
+ DBG("pin_code_reply complete");
+ break;
+ case MGMT_OP_PIN_CODE_NEG_REPLY:
+ DBG("pin_code_neg_reply complete");
+ break;
+ case MGMT_OP_SET_IO_CAPABILITY:
+ DBG("set_io_capability complete");
+ break;
+ case MGMT_OP_PAIR_DEVICE:
+ pair_device_complete(sk, index, ev->data, len);
+ break;
+ case MGMT_OP_USER_CONFIRM_REPLY:
+ DBG("user_confirm_reply complete");
+ break;
+ case MGMT_OP_USER_CONFIRM_NEG_REPLY:
+ DBG("user_confirm_net_reply complete");
+ break;
+ case MGMT_OP_SET_LOCAL_NAME:
+ set_local_name_complete(sk, index, ev->data, len);
+ break;
+ case MGMT_OP_READ_LOCAL_OOB_DATA:
+ read_local_oob_data_complete(sk, index, ev->data, len);
+ break;
+ case MGMT_OP_ADD_REMOTE_OOB_DATA:
+ DBG("add_remote_oob_data complete");
+ break;
+ case MGMT_OP_REMOVE_REMOTE_OOB_DATA:
+ DBG("remove_remote_oob_data complete");
+ break;
+ default:
+ error("Unknown command complete for opcode %u", opcode);
+ break;
+ }
+}
+
+static void mgmt_cmd_status(int sk, uint16_t index, void *buf, size_t len)
+{
+ struct mgmt_ev_cmd_status *ev = buf;
+ uint16_t opcode;
+
+ if (len < sizeof(*ev)) {
+ error("Too small management command status event packet");
+ return;
+ }
+
+ opcode = btohs(bt_get_unaligned(&ev->opcode));
+
+ DBG("status %u opcode %u (index %u)", ev->status, opcode, index);
+
+ switch (opcode) {
+ case MGMT_OP_READ_LOCAL_OOB_DATA:
+ read_local_oob_data_failed(sk, index);
+ break;
+ }
+}
+
+static void mgmt_controller_error(int sk, uint16_t index, void *buf, size_t len)
+{
+ struct mgmt_ev_controller_error *ev = buf;
+
+ if (len < sizeof(*ev)) {
+ error("Too small management controller error event packet");
+ return;
+ }
+
+ DBG("index %u error_code %u", index, ev->error_code);
+}
+
+static void mgmt_auth_failed(int sk, uint16_t index, void *buf, size_t len)
+{
+ struct controller_info *info;
+ struct mgmt_ev_auth_failed *ev = buf;
+
+ if (len < sizeof(*ev)) {
+ error("Too small mgmt_auth_failed event packet");
+ return;
+ }
+
+ DBG("hci%u auth failed status %u", index, ev->status);
+
+ if (index > max_index) {
+ error("Unexpected index %u in auth_failed event", index);
+ return;
+ }
+
+ info = &controllers[index];
+
+ btd_event_bonding_complete(&info->bdaddr, &ev->bdaddr, ev->status);
+}
+
+static void mgmt_local_name_changed(int sk, uint16_t index, void *buf, size_t len)
+{
+ struct mgmt_cp_set_local_name *ev = buf;
+ struct controller_info *info;
+ struct btd_adapter *adapter;
+
+ if (len < sizeof(*ev)) {
+ error("Too small mgmt_local_name_changed event packet");
+ return;
+ }
+
+ DBG("hci%u local name changed: %s", index, (char *) ev->name);
+
+ if (index > max_index) {
+ error("Unexpected index %u in name_changed event", index);
+ return;
+ }
+
+ info = &controllers[index];
+
+ adapter = manager_find_adapter(&info->bdaddr);
+ if (adapter)
+ adapter_update_local_name(adapter, (char *) ev->name);
+}
+
+static gboolean mgmt_event(GIOChannel *io, GIOCondition cond, gpointer user_data)
+{
+ char buf[MGMT_BUF_SIZE];
+ struct mgmt_hdr *hdr = (void *) buf;
+ int sk;
+ ssize_t ret;
+ uint16_t len, opcode, index;
+
+ DBG("cond %d", cond);
+
+ if (cond & G_IO_NVAL)
+ return FALSE;
+
+ sk = g_io_channel_unix_get_fd(io);
+
+ if (cond & (G_IO_ERR | G_IO_HUP)) {
+ error("Error on management socket");
+ return FALSE;
+ }
+
+ ret = read(sk, buf, sizeof(buf));
+ if (ret < 0) {
+ error("Unable to read from management socket: %s (%d)",
+ strerror(errno), errno);
+ return TRUE;
+ }
+
+ DBG("Received %zd bytes from management socket", ret);
+
+ if (ret < MGMT_HDR_SIZE) {
+ error("Too small Management packet");
+ return TRUE;
+ }
+
+ opcode = btohs(bt_get_unaligned(&hdr->opcode));
+ len = btohs(bt_get_unaligned(&hdr->len));
+ index = btohs(bt_get_unaligned(&hdr->index));
+
+ if (ret != MGMT_HDR_SIZE + len) {
+ error("Packet length mismatch. ret %zd len %u", ret, len);
+ return TRUE;
+ }
+
+ switch (opcode) {
+ case MGMT_EV_CMD_COMPLETE:
+ mgmt_cmd_complete(sk, index, buf + MGMT_HDR_SIZE, len);
+ break;
+ case MGMT_EV_CMD_STATUS:
+ mgmt_cmd_status(sk, index, buf + MGMT_HDR_SIZE, len);
+ break;
+ case MGMT_EV_CONTROLLER_ERROR:
+ mgmt_controller_error(sk, index, buf + MGMT_HDR_SIZE, len);
+ break;
+ case MGMT_EV_INDEX_ADDED:
+ mgmt_index_added(sk, index);
+ break;
+ case MGMT_EV_INDEX_REMOVED:
+ mgmt_index_removed(sk, index);
+ break;
+ case MGMT_EV_POWERED:
+ mgmt_powered(sk, index, buf + MGMT_HDR_SIZE, len);
+ break;
+ case MGMT_EV_DISCOVERABLE:
+ mgmt_discoverable(sk, index, buf + MGMT_HDR_SIZE, len);
+ break;
+ case MGMT_EV_CONNECTABLE:
+ mgmt_connectable(sk, index, buf + MGMT_HDR_SIZE, len);
+ break;
+ case MGMT_EV_PAIRABLE:
+ mgmt_pairable(sk, index, buf + MGMT_HDR_SIZE, len);
+ break;
+ case MGMT_EV_NEW_KEY:
+ mgmt_new_key(sk, index, buf + MGMT_HDR_SIZE, len);
+ break;
+ case MGMT_EV_DEVICE_CONNECTED:
+ mgmt_device_connected(sk, index, buf + MGMT_HDR_SIZE, len);
+ break;
+ case MGMT_EV_DEVICE_DISCONNECTED:
+ mgmt_device_disconnected(sk, index, buf + MGMT_HDR_SIZE, len);
+ break;
+ case MGMT_EV_CONNECT_FAILED:
+ mgmt_connect_failed(sk, index, buf + MGMT_HDR_SIZE, len);
+ break;
+ case MGMT_EV_PIN_CODE_REQUEST:
+ mgmt_pin_code_request(sk, index, buf + MGMT_HDR_SIZE, len);
+ break;
+ case MGMT_EV_USER_CONFIRM_REQUEST:
+ mgmt_user_confirm_request(sk, index, buf + MGMT_HDR_SIZE, len);
+ break;
+ case MGMT_EV_AUTH_FAILED:
+ mgmt_auth_failed(sk, index, buf + MGMT_HDR_SIZE, len);
+ break;
+ case MGMT_EV_LOCAL_NAME_CHANGED:
+ mgmt_local_name_changed(sk, index, buf + MGMT_HDR_SIZE, len);
+ break;
+ default:
+ error("Unknown Management opcode %u (index %u)", opcode, index);
+ break;
+ }
+
+ return TRUE;
+}
+
+static int mgmt_setup(void)
+{
+ struct mgmt_hdr hdr;
+ struct sockaddr_hci addr;
+ GIOChannel *io;
+ GIOCondition condition;
+ int dd, err;
+
+ dd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
+ if (dd < 0)
+ return -errno;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.hci_family = AF_BLUETOOTH;
+ addr.hci_dev = HCI_DEV_NONE;
+ addr.hci_channel = HCI_CHANNEL_CONTROL;
+
+ if (bind(dd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ err = -errno;
+ goto fail;
+ }
+
+ memset(&hdr, 0, sizeof(hdr));
+ hdr.opcode = htobs(MGMT_OP_READ_VERSION);
+ hdr.index = htobs(MGMT_INDEX_NONE);
+ if (write(dd, &hdr, sizeof(hdr)) < 0) {
+ err = -errno;
+ goto fail;
+ }
+
+ io = g_io_channel_unix_new(dd);
+ condition = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+ mgmt_watch = g_io_add_watch(io, condition, mgmt_event, NULL);
+ g_io_channel_unref(io);
+
+ mgmt_sock = dd;
+
+ info("Bluetooth Management interface initialized");
+
+ return 0;
+
+fail:
+ close(dd);
+ return err;
+}
+
+static void mgmt_cleanup(void)
+{
+ g_free(controllers);
+ controllers = NULL;
+ max_index = -1;
+
+ if (mgmt_sock >= 0) {
+ close(mgmt_sock);
+ mgmt_sock = -1;
+ }
+
+ if (mgmt_watch > 0) {
+ g_source_remove(mgmt_watch);
+ mgmt_watch = 0;
+ }
+}
+
+static int mgmt_set_dev_class(int index, uint8_t major, uint8_t minor)
+{
+ char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_set_dev_class)];
+ struct mgmt_hdr *hdr = (void *) buf;
+ struct mgmt_cp_set_dev_class *cp = (void *) &buf[sizeof(*hdr)];
+
+ DBG("index %d major %u minor %u", index, major, minor);
+
+ memset(buf, 0, sizeof(buf));
+ hdr->opcode = htobs(MGMT_OP_SET_DEV_CLASS);
+ hdr->len = htobs(sizeof(*cp));
+ hdr->index = htobs(index);
+
+ cp->major = major;
+ cp->minor = minor;
+
+ if (write(mgmt_sock, buf, sizeof(buf)) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int mgmt_set_limited_discoverable(int index, gboolean limited)
+{
+ DBG("index %d limited %d", index, limited);
+ return -ENOSYS;
+}
+
+static int mgmt_start_inquiry(int index, uint8_t length, gboolean periodic)
+{
+ DBG("index %d length %u periodic %d", index, length, periodic);
+ return -ENOSYS;
+}
+
+static int mgmt_stop_inquiry(int index)
+{
+ DBG("index %d", index);
+ return -ENOSYS;
+}
+
+static int mgmt_start_scanning(int index)
+{
+ DBG("index %d", index);
+ return -ENOSYS;
+}
+
+static int mgmt_stop_scanning(int index)
+{
+ DBG("index %d", index);
+ return -ENOSYS;
+}
+
+static int mgmt_resolve_name(int index, bdaddr_t *bdaddr)
+{
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+ DBG("index %d addr %s", index, addr);
+
+ return -ENOSYS;
+}
+
+static int mgmt_set_name(int index, const char *name)
+{
+ char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_set_local_name)];
+ struct mgmt_hdr *hdr = (void *) buf;
+ struct mgmt_cp_set_local_name *cp = (void *) &buf[sizeof(*hdr)];
+
+ DBG("index %d, name %s", index, name);
+
+ memset(buf, 0, sizeof(buf));
+ hdr->opcode = htobs(MGMT_OP_SET_LOCAL_NAME);
+ hdr->len = htobs(sizeof(*cp));
+ hdr->index = htobs(index);
+
+ strncpy((char *) cp->name, name, sizeof(cp->name) - 1);
+
+ if (write(mgmt_sock, buf, sizeof(buf)) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int mgmt_cancel_resolve_name(int index, bdaddr_t *bdaddr)
+{
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+ DBG("index %d addr %s", index, addr);
+
+ return -ENOSYS;
+}
+
+static int mgmt_fast_connectable(int index, gboolean enable)
+{
+ DBG("index %d enable %d", index, enable);
+ return -ENOSYS;
+}
+
+static int mgmt_read_clock(int index, bdaddr_t *bdaddr, int which, int timeout,
+ uint32_t *clock, uint16_t *accuracy)
+{
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+ DBG("index %d addr %s which %d timeout %d", index, addr, which,
+ timeout);
+
+ return -ENOSYS;
+}
+
+static int mgmt_read_bdaddr(int index, bdaddr_t *bdaddr)
+{
+ char addr[18];
+ struct controller_info *info = &controllers[index];
+
+ ba2str(&info->bdaddr, addr);
+ DBG("index %d addr %s", index, addr);
+
+ if (!info->valid)
+ return -ENODEV;
+
+ bacpy(bdaddr, &info->bdaddr);
+
+ return 0;
+}
+
+static int mgmt_block_device(int index, bdaddr_t *bdaddr)
+{
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+ DBG("index %d addr %s", index, addr);
+
+ return -ENOSYS;
+}
+
+static int mgmt_unblock_device(int index, bdaddr_t *bdaddr)
+{
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+ DBG("index %d addr %s", index, addr);
+
+ return -ENOSYS;
+}
+
+static int mgmt_get_conn_list(int index, GSList **conns)
+{
+ struct controller_info *info = &controllers[index];
+
+ DBG("index %d", index);
+
+ *conns = info->connections;
+ info->connections = NULL;
+
+ return 0;
+}
+
+static int mgmt_read_local_version(int index, struct hci_version *ver)
+{
+ struct controller_info *info = &controllers[index];
+
+ DBG("index %d", index);
+
+ if (!info->valid)
+ return -ENODEV;
+
+ memset(ver, 0, sizeof(*ver));
+ ver->manufacturer = info->manufacturer;
+ ver->hci_ver = info->hci_ver;
+ ver->hci_rev = info->hci_rev;
+
+ return 0;
+}
+
+static int mgmt_read_local_features(int index, uint8_t *features)
+{
+ struct controller_info *info = &controllers[index];
+
+ DBG("index %d", index);
+
+ if (!info->valid)
+ return -ENODEV;
+
+ memcpy(features, info->features, 8);
+
+ return 0;
+}
+
+static int mgmt_disconnect(int index, bdaddr_t *bdaddr)
+{
+ char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_disconnect)];
+ struct mgmt_hdr *hdr = (void *) buf;
+ struct mgmt_cp_disconnect *cp = (void *) &buf[sizeof(*hdr)];
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+ DBG("index %d %s", index, addr);
+
+ memset(buf, 0, sizeof(buf));
+ hdr->opcode = htobs(MGMT_OP_DISCONNECT);
+ hdr->len = htobs(sizeof(*cp));
+ hdr->index = htobs(index);
+
+ bacpy(&cp->bdaddr, bdaddr);
+
+ if (write(mgmt_sock, buf, sizeof(buf)) < 0)
+ error("write: %s (%d)", strerror(errno), errno);
+
+ return 0;
+}
+
+static int mgmt_remove_bonding(int index, bdaddr_t *bdaddr)
+{
+ char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_remove_key)];
+ struct mgmt_hdr *hdr = (void *) buf;
+ struct mgmt_cp_remove_key *cp = (void *) &buf[sizeof(*hdr)];
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+ DBG("index %d addr %s", index, addr);
+
+ memset(buf, 0, sizeof(buf));
+ hdr->opcode = htobs(MGMT_OP_REMOVE_KEY);
+ hdr->len = htobs(sizeof(*cp));
+ hdr->index = htobs(index);
+
+ bacpy(&cp->bdaddr, bdaddr);
+ cp->disconnect = 1;
+
+ if (write(mgmt_sock, buf, sizeof(buf)) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int mgmt_passkey_reply(int index, bdaddr_t *bdaddr, uint32_t passkey)
+{
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+ DBG("index %d addr %s passkey %06u", index, addr, passkey);
+
+ return -ENOSYS;
+}
+
+static int mgmt_enable_le(int index)
+{
+ DBG("index %d", index);
+ return -ENOSYS;
+}
+
+static int mgmt_encrypt_link(int index, bdaddr_t *dst, bt_hci_result_t cb,
+ gpointer user_data)
+{
+ char addr[18];
+
+ ba2str(dst, addr);
+ DBG("index %d addr %s", index, addr);
+
+ return -ENOSYS;
+}
+
+static int mgmt_set_did(int index, uint16_t vendor, uint16_t product,
+ uint16_t version)
+{
+ DBG("index %d vendor %u product %u version %u",
+ index, vendor, product, version);
+ return -ENOSYS;
+}
+
+static int mgmt_disable_cod_cache(int index)
+{
+ DBG("index %d", index);
+ return mgmt_set_mode(index, MGMT_OP_SET_SERVICE_CACHE, 0);
+}
+
+static int mgmt_restore_powered(int index)
+{
+ DBG("index %d", index);
+ return -ENOSYS;
+}
+
+static int mgmt_load_keys(int index, GSList *keys, gboolean debug_keys)
+{
+ char *buf;
+ struct mgmt_hdr *hdr;
+ struct mgmt_cp_load_keys *cp;
+ struct mgmt_key_info *key;
+ size_t key_count, cp_size;
+ GSList *l;
+ int err;
+
+ key_count = g_slist_length(keys);
+
+ DBG("index %d keys %zu debug_keys %d", index, key_count, debug_keys);
+
+ cp_size = sizeof(*cp) + (key_count * sizeof(*key));
+
+ buf = g_try_malloc0(sizeof(*hdr) + cp_size);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ memset(buf, 0, sizeof(buf));
+
+ hdr = (void *) buf;
+ hdr->opcode = htobs(MGMT_OP_LOAD_KEYS);
+ hdr->len = htobs(cp_size);
+ hdr->index = htobs(index);
+
+ cp = (void *) (buf + sizeof(*hdr));
+ cp->debug_keys = debug_keys;
+ cp->key_count = htobs(key_count);
+
+ for (l = keys, key = cp->keys; l != NULL; l = g_slist_next(l), key++) {
+ struct link_key_info *info = l->data;
+
+ bacpy(&key->bdaddr, &info->bdaddr);
+ key->type = info->type;
+ memcpy(key->val, info->key, 16);
+ key->pin_len = info->pin_len;
+ }
+
+ if (write(mgmt_sock, buf, sizeof(*hdr) + cp_size) < 0)
+ err = -errno;
+ else
+ err = 0;
+
+ g_free(buf);
+
+ return err;
+}
+
+static int mgmt_set_io_capability(int index, uint8_t io_capability)
+{
+ char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_set_io_capability)];
+ struct mgmt_hdr *hdr = (void *) buf;
+ struct mgmt_cp_set_io_capability *cp = (void *) &buf[sizeof(*hdr)];
+
+ DBG("hci%d io_capability 0x%02x", index, io_capability);
+
+ memset(buf, 0, sizeof(buf));
+ hdr->opcode = htobs(MGMT_OP_SET_IO_CAPABILITY);
+ hdr->len = htobs(sizeof(*cp));
+ hdr->index = htobs(index);
+
+ cp->io_capability = io_capability;
+
+ if (write(mgmt_sock, buf, sizeof(buf)) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int mgmt_create_bonding(int index, bdaddr_t *bdaddr, uint8_t io_cap)
+{
+ char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_pair_device)];
+ struct mgmt_hdr *hdr = (void *) buf;
+ struct mgmt_cp_pair_device *cp = (void *) &buf[sizeof(*hdr)];
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+ DBG("hci%d bdaddr %s io_cap 0x%02x", index, addr, io_cap);
+
+ memset(buf, 0, sizeof(buf));
+ hdr->opcode = htobs(MGMT_OP_PAIR_DEVICE);
+ hdr->len = htobs(sizeof(*cp));
+ hdr->index = htobs(index);
+
+ bacpy(&cp->bdaddr, bdaddr);
+ cp->io_cap = io_cap;
+
+ if (write(mgmt_sock, &buf, sizeof(buf)) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int mgmt_cancel_bonding(int index, bdaddr_t *bdaddr)
+{
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+ DBG("hci%d bdaddr %s", index, addr);
+
+ return -ENOSYS;
+}
+
+static int mgmt_read_local_oob_data(int index)
+{
+ struct mgmt_hdr hdr;
+
+ DBG("hci%d", index);
+
+ hdr.opcode = htobs(MGMT_OP_READ_LOCAL_OOB_DATA);
+ hdr.len = 0;
+ hdr.index = htobs(index);
+
+ if (write(mgmt_sock, &hdr, sizeof(hdr)) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int mgmt_add_remote_oob_data(int index, bdaddr_t *bdaddr,
+ uint8_t *hash, uint8_t *randomizer)
+{
+ char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_add_remote_oob_data)];
+ struct mgmt_hdr *hdr = (void *) buf;
+ struct mgmt_cp_add_remote_oob_data *cp = (void *) &buf[sizeof(*hdr)];
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+ DBG("hci%d bdaddr %s", index, addr);
+
+ memset(buf, 0, sizeof(buf));
+
+ hdr->opcode = htobs(MGMT_OP_ADD_REMOTE_OOB_DATA);
+ hdr->index = htobs(index);
+ hdr->len = htobs(sizeof(*cp));
+
+ bacpy(&cp->bdaddr, bdaddr);
+ memcpy(cp->hash, hash, 16);
+ memcpy(cp->randomizer, randomizer, 16);
+
+ if (write(mgmt_sock, &buf, sizeof(buf)) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int mgmt_remove_remote_oob_data(int index, bdaddr_t *bdaddr)
+{
+ char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_remove_remote_oob_data)];
+ struct mgmt_hdr *hdr = (void *) buf;
+ struct mgmt_cp_remove_remote_oob_data *cp = (void *) &buf[sizeof(*hdr)];
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+ DBG("hci%d bdaddr %s", index, addr);
+
+ memset(buf, 0, sizeof(buf));
+
+ hdr->opcode = htobs(MGMT_OP_REMOVE_REMOTE_OOB_DATA);
+ hdr->index = htobs(index);
+ hdr->len = htobs(sizeof(*cp));
+
+ bacpy(&cp->bdaddr, bdaddr);
+
+ if (write(mgmt_sock, &buf, sizeof(buf)) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static struct btd_adapter_ops mgmt_ops = {
+ .setup = mgmt_setup,
+ .cleanup = mgmt_cleanup,
+ .set_powered = mgmt_set_powered,
+ .set_discoverable = mgmt_set_discoverable,
+ .set_pairable = mgmt_set_pairable,
+ .set_limited_discoverable = mgmt_set_limited_discoverable,
+ .start_inquiry = mgmt_start_inquiry,
+ .stop_inquiry = mgmt_stop_inquiry,
+ .start_scanning = mgmt_start_scanning,
+ .stop_scanning = mgmt_stop_scanning,
+ .resolve_name = mgmt_resolve_name,
+ .cancel_resolve_name = mgmt_cancel_resolve_name,
+ .set_name = mgmt_set_name,
+ .set_dev_class = mgmt_set_dev_class,
+ .set_fast_connectable = mgmt_fast_connectable,
+ .read_clock = mgmt_read_clock,
+ .read_bdaddr = mgmt_read_bdaddr,
+ .block_device = mgmt_block_device,
+ .unblock_device = mgmt_unblock_device,
+ .get_conn_list = mgmt_get_conn_list,
+ .read_local_version = mgmt_read_local_version,
+ .read_local_features = mgmt_read_local_features,
+ .disconnect = mgmt_disconnect,
+ .remove_bonding = mgmt_remove_bonding,
+ .pincode_reply = mgmt_pincode_reply,
+ .confirm_reply = mgmt_confirm_reply,
+ .passkey_reply = mgmt_passkey_reply,
+ .enable_le = mgmt_enable_le,
+ .encrypt_link = mgmt_encrypt_link,
+ .set_did = mgmt_set_did,
+ .add_uuid = mgmt_add_uuid,
+ .remove_uuid = mgmt_remove_uuid,
+ .disable_cod_cache = mgmt_disable_cod_cache,
+ .restore_powered = mgmt_restore_powered,
+ .load_keys = mgmt_load_keys,
+ .set_io_capability = mgmt_set_io_capability,
+ .create_bonding = mgmt_create_bonding,
+ .cancel_bonding = mgmt_cancel_bonding,
+ .read_local_oob_data = mgmt_read_local_oob_data,
+ .add_remote_oob_data = mgmt_add_remote_oob_data,
+ .remove_remote_oob_data = mgmt_remove_remote_oob_data,
+};
+
+static int mgmt_init(void)
+{
+ return btd_register_adapter_ops(&mgmt_ops, TRUE);
+}
+
+static void mgmt_exit(void)
+{
+ btd_adapter_cleanup_ops(&mgmt_ops);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(mgmtops, VERSION,
+ BLUETOOTH_PLUGIN_PRIORITY_LOW, mgmt_init, mgmt_exit)
diff --git a/plugins/pnat.c b/plugins/pnat.c
new file mode 100644
index 0000000..2d73910
--- /dev/null
+++ b/plugins/pnat.c
@@ -0,0 +1,526 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2010 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 <errno.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.h>
+
+#include <gdbus.h>
+
+#include "plugin.h"
+#include "sdpd.h"
+#include "btio.h"
+#include "adapter.h"
+#include "log.h"
+
+/* FIXME: This location should be build-time configurable */
+#define PNATD "/usr/bin/phonet-at"
+
+#define DUN_CHANNEL 1
+#define DUN_UUID "00001103-0000-1000-8000-00805F9B34FB"
+
+#define TTY_TIMEOUT 100
+#define TTY_TRIES 10
+
+struct dun_client {
+ bdaddr_t bda;
+
+ GIOChannel *io; /* Client socket */
+ guint io_watch; /* Client IO watch id */
+
+ guint tty_timer;
+ int tty_tries;
+ gboolean tty_open;
+ int tty_id;
+ char tty_name[PATH_MAX];
+
+ GPid pnatd_pid;
+};
+
+struct dun_server {
+ bdaddr_t bda; /* Local adapter address */
+
+ uint32_t record_handle; /* Local SDP record handle */
+ GIOChannel *server; /* Server socket */
+
+ int rfcomm_ctl;
+
+ struct dun_client client;
+};
+
+static GSList *servers = NULL;
+
+static void disconnect(struct dun_server *server)
+{
+ struct dun_client *client = &server->client;
+
+ if (!client->io)
+ return;
+
+ if (client->io_watch > 0) {
+ g_source_remove(client->io_watch);
+ client->io_watch = 0;
+ }
+
+ g_io_channel_shutdown(client->io, TRUE, NULL);
+ g_io_channel_unref(client->io);
+ client->io = NULL;
+
+ if (client->pnatd_pid > 0) {
+ kill(client->pnatd_pid, SIGTERM);
+ client->pnatd_pid = 0;
+ }
+
+ if (client->tty_timer > 0) {
+ g_source_remove(client->tty_timer);
+ client->tty_timer = 0;
+ }
+
+ if (client->tty_id >= 0) {
+ struct rfcomm_dev_req req;
+
+ memset(&req, 0, sizeof(req));
+ req.dev_id = client->tty_id;
+ req.flags = (1 << RFCOMM_HANGUP_NOW);
+ ioctl(server->rfcomm_ctl, RFCOMMRELEASEDEV, &req);
+
+ client->tty_name[0] = '\0';
+ client->tty_open = FALSE;
+ client->tty_id = -1;
+ }
+}
+
+static gboolean client_event(GIOChannel *chan,
+ GIOCondition cond, gpointer data)
+{
+ struct dun_server *server = data;
+ struct dun_client *client = &server->client;
+ char addr[18];
+
+ ba2str(&client->bda, addr);
+
+ DBG("Disconnected DUN from %s (%s)", addr, client->tty_name);
+
+ client->io_watch = 0;
+ disconnect(server);
+
+ return FALSE;
+}
+
+static void pnatd_exit(GPid pid, gint status, gpointer user_data)
+{
+ struct dun_server *server = user_data;
+ struct dun_client *client = &server->client;
+
+ if (WIFEXITED(status))
+ DBG("pnatd (%d) exited with status %d", pid,
+ WEXITSTATUS(status));
+ else
+ DBG("pnatd (%d) was killed by signal %d", pid,
+ WTERMSIG(status));
+ g_spawn_close_pid(pid);
+
+ if (pid != client->pnatd_pid)
+ return;
+
+ /* So disconnect() doesn't send SIGTERM to a non-existing process */
+ client->pnatd_pid = 0;
+
+ disconnect(server);
+}
+
+static gboolean start_pnatd(struct dun_server *server)
+{
+ struct dun_client *client = &server->client;
+ GSpawnFlags flags = G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH;
+ char *argv[] = { PNATD, client->tty_name, NULL };
+ GError *err = NULL;
+ GPid pid;
+
+ g_spawn_async(NULL, argv, NULL, flags, NULL, NULL, &pid, &err);
+ if (err != NULL) {
+ error("Unable to spawn pnatd: %s", err->message);
+ g_error_free(err);
+ return FALSE;
+ }
+
+ DBG("pnatd started for %s with pid %d", client->tty_name, pid);
+
+ client->pnatd_pid = pid;
+
+ /* We do not store the GSource id since g_remove_source doesn't
+ * make sense for a child watch. If the callback gets removed
+ * waitpid won't be called and the child remains as a zombie)
+ */
+ g_child_watch_add(pid, pnatd_exit, server);
+
+ return TRUE;
+}
+
+static gboolean tty_try_open(gpointer user_data)
+{
+ struct dun_server *server = user_data;
+ struct dun_client *client = &server->client;
+ int tty_fd;
+
+ tty_fd = open(client->tty_name, O_RDONLY | O_NOCTTY);
+ if (tty_fd < 0) {
+ if (errno == EACCES)
+ goto disconnect;
+
+ client->tty_tries--;
+
+ if (client->tty_tries <= 0)
+ goto disconnect;
+
+ return TRUE;
+ }
+
+ DBG("%s created for DUN", client->tty_name);
+
+ client->tty_open = TRUE;
+ client->tty_timer = 0;
+
+ g_io_channel_unref(client->io);
+ g_source_remove(client->io_watch);
+
+ client->io = g_io_channel_unix_new(tty_fd);
+ client->io_watch = g_io_add_watch(client->io,
+ G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ client_event, server);
+
+ if (!start_pnatd(server))
+ goto disconnect;
+
+ return FALSE;
+
+disconnect:
+ client->tty_timer = 0;
+ disconnect(server);
+ return FALSE;
+}
+
+static gboolean create_tty(struct dun_server *server)
+{
+ struct dun_client *client = &server->client;
+ struct rfcomm_dev_req req;
+ int sk = g_io_channel_unix_get_fd(client->io);
+
+ memset(&req, 0, sizeof(req));
+ req.dev_id = -1;
+ req.flags = (1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP);
+
+ bacpy(&req.src, &server->bda);
+ bacpy(&req.dst, &client->bda);
+
+ bt_io_get(client->io, BT_IO_RFCOMM, NULL,
+ BT_IO_OPT_DEST_CHANNEL, &req.channel,
+ BT_IO_OPT_INVALID);
+
+ client->tty_id = ioctl(sk, RFCOMMCREATEDEV, &req);
+ if (client->tty_id < 0) {
+ error("Can't create RFCOMM TTY: %s", strerror(errno));
+ return FALSE;
+ }
+
+ snprintf(client->tty_name, PATH_MAX - 1, "/dev/rfcomm%d",
+ client->tty_id);
+
+ client->tty_tries = TTY_TRIES;
+
+ tty_try_open(server);
+ if (!client->tty_open && client->tty_tries > 0)
+ client->tty_timer = g_timeout_add(TTY_TIMEOUT,
+ tty_try_open, server);
+
+ return TRUE;
+}
+
+static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
+{
+ struct dun_server *server = user_data;
+
+ if (err) {
+ error("Accepting DUN connection failed: %s", err->message);
+ disconnect(server);
+ return;
+ }
+
+ if (!create_tty(server)) {
+ error("Device creation failed");
+ disconnect(server);
+ }
+}
+
+static void auth_cb(DBusError *derr, void *user_data)
+{
+ struct dun_server *server = user_data;
+ struct dun_client *client = &server->client;
+ GError *err = NULL;
+
+ if (derr && dbus_error_is_set(derr)) {
+ error("DUN access denied: %s", derr->message);
+ goto drop;
+ }
+
+ if (!bt_io_accept(client->io, connect_cb, server, NULL, &err)) {
+ error("bt_io_accept: %s", err->message);
+ g_error_free(err);
+ goto drop;
+ }
+
+ return;
+
+drop:
+ disconnect(server);
+}
+
+static gboolean auth_watch(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+ struct dun_server *server = data;
+ struct dun_client *client = &server->client;
+
+ error("DUN client disconnected while waiting for authorization");
+
+ btd_cancel_authorization(&server->bda, &client->bda);
+
+ disconnect(server);
+
+ return FALSE;
+}
+
+static void confirm_cb(GIOChannel *io, gpointer user_data)
+{
+ struct dun_server *server = user_data;
+ struct dun_client *client = &server->client;
+ GError *err = NULL;
+
+ if (client->io) {
+ error("Rejecting DUN connection since one already exists");
+ return;
+ }
+
+ bt_io_get(io, BT_IO_RFCOMM, &err,
+ BT_IO_OPT_DEST_BDADDR, &client->bda,
+ BT_IO_OPT_INVALID);
+ if (err != NULL) {
+ error("Unable to get DUN source and dest address: %s",
+ err->message);
+ g_error_free(err);
+ return;
+ }
+
+ if (btd_request_authorization(&server->bda, &client->bda, DUN_UUID,
+ auth_cb, user_data) < 0) {
+ error("Requesting DUN authorization failed");
+ return;
+ }
+
+ client->io_watch = g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ (GIOFunc) auth_watch, server);
+ client->io = g_io_channel_ref(io);
+}
+
+static sdp_record_t *dun_record(uint8_t ch)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root, *aproto;
+ uuid_t root_uuid, dun, gn, l2cap, rfcomm;
+ sdp_profile_desc_t profile;
+ sdp_list_t *proto[2];
+ sdp_record_t *record;
+ sdp_data_t *channel;
+
+ record = sdp_record_alloc();
+ if (!record)
+ return NULL;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(record, root);
+
+ sdp_uuid16_create(&dun, DIALUP_NET_SVCLASS_ID);
+ svclass_id = sdp_list_append(NULL, &dun);
+ sdp_uuid16_create(&gn, GENERIC_NETWORKING_SVCLASS_ID);
+ svclass_id = sdp_list_append(svclass_id, &gn);
+ sdp_set_service_classes(record, svclass_id);
+
+ sdp_uuid16_create(&profile.uuid, DIALUP_NET_PROFILE_ID);
+ profile.version = 0x0100;
+ pfseq = sdp_list_append(NULL, &profile);
+ sdp_set_profile_descs(record, pfseq);
+
+ sdp_uuid16_create(&l2cap, L2CAP_UUID);
+ proto[0] = sdp_list_append(NULL, &l2cap);
+ apseq = sdp_list_append(NULL, proto[0]);
+
+ sdp_uuid16_create(&rfcomm, RFCOMM_UUID);
+ proto[1] = sdp_list_append(NULL, &rfcomm);
+ channel = sdp_data_alloc(SDP_UINT8, &ch);
+ proto[1] = sdp_list_append(proto[1], channel);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(record, aproto);
+
+ sdp_set_info_attr(record, "Dial-Up Networking", 0, 0);
+
+ sdp_data_free(channel);
+ sdp_list_free(root, NULL);
+ sdp_list_free(svclass_id, NULL);
+ sdp_list_free(proto[0], NULL);
+ sdp_list_free(proto[1], NULL);
+ sdp_list_free(pfseq, NULL);
+ sdp_list_free(apseq, NULL);
+ sdp_list_free(aproto, NULL);
+
+ return record;
+}
+
+static gint server_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct dun_server *server = a;
+ const bdaddr_t *src = b;
+
+ return bacmp(src, &server->bda);
+}
+
+static int pnat_probe(struct btd_adapter *adapter)
+{
+ struct dun_server *server;
+ GIOChannel *io;
+ GError *err = NULL;
+ sdp_record_t *record;
+ bdaddr_t src;
+
+ adapter_get_address(adapter, &src);
+
+ server = g_new0(struct dun_server, 1);
+
+ io = bt_io_listen(BT_IO_RFCOMM, NULL, confirm_cb, server, NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &src,
+ BT_IO_OPT_CHANNEL, DUN_CHANNEL,
+ BT_IO_OPT_INVALID);
+ if (err != NULL) {
+ error("Failed to start DUN server: %s", err->message);
+ g_error_free(err);
+ goto fail;
+ }
+
+ record = dun_record(DUN_CHANNEL);
+ if (!record) {
+ error("Unable to allocate new service record");
+ goto fail;
+ }
+
+ if (add_record_to_server(&src, record) < 0) {
+ error("Unable to register DUN service record");
+ goto fail;
+ }
+
+ server->rfcomm_ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_RFCOMM);
+ if (server->rfcomm_ctl < 0) {
+ error("Unable to create RFCOMM control socket: %s (%d)",
+ strerror(errno), errno);
+ goto fail;
+ }
+
+ server->server = io;
+ server->record_handle = record->handle;
+ bacpy(&server->bda, &src);
+
+ servers = g_slist_append(servers, server);
+
+ return 0;
+
+fail:
+ if (io != NULL)
+ g_io_channel_unref(io);
+ g_free(server);
+ return -EIO;
+}
+
+static void pnat_remove(struct btd_adapter *adapter)
+{
+ struct dun_server *server;
+ GSList *match;
+ bdaddr_t src;
+
+ adapter_get_address(adapter, &src);
+
+ match = g_slist_find_custom(servers, &src, server_cmp);
+ if (match == NULL)
+ return;
+
+ server = match->data;
+
+ servers = g_slist_delete_link(servers, match);
+
+ disconnect(server);
+
+ remove_record_from_server(server->record_handle);
+ close(server->rfcomm_ctl);
+ g_io_channel_shutdown(server->server, TRUE, NULL);
+ g_io_channel_unref(server->server);
+ g_free(server);
+}
+
+static struct btd_adapter_driver pnat_server = {
+ .name = "pnat-server",
+ .probe = pnat_probe,
+ .remove = pnat_remove,
+};
+
+static int pnat_init(void)
+{
+ DBG("Setup Phonet AT (DUN) plugin");
+
+ return btd_register_adapter_driver(&pnat_server);
+}
+
+static void pnat_exit(void)
+{
+ DBG("Cleanup Phonet AT (DUN) plugin");
+
+ btd_unregister_adapter_driver(&pnat_server);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(pnat, VERSION,
+ BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+ pnat_init, pnat_exit)
diff --git a/plugins/service.c b/plugins/service.c
new file mode 100644
index 0000000..d73cdea
--- /dev/null
+++ b/plugins/service.c
@@ -0,0 +1,824 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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 <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <gdbus.h>
+
+#include "sdpd.h"
+#include "sdp-xml.h"
+#include "plugin.h"
+#include "adapter.h"
+#include "error.h"
+#include "log.h"
+
+#define SERVICE_INTERFACE "org.bluez.Service"
+
+static DBusConnection *connection;
+
+struct record_data {
+ uint32_t handle;
+ char *sender;
+ guint listener_id;
+ struct service_adapter *serv_adapter;
+};
+
+struct context_data {
+ sdp_record_t *record;
+ sdp_data_t attr_data;
+ struct sdp_xml_data *stack_head;
+ uint16_t attr_id;
+};
+
+struct pending_auth {
+ DBusConnection *conn;
+ DBusMessage *msg;
+ char *sender;
+ bdaddr_t dst;
+ char uuid[MAX_LEN_UUID_STR];
+};
+
+struct service_adapter {
+ struct btd_adapter *adapter;
+ GSList *pending_list;
+ GSList *records;
+};
+
+static struct service_adapter *serv_adapter_any = NULL;
+
+static int compute_seq_size(sdp_data_t *data)
+{
+ int unit_size = data->unitSize;
+ sdp_data_t *seq = data->val.dataseq;
+
+ for (; seq; seq = seq->next)
+ unit_size += seq->unitSize;
+
+ return unit_size;
+}
+
+static void element_start(GMarkupParseContext *context,
+ const gchar *element_name, const gchar **attribute_names,
+ const gchar **attribute_values, gpointer user_data, GError **err)
+{
+ struct context_data *ctx_data = user_data;
+
+ if (!strcmp(element_name, "record"))
+ return;
+
+ if (!strcmp(element_name, "attribute")) {
+ int i;
+ for (i = 0; attribute_names[i]; i++) {
+ if (!strcmp(attribute_names[i], "id")) {
+ ctx_data->attr_id = strtol(attribute_values[i], 0, 0);
+ break;
+ }
+ }
+ DBG("New attribute 0x%04x", ctx_data->attr_id);
+ return;
+ }
+
+ if (ctx_data->stack_head) {
+ struct sdp_xml_data *newelem = sdp_xml_data_alloc();
+ newelem->next = ctx_data->stack_head;
+ ctx_data->stack_head = newelem;
+ } else {
+ ctx_data->stack_head = sdp_xml_data_alloc();
+ ctx_data->stack_head->next = NULL;
+ }
+
+ if (!strcmp(element_name, "sequence"))
+ ctx_data->stack_head->data = sdp_data_alloc(SDP_SEQ8, NULL);
+ else if (!strcmp(element_name, "alternate"))
+ ctx_data->stack_head->data = sdp_data_alloc(SDP_ALT8, NULL);
+ else {
+ int i;
+ /* Parse value, name, encoding */
+ for (i = 0; attribute_names[i]; i++) {
+ if (!strcmp(attribute_names[i], "value")) {
+ int curlen = strlen(ctx_data->stack_head->text);
+ int attrlen = strlen(attribute_values[i]);
+
+ /* Ensure we're big enough */
+ while ((curlen + 1 + attrlen) > ctx_data->stack_head->size) {
+ sdp_xml_data_expand(ctx_data->stack_head);
+ }
+
+ memcpy(ctx_data->stack_head->text + curlen,
+ attribute_values[i], attrlen);
+ ctx_data->stack_head->text[curlen + attrlen] = '\0';
+ }
+
+ if (!strcmp(attribute_names[i], "encoding")) {
+ if (!strcmp(attribute_values[i], "hex"))
+ ctx_data->stack_head->type = 1;
+ }
+
+ if (!strcmp(attribute_names[i], "name")) {
+ ctx_data->stack_head->name = strdup(attribute_values[i]);
+ }
+ }
+
+ ctx_data->stack_head->data = sdp_xml_parse_datatype(element_name,
+ ctx_data->stack_head, ctx_data->record);
+
+ if (ctx_data->stack_head->data == NULL)
+ error("Can't parse element %s", element_name);
+ }
+}
+
+static void element_end(GMarkupParseContext *context,
+ const gchar *element_name, gpointer user_data, GError **err)
+{
+ struct context_data *ctx_data = user_data;
+ struct sdp_xml_data *elem;
+
+ if (!strcmp(element_name, "record"))
+ return;
+
+ if (!strcmp(element_name, "attribute")) {
+ if (ctx_data->stack_head && ctx_data->stack_head->data) {
+ int ret = sdp_attr_add(ctx_data->record, ctx_data->attr_id,
+ ctx_data->stack_head->data);
+ if (ret == -1)
+ DBG("Could not add attribute 0x%04x",
+ ctx_data->attr_id);
+
+ ctx_data->stack_head->data = NULL;
+ sdp_xml_data_free(ctx_data->stack_head);
+ ctx_data->stack_head = NULL;
+ } else {
+ DBG("No data for attribute 0x%04x", ctx_data->attr_id);
+ }
+ return;
+ }
+
+ if (!strcmp(element_name, "sequence")) {
+ ctx_data->stack_head->data->unitSize = compute_seq_size(ctx_data->stack_head->data);
+
+ if (ctx_data->stack_head->data->unitSize > USHRT_MAX) {
+ ctx_data->stack_head->data->unitSize += sizeof(uint32_t);
+ ctx_data->stack_head->data->dtd = SDP_SEQ32;
+ } else if (ctx_data->stack_head->data->unitSize > UCHAR_MAX) {
+ ctx_data->stack_head->data->unitSize += sizeof(uint16_t);
+ ctx_data->stack_head->data->dtd = SDP_SEQ16;
+ } else {
+ ctx_data->stack_head->data->unitSize += sizeof(uint8_t);
+ }
+ } else if (!strcmp(element_name, "alternate")) {
+ ctx_data->stack_head->data->unitSize = compute_seq_size(ctx_data->stack_head->data);
+
+ if (ctx_data->stack_head->data->unitSize > USHRT_MAX) {
+ ctx_data->stack_head->data->unitSize += sizeof(uint32_t);
+ ctx_data->stack_head->data->dtd = SDP_ALT32;
+ } else if (ctx_data->stack_head->data->unitSize > UCHAR_MAX) {
+ ctx_data->stack_head->data->unitSize += sizeof(uint16_t);
+ ctx_data->stack_head->data->dtd = SDP_ALT16;
+ } else {
+ ctx_data->stack_head->data->unitSize += sizeof(uint8_t);
+ }
+ }
+
+ if (ctx_data->stack_head->next && ctx_data->stack_head->data &&
+ ctx_data->stack_head->next->data) {
+ switch (ctx_data->stack_head->next->data->dtd) {
+ case SDP_SEQ8:
+ case SDP_SEQ16:
+ case SDP_SEQ32:
+ case SDP_ALT8:
+ case SDP_ALT16:
+ case SDP_ALT32:
+ ctx_data->stack_head->next->data->val.dataseq =
+ sdp_seq_append(ctx_data->stack_head->next->data->val.dataseq,
+ ctx_data->stack_head->data);
+ ctx_data->stack_head->data = NULL;
+ break;
+ }
+
+ elem = ctx_data->stack_head;
+ ctx_data->stack_head = ctx_data->stack_head->next;
+
+ sdp_xml_data_free(elem);
+ }
+}
+
+static GMarkupParser parser = {
+ element_start, element_end, NULL, NULL, NULL
+};
+
+static sdp_record_t *sdp_xml_parse_record(const char *data, int size)
+{
+ GMarkupParseContext *ctx;
+ struct context_data *ctx_data;
+ sdp_record_t *record;
+
+ ctx_data = malloc(sizeof(*ctx_data));
+ if (!ctx_data)
+ return NULL;
+
+ record = sdp_record_alloc();
+ if (!record) {
+ free(ctx_data);
+ return NULL;
+ }
+
+ memset(ctx_data, 0, sizeof(*ctx_data));
+ ctx_data->record = record;
+
+ ctx = g_markup_parse_context_new(&parser, 0, ctx_data, NULL);
+
+ if (g_markup_parse_context_parse(ctx, data, size, NULL) == FALSE) {
+ error("XML parsing error");
+ g_markup_parse_context_free(ctx);
+ sdp_record_free(record);
+ free(ctx_data);
+ return NULL;
+ }
+
+ g_markup_parse_context_free(ctx);
+
+ free(ctx_data);
+
+ return record;
+}
+
+static struct record_data *find_record(struct service_adapter *serv_adapter,
+ uint32_t handle, const char *sender)
+{
+ GSList *list;
+
+ for (list = serv_adapter->records; list; list = list->next) {
+ struct record_data *data = list->data;
+ if (handle == data->handle && !strcmp(sender, data->sender))
+ return data;
+ }
+
+ return NULL;
+}
+
+static struct pending_auth *next_pending(struct service_adapter *serv_adapter)
+{
+ GSList *l = serv_adapter->pending_list;
+
+ if (l) {
+ struct pending_auth *auth = l->data;
+ return auth;
+ }
+
+ return NULL;
+}
+
+static struct pending_auth *find_pending_by_sender(
+ struct service_adapter *serv_adapter,
+ const char *sender)
+{
+ GSList *l = serv_adapter->pending_list;
+
+ for (; l; l = l->next) {
+ struct pending_auth *auth = l->data;
+ if (g_str_equal(auth->sender, sender))
+ return auth;
+ }
+
+ return NULL;
+}
+
+static void exit_callback(DBusConnection *conn, void *user_data)
+{
+ struct record_data *user_record = user_data;
+ struct service_adapter *serv_adapter = user_record->serv_adapter;
+ struct pending_auth *auth;
+
+ DBG("remove record");
+
+ serv_adapter->records = g_slist_remove(serv_adapter->records,
+ user_record);
+
+ auth = find_pending_by_sender(serv_adapter, user_record->sender);
+ if (auth) {
+ serv_adapter->pending_list = g_slist_remove(serv_adapter->pending_list,
+ auth);
+ g_free(auth);
+ }
+
+ remove_record_from_server(user_record->handle);
+
+ g_free(user_record->sender);
+ g_free(user_record);
+}
+
+static int add_xml_record(DBusConnection *conn, const char *sender,
+ struct service_adapter *serv_adapter,
+ const char *record, dbus_uint32_t *handle)
+{
+ struct record_data *user_record;
+ sdp_record_t *sdp_record;
+ bdaddr_t src;
+
+ sdp_record = sdp_xml_parse_record(record, strlen(record));
+ if (!sdp_record) {
+ error("Parsing of XML service record failed");
+ return -EIO;
+ }
+
+ if (serv_adapter->adapter)
+ adapter_get_address(serv_adapter->adapter, &src);
+ else
+ bacpy(&src, BDADDR_ANY);
+
+ if (add_record_to_server(&src, sdp_record) < 0) {
+ error("Failed to register service record");
+ sdp_record_free(sdp_record);
+ return -EIO;
+ }
+
+ user_record = g_new0(struct record_data, 1);
+ user_record->handle = sdp_record->handle;
+ user_record->sender = g_strdup(sender);
+ user_record->serv_adapter = serv_adapter;
+ user_record->listener_id = g_dbus_add_disconnect_watch(conn, sender,
+ exit_callback, user_record, NULL);
+
+ serv_adapter->records = g_slist_append(serv_adapter->records,
+ user_record);
+
+ DBG("listener_id %d", user_record->listener_id);
+
+ *handle = user_record->handle;
+
+ return 0;
+}
+
+static DBusMessage *update_record(DBusConnection *conn, DBusMessage *msg,
+ struct service_adapter *serv_adapter,
+ dbus_uint32_t handle, sdp_record_t *sdp_record)
+{
+ bdaddr_t src;
+ int err;
+
+ if (remove_record_from_server(handle) < 0) {
+ sdp_record_free(sdp_record);
+ return btd_error_not_available(msg);
+ }
+
+ if (serv_adapter->adapter)
+ adapter_get_address(serv_adapter->adapter, &src);
+ else
+ bacpy(&src, BDADDR_ANY);
+
+ sdp_record->handle = handle;
+ err = add_record_to_server(&src, sdp_record);
+ if (err < 0) {
+ sdp_record_free(sdp_record);
+ error("Failed to update the service record");
+ return btd_error_failed(msg, strerror(-err));
+ }
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *update_xml_record(DBusConnection *conn,
+ DBusMessage *msg,
+ struct service_adapter *serv_adapter)
+{
+ struct record_data *user_record;
+ sdp_record_t *sdp_record;
+ const char *record;
+ dbus_uint32_t handle;
+ int len;
+
+ if (dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_UINT32, &handle,
+ DBUS_TYPE_STRING, &record,
+ DBUS_TYPE_INVALID) == FALSE)
+ return NULL;
+
+ len = (record ? strlen(record) : 0);
+ if (len == 0)
+ return btd_error_invalid_args(msg);
+
+ user_record = find_record(serv_adapter, handle,
+ dbus_message_get_sender(msg));
+ if (!user_record)
+ return btd_error_not_available(msg);
+
+ sdp_record = sdp_xml_parse_record(record, len);
+ if (!sdp_record) {
+ error("Parsing of XML service record failed");
+ sdp_record_free(sdp_record);
+ return btd_error_failed(msg,
+ "Parsing of XML service record failed");
+ }
+
+ return update_record(conn, msg, serv_adapter, handle, sdp_record);
+}
+
+static int remove_record(DBusConnection *conn, const char *sender,
+ struct service_adapter *serv_adapter,
+ dbus_uint32_t handle)
+{
+ struct record_data *user_record;
+
+ DBG("remove record 0x%x", handle);
+
+ user_record = find_record(serv_adapter, handle, sender);
+ if (!user_record)
+ return -1;
+
+ DBG("listner_id %d", user_record->listener_id);
+
+ g_dbus_remove_watch(conn, user_record->listener_id);
+
+ exit_callback(conn, user_record);
+
+ return 0;
+}
+
+static DBusMessage *add_service_record(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct service_adapter *serv_adapter = data;
+ DBusMessage *reply;
+ const char *sender, *record;
+ dbus_uint32_t handle;
+ int err;
+
+ if (dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &record, DBUS_TYPE_INVALID) == FALSE)
+ return NULL;
+
+ sender = dbus_message_get_sender(msg);
+ err = add_xml_record(conn, sender, serv_adapter, record, &handle);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_append_args(reply, DBUS_TYPE_UINT32, &handle,
+ DBUS_TYPE_INVALID);
+
+ return reply;
+}
+
+static DBusMessage *update_service_record(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct service_adapter *serv_adapter = data;
+
+ return update_xml_record(conn, msg, serv_adapter);
+}
+
+static DBusMessage *remove_service_record(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct service_adapter *serv_adapter = data;
+ dbus_uint32_t handle;
+ const char *sender;
+
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &handle,
+ DBUS_TYPE_INVALID) == FALSE)
+ return NULL;
+
+ sender = dbus_message_get_sender(msg);
+
+ if (remove_record(conn, sender, serv_adapter, handle) < 0)
+ return btd_error_not_available(msg);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static void auth_cb(DBusError *derr, void *user_data)
+{
+ struct service_adapter *serv_adapter = user_data;
+ DBusMessage *reply;
+ struct pending_auth *auth;
+ bdaddr_t src;
+
+ auth = next_pending(serv_adapter);
+ if (auth == NULL) {
+ info("Authorization cancelled: Client exited");
+ return;
+ }
+
+ if (derr) {
+ error("Access denied: %s", derr->message);
+
+ reply = btd_error_not_authorized(auth->msg);
+ dbus_message_unref(auth->msg);
+ g_dbus_send_message(auth->conn, reply);
+ goto done;
+ }
+
+ g_dbus_send_reply(auth->conn, auth->msg,
+ DBUS_TYPE_INVALID);
+
+done:
+ dbus_connection_unref(auth->conn);
+
+ serv_adapter->pending_list = g_slist_remove(serv_adapter->pending_list,
+ auth);
+ g_free(auth);
+
+ auth = next_pending(serv_adapter);
+ if (auth == NULL)
+ return;
+
+ if (serv_adapter->adapter)
+ adapter_get_address(serv_adapter->adapter, &src);
+ else
+ bacpy(&src, BDADDR_ANY);
+
+ btd_request_authorization(&src, &auth->dst,
+ auth->uuid, auth_cb, serv_adapter);
+}
+
+static DBusMessage *request_authorization(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct record_data *user_record;
+ struct service_adapter *serv_adapter = data;
+ sdp_record_t *record;
+ sdp_list_t *services;
+ const char *sender;
+ dbus_uint32_t handle;
+ const char *address;
+ struct pending_auth *auth;
+ char uuid_str[MAX_LEN_UUID_STR];
+ uuid_t *uuid, *uuid128;
+ bdaddr_t src;
+
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &address,
+ DBUS_TYPE_UINT32, &handle,
+ DBUS_TYPE_INVALID) == FALSE)
+ return NULL;
+
+ sender = dbus_message_get_sender(msg);
+ if (find_pending_by_sender(serv_adapter, sender))
+ return btd_error_does_not_exist(msg);
+
+ user_record = find_record(serv_adapter, handle, sender);
+ if (!user_record) {
+ user_record = find_record(serv_adapter_any, handle, sender);
+ if (!user_record)
+ return btd_error_not_authorized(msg);
+ }
+
+ record = sdp_record_find(user_record->handle);
+ if (record == NULL)
+ return btd_error_not_authorized(msg);
+
+ if (sdp_get_service_classes(record, &services) < 0) {
+ sdp_record_free(record);
+ return btd_error_not_authorized(msg);
+ }
+
+ if (services == NULL)
+ return btd_error_not_authorized(msg);
+
+ uuid = services->data;
+ uuid128 = sdp_uuid_to_uuid128(uuid);
+
+ sdp_list_free(services, bt_free);
+
+ if (sdp_uuid2strn(uuid128, uuid_str, MAX_LEN_UUID_STR) < 0) {
+ bt_free(uuid128);
+ return btd_error_not_authorized(msg);
+ }
+ bt_free(uuid128);
+
+ auth = g_new0(struct pending_auth, 1);
+ auth->msg = dbus_message_ref(msg);
+ auth->conn = dbus_connection_ref(connection);
+ auth->sender = user_record->sender;
+ memcpy(auth->uuid, uuid_str, MAX_LEN_UUID_STR);
+ str2ba(address, &auth->dst);
+
+ serv_adapter->pending_list = g_slist_append(serv_adapter->pending_list,
+ auth);
+
+ auth = next_pending(serv_adapter);
+ if (auth == NULL)
+ return btd_error_does_not_exist(msg);
+
+ if (serv_adapter->adapter)
+ adapter_get_address(serv_adapter->adapter, &src);
+ else
+ bacpy(&src, BDADDR_ANY);
+
+ if (btd_request_authorization(&src, &auth->dst, auth->uuid, auth_cb,
+ serv_adapter) < 0) {
+ serv_adapter->pending_list = g_slist_remove(serv_adapter->pending_list,
+ auth);
+ g_free(auth);
+ return btd_error_not_authorized(msg);
+ }
+
+ return NULL;
+}
+
+static DBusMessage *cancel_authorization(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessage *reply;
+ struct service_adapter *serv_adapter = data;
+ struct pending_auth *auth;
+ const gchar *sender;
+ bdaddr_t src;
+
+ sender = dbus_message_get_sender(msg);
+
+ auth = find_pending_by_sender(serv_adapter, sender);
+ if (auth == NULL)
+ return btd_error_does_not_exist(msg);
+
+ if (serv_adapter->adapter)
+ adapter_get_address(serv_adapter->adapter, &src);
+ else
+ bacpy(&src, BDADDR_ANY);
+
+ btd_cancel_authorization(&src, &auth->dst);
+
+ reply = btd_error_not_authorized(auth->msg);
+ dbus_message_unref(auth->msg);
+ g_dbus_send_message(auth->conn, reply);
+
+ dbus_connection_unref(auth->conn);
+
+ serv_adapter->pending_list = g_slist_remove(serv_adapter->pending_list,
+ auth);
+ g_free(auth);
+
+ auth = next_pending(serv_adapter);
+ if (auth == NULL)
+ goto done;
+
+ if (serv_adapter->adapter)
+ adapter_get_address(serv_adapter->adapter, &src);
+ else
+ bacpy(&src, BDADDR_ANY);
+
+ btd_request_authorization(&src, &auth->dst,
+ auth->uuid, auth_cb, serv_adapter);
+
+done:
+ return dbus_message_new_method_return(msg);
+}
+
+static GDBusMethodTable service_methods[] = {
+ { "AddRecord", "s", "u", add_service_record },
+ { "UpdateRecord", "us", "", update_service_record },
+ { "RemoveRecord", "u", "", remove_service_record },
+ { "RequestAuthorization","su", "", request_authorization,
+ G_DBUS_METHOD_FLAG_ASYNC},
+ { "CancelAuthorization", "", "", cancel_authorization },
+ { }
+};
+
+static void path_unregister(void *data)
+{
+ struct service_adapter *serv_adapter = data;
+ GSList *l, *next = NULL;
+
+ for (l = serv_adapter->records; l != NULL; l = next) {
+ struct record_data *user_record = l->data;
+
+ next = l->next;
+
+ g_dbus_remove_watch(connection, user_record->listener_id);
+ exit_callback(connection, user_record);
+ }
+
+ g_free(serv_adapter);
+}
+
+static int register_interface(const char *path, struct btd_adapter *adapter)
+{
+ struct service_adapter *serv_adapter;
+
+ DBG("path %s", path);
+
+ serv_adapter = g_try_new0(struct service_adapter, 1);
+ if (serv_adapter == NULL)
+ return -ENOMEM;
+
+ serv_adapter->adapter = adapter;
+ serv_adapter->pending_list = NULL;
+
+ if (g_dbus_register_interface(connection, path, SERVICE_INTERFACE,
+ service_methods, NULL, NULL, serv_adapter,
+ path_unregister) == FALSE) {
+ error("D-Bus failed to register %s interface",
+ SERVICE_INTERFACE);
+ g_free(serv_adapter);
+ return -EIO;
+ }
+
+ DBG("Registered interface %s on path %s", SERVICE_INTERFACE, path);
+
+ if (serv_adapter->adapter == NULL)
+ serv_adapter_any = serv_adapter;
+
+ return 0;
+}
+
+static void unregister_interface(const char *path)
+{
+ DBG("path %s", path);
+
+ g_dbus_unregister_interface(connection, path, SERVICE_INTERFACE);
+}
+
+static int service_probe(struct btd_adapter *adapter)
+{
+ register_interface(adapter_get_path(adapter), adapter);
+
+ return 0;
+}
+
+static void service_remove(struct btd_adapter *adapter)
+{
+ unregister_interface(adapter_get_path(adapter));
+}
+
+static struct btd_adapter_driver service_driver = {
+ .name = "service",
+ .probe = service_probe,
+ .remove = service_remove,
+};
+
+static const char *any_path;
+
+static int service_init(void)
+{
+ int err;
+
+ connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+ if (connection == NULL)
+ return -EIO;
+
+ any_path = btd_adapter_any_request_path();
+ if (any_path != NULL) {
+ if (register_interface(any_path, NULL) < 0) {
+ btd_adapter_any_release_path();
+ any_path = NULL;
+ }
+ }
+
+ err = btd_register_adapter_driver(&service_driver);
+ if (err < 0) {
+ dbus_connection_unref(connection);
+ return err;
+ }
+
+ return 0;
+}
+
+static void service_exit(void)
+{
+ btd_unregister_adapter_driver(&service_driver);
+
+ if (any_path != NULL) {
+ unregister_interface(any_path);
+
+ btd_adapter_any_release_path();
+ any_path = NULL;
+ }
+
+ dbus_connection_unref(connection);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(service, VERSION,
+ BLUETOOTH_PLUGIN_PRIORITY_HIGH, service_init, service_exit)
diff --git a/plugins/storage.c b/plugins/storage.c
new file mode 100644
index 0000000..04a02c7
--- /dev/null
+++ b/plugins/storage.c
@@ -0,0 +1,43 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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 <bluetooth/bluetooth.h>
+
+#include "plugin.h"
+#include "log.h"
+
+static int storage_init(void)
+{
+ return 0;
+}
+
+static void storage_exit(void)
+{
+}
+
+BLUETOOTH_PLUGIN_DEFINE(storage, VERSION,
+ BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, storage_init, storage_exit)
diff --git a/sap/main.c b/sap/main.c
new file mode 100644
index 0000000..c9c90bd
--- /dev/null
+++ b/sap/main.c
@@ -0,0 +1,55 @@
+/*
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 Instituto Nokia de Tecnologia - INdT
+ *
+ * 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 <errno.h>
+#include <gdbus.h>
+#include "plugin.h"
+#include "manager.h"
+
+static DBusConnection *connection;
+
+static int sap_init(void)
+{
+ connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+
+ if (!connection)
+ return -EIO;
+
+ if (sap_manager_init(connection) < 0) {
+ dbus_connection_unref(connection);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void sap_exit(void)
+{
+ sap_manager_exit();
+
+ dbus_connection_unref(connection);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(sap, VERSION,
+ BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, sap_init, sap_exit)
diff --git a/sap/manager.c b/sap/manager.c
new file mode 100644
index 0000000..a97f434
--- /dev/null
+++ b/sap/manager.c
@@ -0,0 +1,93 @@
+/*
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 Instituto Nokia de Tecnologia - INdT
+ *
+ * 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 <errno.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <gdbus.h>
+
+#include "log.h"
+#include "adapter.h"
+#include "device.h"
+
+#include "manager.h"
+#include "server.h"
+
+static DBusConnection *connection = NULL;
+
+static int sap_server_probe(struct btd_adapter *adapter)
+{
+ const char *path = adapter_get_path(adapter);
+ bdaddr_t src;
+
+ DBG("path %s", path);
+
+ adapter_get_address(adapter, &src);
+
+ return sap_server_register(path, &src);
+}
+
+static void sap_server_remove(struct btd_adapter *adapter)
+{
+ const char *path = adapter_get_path(adapter);
+
+ DBG("path %s", path);
+
+ sap_server_unregister(path);
+}
+
+static struct btd_adapter_driver sap_server_driver = {
+ .name = "sap-server",
+ .probe = sap_server_probe,
+ .remove = sap_server_remove,
+};
+
+int sap_manager_init(DBusConnection *conn)
+{
+ connection = dbus_connection_ref(conn);
+
+ if (sap_server_init(connection) < 0) {
+ error("Can't init SAP server");
+ dbus_connection_unref(conn);
+ return -1;
+ }
+
+ btd_register_adapter_driver(&sap_server_driver);
+
+ return 0;
+}
+
+void sap_manager_exit(void)
+{
+ btd_unregister_adapter_driver(&sap_server_driver);
+
+ dbus_connection_unref(connection);
+ connection = NULL;
+
+ sap_server_exit();
+}
diff --git a/sap/manager.h b/sap/manager.h
new file mode 100644
index 0000000..e08c882
--- /dev/null
+++ b/sap/manager.h
@@ -0,0 +1,22 @@
+/*
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 Instituto Nokia de Tecnologia - INdT
+ *
+ * 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
+ */
+
+int sap_manager_init(DBusConnection *conn);
+void sap_manager_exit(void);
diff --git a/sap/sap-dummy.c b/sap/sap-dummy.c
new file mode 100644
index 0000000..39e1fc9
--- /dev/null
+++ b/sap/sap-dummy.c
@@ -0,0 +1,330 @@
+/*
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 ST-Ericsson SA
+ * Copyright (C) 2011 Tieto Poland
+ *
+ * Author: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
+ * for ST-Ericsson
+ *
+ * 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 <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <glib.h>
+#include <gdbus.h>
+
+#include "log.h"
+#include "sap.h"
+
+#define SAP_DUMMY_IFACE "org.bluez.SimAccessTest"
+#define SAP_DUMMY_PATH "/org/bluez/test"
+
+enum {
+ SIM_DISCONNECTED= 0x00,
+ SIM_CONNECTED = 0x01,
+ SIM_POWERED_OFF = 0x02,
+ SIM_MISSING = 0x03
+};
+
+static DBusConnection *connection = NULL;
+
+static int sim_card_conn_status = SIM_DISCONNECTED;
+static void *sap_data = NULL; /* SAP server private data.*/
+static gboolean ongoing_call_status = FALSE;
+static int max_msg_size_supported = 512;
+
+void sap_connect_req(void *sap_device, uint16_t maxmsgsize)
+{
+ DBG("status: %d", sim_card_conn_status);
+
+ if (sim_card_conn_status != SIM_DISCONNECTED) {
+ sap_connect_rsp(sap_device, SAP_STATUS_CONNECTION_FAILED,
+ maxmsgsize);
+ return;
+ } else if (max_msg_size_supported > maxmsgsize) {
+ sap_connect_rsp(sap_device, SAP_STATUS_MAX_MSG_SIZE_TOO_SMALL,
+ max_msg_size_supported);
+ return;
+ } else if (max_msg_size_supported < maxmsgsize) {
+ sap_connect_rsp(sap_device,
+ SAP_STATUS_MAX_MSG_SIZE_NOT_SUPPORTED,
+ max_msg_size_supported);
+ return;
+ } else if (ongoing_call_status) {
+ sap_connect_rsp(sap_device, SAP_STATUS_OK_ONGOING_CALL,
+ maxmsgsize);
+ return;
+ } else {
+ sim_card_conn_status = SIM_CONNECTED;
+ sap_data = sap_device;
+
+ sap_connect_rsp(sap_device, SAP_STATUS_OK, maxmsgsize);
+ sap_status_ind(sap_device, SAP_STATUS_CHANGE_CARD_RESET);
+ }
+}
+
+void sap_disconnect_req(void *sap_device, uint8_t linkloss)
+{
+ sim_card_conn_status = SIM_DISCONNECTED;
+ sap_data = NULL;
+ ongoing_call_status = FALSE;
+
+ DBG("status: %d", sim_card_conn_status);
+
+ if (linkloss)
+ return;
+
+ sap_disconnect_rsp(sap_device);
+}
+
+void sap_transfer_apdu_req(void *sap_device, struct sap_parameter *param)
+{
+ char apdu[] = "APDU response!";
+
+ DBG("status: %d", sim_card_conn_status);
+
+ if (sim_card_conn_status == SIM_MISSING)
+ sap_transfer_apdu_rsp(sap_device,
+ SAP_RESULT_ERROR_CARD_REMOVED, NULL, 0);
+ else if (sim_card_conn_status == SIM_POWERED_OFF)
+ sap_transfer_apdu_rsp(sap_device, SAP_RESULT_ERROR_POWERED_OFF,
+ NULL, 0);
+ else if (sim_card_conn_status != SIM_CONNECTED)
+ sap_transfer_apdu_rsp(sap_device,
+ SAP_RESULT_ERROR_NOT_ACCESSIBLE, NULL, 0);
+ else
+ sap_transfer_apdu_rsp(sap_device, SAP_RESULT_OK,
+ (uint8_t*)&apdu, sizeof(apdu));
+}
+
+void sap_transfer_atr_req(void *sap_device)
+{
+ char atr[] = "ATR response!";
+
+ DBG("status: %d", sim_card_conn_status);
+
+ if (sim_card_conn_status == SIM_MISSING)
+ sap_transfer_atr_rsp(sap_device, SAP_RESULT_ERROR_CARD_REMOVED,
+ NULL, 0);
+ else if (sim_card_conn_status == SIM_POWERED_OFF)
+ sap_transfer_atr_rsp(sap_device, SAP_RESULT_ERROR_POWERED_OFF,
+ NULL, 0);
+ else if (sim_card_conn_status != SIM_CONNECTED)
+ sap_transfer_atr_rsp(sap_device, SAP_RESULT_ERROR_NO_REASON,
+ NULL, 0);
+ else
+ sap_transfer_atr_rsp(sap_device, SAP_RESULT_OK,
+ (uint8_t*)&atr, sizeof(atr));
+}
+
+void sap_power_sim_off_req(void *sap_device)
+{
+ DBG("status: %d", sim_card_conn_status);
+
+ if (sim_card_conn_status == SIM_MISSING) {
+ sap_power_sim_off_rsp(sap_device,
+ SAP_RESULT_ERROR_CARD_REMOVED);
+ } else if (sim_card_conn_status == SIM_POWERED_OFF) {
+ sap_power_sim_off_rsp(sap_device,
+ SAP_RESULT_ERROR_POWERED_OFF);
+ } else if (sim_card_conn_status != SIM_CONNECTED) {
+ sap_power_sim_off_rsp(sap_device,
+ SAP_RESULT_ERROR_NO_REASON);
+ } else {
+ sap_power_sim_off_rsp(sap_device, SAP_RESULT_OK);
+ sim_card_conn_status = SIM_POWERED_OFF;
+ }
+}
+
+void sap_power_sim_on_req(void *sap_device)
+{
+ DBG("status: %d", sim_card_conn_status);
+
+ if (sim_card_conn_status == SIM_MISSING) {
+ sap_power_sim_on_rsp(sap_device,
+ SAP_RESULT_ERROR_CARD_REMOVED);
+ } else if (sim_card_conn_status == SIM_POWERED_OFF) {
+ sap_power_sim_on_rsp(sap_device, SAP_RESULT_OK);
+ sim_card_conn_status = SIM_CONNECTED;
+ return;
+ } else if (sim_card_conn_status != SIM_CONNECTED) {
+ sap_power_sim_on_rsp(sap_device,
+ SAP_RESULT_ERROR_NOT_ACCESSIBLE);
+ } else {
+ sap_power_sim_on_rsp(sap_device,
+ SAP_RESULT_ERROR_NO_REASON);
+ }
+}
+
+void sap_reset_sim_req(void *sap_device)
+{
+ DBG("status: %d", sim_card_conn_status);
+
+ if (sim_card_conn_status == SIM_MISSING) {
+ sap_reset_sim_rsp(sap_device, SAP_RESULT_ERROR_CARD_REMOVED);
+ } else if (sim_card_conn_status == SIM_POWERED_OFF) {
+ sap_reset_sim_rsp(sap_device, SAP_RESULT_ERROR_POWERED_OFF);
+ } else if (sim_card_conn_status != SIM_CONNECTED) {
+ sap_reset_sim_rsp(sap_device, SAP_RESULT_ERROR_NO_REASON);
+ } else {
+ sap_reset_sim_rsp(sap_device, SAP_RESULT_OK);
+ }
+}
+
+void sap_transfer_card_reader_status_req(void *sap_device)
+{
+ DBG("status: %d", sim_card_conn_status);
+
+ if (sim_card_conn_status != SIM_CONNECTED) {
+ sap_transfer_card_reader_status_rsp(sap_device,
+ SAP_RESULT_ERROR_NO_REASON, 0xF1);
+ return;
+ }
+
+ sap_transfer_card_reader_status_rsp(sap_device, SAP_RESULT_OK, 0xF1);
+}
+
+void sap_set_transport_protocol_req(void *sap_device,
+ struct sap_parameter *param)
+{
+ sap_transport_protocol_rsp(sap_device, SAP_RESULT_NOT_SUPPORTED);
+}
+
+static inline DBusMessage *invalid_args(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, "org.bluez.Error.InvalidArguments",
+ "Invalid arguments in method call");
+}
+
+static DBusMessage *ongoing_call(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ dbus_bool_t ongoing;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_BOOLEAN, &ongoing,
+ DBUS_TYPE_INVALID))
+ return invalid_args(msg);
+
+ if (ongoing_call_status && !ongoing) {
+ /* An ongoing call has finished. Continue connection.*/
+ sap_connect_rsp(sap_data, SAP_STATUS_OK,
+ max_msg_size_supported);
+ sap_status_ind(sap_data, SAP_STATUS_CHANGE_CARD_RESET);
+ ongoing_call_status = ongoing;
+ } else if (!ongoing_call_status && ongoing) {
+ /* An ongoing call has started.*/
+ ongoing_call_status = ongoing;
+ }
+
+ DBG("OngoingCall status set to %d", ongoing_call_status);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *max_msg_size(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ dbus_uint32_t size;
+
+ if (sim_card_conn_status == SIM_CONNECTED)
+ return g_dbus_create_error(msg, "org.bluez.Error.Failed",
+ "Can't change msg size when connected.");
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &size,
+ DBUS_TYPE_INVALID))
+ return invalid_args(msg);
+
+ max_msg_size_supported = size;
+
+ DBG("MaxMessageSize set to %d", max_msg_size_supported);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *disconnect(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ sim_card_conn_status = SIM_DISCONNECTED;
+ sap_status_ind(sap_data, SAP_STATUS_CHANGE_CARD_NOT_ACCESSIBLE);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *card_status(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ dbus_uint32_t status;
+
+ DBG("status %d", sim_card_conn_status);
+
+ if (sim_card_conn_status != SIM_CONNECTED)
+ return g_dbus_create_error(msg, "org.bluez.Error.Failed",
+ "Can't change msg size when not connected.");
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &status,
+ DBUS_TYPE_INVALID))
+ return invalid_args(msg);
+
+ if (status) {
+ if (sim_card_conn_status == SIM_MISSING) {
+ sim_card_conn_status = SIM_CONNECTED;
+ sap_status_ind(sap_data,
+ SAP_STATUS_CHANGE_CARD_INSERTED);
+ }
+ } else {
+ sim_card_conn_status = SIM_MISSING;
+ sap_status_ind(sap_data, SAP_STATUS_CHANGE_CARD_REMOVED);
+ }
+
+ DBG("Card status changed to %d", status);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static GDBusMethodTable dummy_methods[] = {
+ { "OngoingCall", "b", "", ongoing_call},
+ { "MaxMessageSize", "u", "", max_msg_size},
+ { "Disconnect", "", "", disconnect},
+ { "CardStatus", "u", "", card_status},
+ { }
+};
+
+int sap_init(void)
+{
+ connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+
+ if (g_dbus_register_interface(connection, SAP_DUMMY_PATH,
+ SAP_DUMMY_IFACE, dummy_methods, NULL, NULL,
+ NULL, NULL) == FALSE) {
+ error("sap-dummy interface %s init failed on path %s",
+ SAP_DUMMY_IFACE, SAP_DUMMY_PATH);
+ return -1;
+ }
+
+ return 0;
+}
+
+void sap_exit(void)
+{
+ dbus_connection_unref(connection);
+ connection = NULL;
+}
diff --git a/sap/sap.h b/sap/sap.h
new file mode 100644
index 0000000..24240ca
--- /dev/null
+++ b/sap/sap.h
@@ -0,0 +1,186 @@
+/*
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 Instituto Nokia de Tecnologia - INdT
+ * Copyright (C) 2010 ST-Ericsson SA
+ *
+ * Author: Marek Skowron <marek.skowron@tieto.com> for ST-Ericsson.
+ * Author: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
+ * for ST-Ericsson.
+ *
+ * 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
+ */
+
+#include <stdint.h>
+#include <glib.h>
+
+#define SAP_VERSION 0x0101
+
+/* Connection Status - SAP v1.1 section 5.2.2 */
+enum sap_status {
+ SAP_STATUS_OK = 0x00,
+ SAP_STATUS_CONNECTION_FAILED = 0x01,
+ SAP_STATUS_MAX_MSG_SIZE_NOT_SUPPORTED = 0x02,
+ SAP_STATUS_MAX_MSG_SIZE_TOO_SMALL = 0x03,
+ SAP_STATUS_OK_ONGOING_CALL = 0x04
+};
+
+/* Disconnection Type - SAP v1.1 section 5.2.3 */
+enum sap_disconnection_type {
+ SAP_DISCONNECTION_TYPE_GRACEFUL = 0x00,
+ SAP_DISCONNECTION_TYPE_IMMEDIATE = 0x01,
+ SAP_DISCONNECTION_TYPE_CLIENT = 0xFF
+};
+
+/* Result codes - SAP v1.1 section 5.2.4 */
+enum sap_result {
+ SAP_RESULT_OK = 0x00,
+ SAP_RESULT_ERROR_NO_REASON = 0x01,
+ SAP_RESULT_ERROR_NOT_ACCESSIBLE = 0x02,
+ SAP_RESULT_ERROR_POWERED_OFF = 0x03,
+ SAP_RESULT_ERROR_CARD_REMOVED = 0x04,
+ SAP_RESULT_ERROR_POWERED_ON = 0x05,
+ SAP_RESULT_ERROR_NO_DATA = 0x06,
+ SAP_RESULT_NOT_SUPPORTED = 0x07
+};
+
+/* Status Change - SAP v1.1 section 5.2.8 */
+enum sap_status_change {
+ SAP_STATUS_CHANGE_UNKNOWN_ERROR = 0x00,
+ SAP_STATUS_CHANGE_CARD_RESET = 0x01,
+ SAP_STATUS_CHANGE_CARD_NOT_ACCESSIBLE = 0x02,
+ SAP_STATUS_CHANGE_CARD_REMOVED = 0x03,
+ SAP_STATUS_CHANGE_CARD_INSERTED = 0x04,
+ SAP_STATUS_CHANGE_CARD_RECOVERED = 0x05
+};
+
+/* Message format - SAP v1.1 section 5.1 */
+struct sap_parameter {
+ uint8_t id;
+ uint8_t reserved;
+ uint16_t len;
+ uint8_t val[0];
+ /*
+ * Padding bytes 0-3 bytes
+ */
+} __attribute__((packed));
+
+struct sap_message {
+ uint8_t id;
+ uint8_t nparam;
+ uint16_t reserved;
+ struct sap_parameter param[0];
+} __attribute__((packed));
+
+enum {
+ ICC_READER_UNSPECIFIED_ERROR, /* No further information available */
+ ICC_READER_NOT_PRESENT, /* Card Reader removed or not present */
+ ICC_READER_BUSY, /* Card Reader in use */
+ ICC_READER_CARD_POWERED_ON, /* Card in reader and is powered on */
+ ICC_READER_DEACTIVATED, /* Card Reader deactivated */
+ ICC_READER_CARD_POWERED_OFF, /* Card in reader, but powered off */
+ ICC_READER_NO_CARD, /* No card in reader */
+ ICC_READER_LAST
+};
+
+#define SAP_BUF_SIZE 512
+#define SAP_MSG_HEADER_SIZE 4
+
+enum sap_protocol {
+ SAP_CONNECT_REQ = 0x00,
+ SAP_CONNECT_RESP = 0x01,
+ SAP_DISCONNECT_REQ = 0x02,
+ SAP_DISCONNECT_RESP = 0x03,
+ SAP_DISCONNECT_IND = 0x04,
+ SAP_TRANSFER_APDU_REQ = 0x05,
+ SAP_TRANSFER_APDU_RESP = 0x06,
+ SAP_TRANSFER_ATR_REQ = 0x07,
+ SAP_TRANSFER_ATR_RESP = 0x08,
+ SAP_POWER_SIM_OFF_REQ = 0x09,
+ SAP_POWER_SIM_OFF_RESP = 0x0A,
+ SAP_POWER_SIM_ON_REQ = 0x0B,
+ SAP_POWER_SIM_ON_RESP = 0x0C,
+ SAP_RESET_SIM_REQ = 0x0D,
+ SAP_RESET_SIM_RESP = 0x0E,
+ SAP_TRANSFER_CARD_READER_STATUS_REQ = 0x0F,
+ SAP_TRANSFER_CARD_READER_STATUS_RESP = 0x10,
+ SAP_STATUS_IND = 0x11,
+ SAP_ERROR_RESP = 0x12,
+ SAP_SET_TRANSPORT_PROTOCOL_REQ = 0x13,
+ SAP_SET_TRANSPORT_PROTOCOL_RESP = 0x14
+};
+
+/* Parameters Ids - SAP 1.1 section 5.2 */
+enum sap_param_id {
+ SAP_PARAM_ID_MAX_MSG_SIZE = 0x00,
+ SAP_PARAM_ID_CONN_STATUS = 0x01,
+ SAP_PARAM_ID_RESULT_CODE = 0x02,
+ SAP_PARAM_ID_DISCONNECT_IND = 0x03,
+ SAP_PARAM_ID_COMMAND_APDU = 0x04,
+ SAP_PARAM_ID_COMMAND_APDU7816 = 0x10,
+ SAP_PARAM_ID_RESPONSE_APDU = 0x05,
+ SAP_PARAM_ID_ATR = 0x06,
+ SAP_PARAM_ID_CARD_READER_STATUS = 0x07,
+ SAP_PARAM_ID_STATUS_CHANGE = 0x08,
+ SAP_PARAM_ID_TRANSPORT_PROTOCOL = 0x09
+};
+
+#define SAP_PARAM_ID_MAX_MSG_SIZE_LEN 0x02
+#define SAP_PARAM_ID_CONN_STATUS_LEN 0x01
+#define SAP_PARAM_ID_RESULT_CODE_LEN 0x01
+#define SAP_PARAM_ID_DISCONNECT_IND_LEN 0x01
+#define SAP_PARAM_ID_CARD_READER_STATUS_LEN 0x01
+#define SAP_PARAM_ID_STATUS_CHANGE_LEN 0x01
+#define SAP_PARAM_ID_TRANSPORT_PROTO_LEN 0x01
+
+/* Transport Protocol - SAP v1.1 section 5.2.9 */
+enum sap_transport_protocol {
+ SAP_TRANSPORT_PROTOCOL_T0 = 0x00,
+ SAP_TRANSPORT_PROTOCOL_T1 = 0x01
+};
+
+/*SAP driver init and exit routines. Implemented by sap-*.c */
+int sap_init(void);
+void sap_exit(void);
+
+/* SAP requests implemented by sap-*.c */
+void sap_connect_req(void *sap_device, uint16_t maxmsgsize);
+void sap_disconnect_req(void *sap_device, uint8_t linkloss);
+void sap_transfer_apdu_req(void *sap_device, struct sap_parameter *param);
+void sap_transfer_atr_req(void *sap_device);
+void sap_power_sim_off_req(void *sap_device);
+void sap_power_sim_on_req(void *sap_device);
+void sap_reset_sim_req(void *sap_device);
+void sap_transfer_card_reader_status_req(void *sap_device);
+void sap_set_transport_protocol_req(void *sap_device,
+ struct sap_parameter *param);
+
+/*SAP responses to SAP requests. Implemented by server.c */
+int sap_connect_rsp(void *sap_device, uint8_t status, uint16_t maxmsgsize);
+int sap_disconnect_rsp(void *sap_device);
+int sap_transfer_apdu_rsp(void *sap_device, uint8_t result,
+ uint8_t *sap_apdu_resp, uint16_t length);
+int sap_transfer_atr_rsp(void *sap_device, uint8_t result,
+ uint8_t *sap_atr, uint16_t length);
+int sap_power_sim_off_rsp(void *sap_device, uint8_t result);
+int sap_power_sim_on_rsp(void *sap_device, uint8_t result);
+int sap_reset_sim_rsp(void *sap_device, uint8_t result);
+int sap_transfer_card_reader_status_rsp(void *sap_device, uint8_t result,
+ uint8_t status);
+int sap_error_rsp(void *sap_device);
+int sap_transport_protocol_rsp(void *sap_device, uint8_t result);
+
+/* Event indication. Implemented by server.c*/
+int sap_status_ind(void *sap_device, uint8_t status_change);
diff --git a/sap/server.c b/sap/server.c
new file mode 100644
index 0000000..c9c9a12
--- /dev/null
+++ b/sap/server.c
@@ -0,0 +1,1438 @@
+/*
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 Instituto Nokia de Tecnologia - INdT
+ * Copyright (C) 2010 ST-Ericsson SA
+ * Copyright (C) 2011 Tieto Poland
+ *
+ * Author: Marek Skowron <marek.skowron@tieto.com> for ST-Ericsson.
+ * Author: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
+ * for ST-Ericsson.
+ *
+ * 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 <glib.h>
+#include <netinet/in.h>
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include "adapter.h"
+#include "btio.h"
+#include "sdpd.h"
+#include "log.h"
+#include "error.h"
+#include "dbus-common.h"
+#include "sap.h"
+#include "server.h"
+
+#define SAP_SERVER_INTERFACE "org.bluez.SimAccess"
+#define SAP_UUID "0000112D-0000-1000-8000-00805F9B34FB"
+#define SAP_SERVER_CHANNEL 8
+#define SAP_BUF_SIZE 512
+
+#define PADDING4(x) (4 - (x & 0x03))
+#define PARAMETER_SIZE(x) (sizeof(struct sap_parameter) + x + PADDING4(x))
+
+#define SAP_NO_REQ 0xFF
+
+#define SAP_TIMER_GRACEFUL_DISCONNECT 30
+#define SAP_TIMER_NO_ACTIVITY 30
+
+enum {
+ SAP_STATE_DISCONNECTED,
+ SAP_STATE_CONNECT_IN_PROGRESS,
+ SAP_STATE_CONNECTED,
+ SAP_STATE_GRACEFUL_DISCONNECT,
+ SAP_STATE_IMMEDIATE_DISCONNECT,
+ SAP_STATE_CLIENT_DISCONNECT
+};
+
+struct sap_connection {
+ GIOChannel *io;
+ uint32_t state;
+ uint8_t processing_req;
+ guint timer_id;
+};
+
+struct sap_server {
+ bdaddr_t src;
+ char *path;
+ uint32_t record_id;
+ GIOChannel *listen_io;
+ struct sap_connection *conn;
+};
+
+static DBusConnection *connection;
+static struct sap_server *server;
+
+static void start_guard_timer(struct sap_connection *conn, guint interval);
+static void stop_guard_timer(struct sap_connection *conn);
+static gboolean guard_timeout(gpointer data);
+
+static size_t add_result_parameter(uint8_t result,
+ struct sap_parameter *param)
+{
+ param->id = SAP_PARAM_ID_RESULT_CODE;
+ param->len = htons(SAP_PARAM_ID_RESULT_CODE_LEN);
+ *param->val = result;
+
+ return PARAMETER_SIZE(SAP_PARAM_ID_RESULT_CODE_LEN);
+}
+
+static int is_power_sim_off_req_allowed(uint8_t processing_req)
+{
+ switch (processing_req) {
+ case SAP_NO_REQ:
+ case SAP_TRANSFER_APDU_REQ:
+ case SAP_TRANSFER_ATR_REQ:
+ case SAP_POWER_SIM_ON_REQ:
+ case SAP_RESET_SIM_REQ:
+ case SAP_TRANSFER_CARD_READER_STATUS_REQ:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int is_reset_sim_req_allowed(uint8_t processing_req)
+{
+ switch (processing_req) {
+ case SAP_NO_REQ:
+ case SAP_TRANSFER_APDU_REQ:
+ case SAP_TRANSFER_ATR_REQ:
+ case SAP_TRANSFER_CARD_READER_STATUS_REQ:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int check_msg(struct sap_message *msg)
+{
+ if (!msg)
+ return -EINVAL;
+
+ switch (msg->id) {
+ case SAP_CONNECT_REQ:
+ if (msg->nparam != 0x01)
+ return -EBADMSG;
+
+ if (msg->param->id != SAP_PARAM_ID_MAX_MSG_SIZE)
+ return -EBADMSG;
+
+ if (ntohs(msg->param->len) != SAP_PARAM_ID_MAX_MSG_SIZE_LEN)
+ return -EBADMSG;
+
+ break;
+
+ case SAP_TRANSFER_APDU_REQ:
+ if (msg->nparam != 0x01)
+ return -EBADMSG;
+
+ if (msg->param->id != SAP_PARAM_ID_COMMAND_APDU)
+ if ( msg->param->id != SAP_PARAM_ID_COMMAND_APDU7816)
+ return -EBADMSG;
+
+ if (msg->param->len == 0x00)
+ return -EBADMSG;
+
+ break;
+
+ case SAP_SET_TRANSPORT_PROTOCOL_REQ:
+ if (msg->nparam != 0x01)
+ return -EBADMSG;
+
+ if (msg->param->id != SAP_PARAM_ID_TRANSPORT_PROTOCOL)
+ return -EBADMSG;
+
+ if (ntohs(msg->param->len) != SAP_PARAM_ID_TRANSPORT_PROTO_LEN)
+ return -EBADMSG;
+
+ if (*msg->param->val != SAP_TRANSPORT_PROTOCOL_T0)
+ if (*msg->param->val != SAP_TRANSPORT_PROTOCOL_T1)
+ return -EBADMSG;
+
+ break;
+
+ case SAP_DISCONNECT_REQ:
+ case SAP_TRANSFER_ATR_REQ:
+ case SAP_POWER_SIM_OFF_REQ:
+ case SAP_POWER_SIM_ON_REQ:
+ case SAP_RESET_SIM_REQ:
+ case SAP_TRANSFER_CARD_READER_STATUS_REQ:
+ if (msg->nparam != 0x00)
+ return -EBADMSG;
+
+ break;
+ }
+
+ return 0;
+}
+
+static sdp_record_t *create_sap_record(uint8_t channel)
+{
+ sdp_list_t *apseq, *aproto, *profiles, *proto[2], *root, *svclass_id;
+ uuid_t sap_uuid, gt_uuid, root_uuid, l2cap, rfcomm;
+ sdp_profile_desc_t profile;
+ sdp_record_t *record;
+ sdp_data_t *ch;
+
+ record = sdp_record_alloc();
+ if (!record)
+ return NULL;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(record, root);
+ sdp_list_free(root, NULL);
+
+ sdp_uuid16_create(&sap_uuid, SAP_SVCLASS_ID);
+ svclass_id = sdp_list_append(NULL, &sap_uuid);
+ sdp_uuid16_create(&gt_uuid, GENERIC_TELEPHONY_SVCLASS_ID);
+ svclass_id = sdp_list_append(svclass_id, &gt_uuid);
+
+ sdp_set_service_classes(record, svclass_id);
+ sdp_list_free(svclass_id, NULL);
+
+ sdp_uuid16_create(&profile.uuid, SAP_PROFILE_ID);
+ profile.version = SAP_VERSION;
+ profiles = sdp_list_append(NULL, &profile);
+ sdp_set_profile_descs(record, profiles);
+ sdp_list_free(profiles, NULL);
+
+ sdp_uuid16_create(&l2cap, L2CAP_UUID);
+ proto[0] = sdp_list_append(NULL, &l2cap);
+ apseq = sdp_list_append(NULL, proto[0]);
+
+ sdp_uuid16_create(&rfcomm, RFCOMM_UUID);
+ proto[1] = sdp_list_append(NULL, &rfcomm);
+ ch = sdp_data_alloc(SDP_UINT8, &channel);
+ proto[1] = sdp_list_append(proto[1], ch);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(NULL, apseq);
+ sdp_set_access_protos(record, aproto);
+
+ sdp_set_info_attr(record, "SIM Access Server",
+ NULL, NULL);
+
+ sdp_data_free(ch);
+ sdp_list_free(proto[0], NULL);
+ sdp_list_free(proto[1], NULL);
+ sdp_list_free(apseq, NULL);
+ sdp_list_free(aproto, NULL);
+
+ return record;
+}
+
+static int send_message(struct sap_connection *conn, void *buf, size_t size)
+{
+ size_t written = 0;
+ GError *gerr = NULL;
+ GIOStatus gstatus;
+
+ if (!conn || !buf)
+ return -EINVAL;
+
+ DBG("size %zu", size);
+
+ gstatus = g_io_channel_write_chars(conn->io, buf, size, &written,
+ &gerr);
+ if (gstatus != G_IO_STATUS_NORMAL) {
+ if (gerr)
+ g_error_free(gerr);
+
+ error("write error (0x%02x).", gstatus);
+ return -EINVAL;
+ }
+
+ if (written != size)
+ error("write error.(written %zu size %zu)", written, size);
+
+ return 0;
+}
+
+static int disconnect_ind(void *sap_device, uint8_t disc_type)
+{
+ struct sap_connection *conn = sap_device;
+ char buf[SAP_BUF_SIZE];
+ struct sap_message *msg = (struct sap_message *) buf;
+ struct sap_parameter *param = (struct sap_parameter *) msg->param;
+ size_t size = sizeof(struct sap_message);
+
+ if (!conn)
+ return -EINVAL;
+
+ DBG("data %p state %d disc_type 0x%02x", conn, conn->state, disc_type);
+
+ if (conn->state != SAP_STATE_GRACEFUL_DISCONNECT &&
+ conn->state != SAP_STATE_IMMEDIATE_DISCONNECT) {
+ error("Processing error (state %d pr 0x%02x)", conn->state,
+ conn->processing_req);
+ return -EPERM;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ msg->id = SAP_DISCONNECT_IND;
+ msg->nparam = 0x01;
+
+ /* Add disconnection type param. */
+ param->id = SAP_PARAM_ID_DISCONNECT_IND;
+ param->len = htons(SAP_PARAM_ID_DISCONNECT_IND_LEN);
+ *param->val = disc_type;
+ size += PARAMETER_SIZE(SAP_PARAM_ID_DISCONNECT_IND_LEN);
+
+ return send_message(sap_device, buf, size);
+}
+
+static void connect_req(struct sap_connection *conn,
+ struct sap_parameter *param)
+{
+ uint16_t maxmsgsize, *val;
+
+ DBG("conn %p state %d", conn, conn->state);
+
+ if (!param)
+ goto error_rsp;
+
+ if (conn->state != SAP_STATE_DISCONNECTED)
+ goto error_rsp;
+
+ stop_guard_timer(conn);
+
+ val = (uint16_t *) &param->val;
+ maxmsgsize = ntohs(*val);
+
+ DBG("Connect MaxMsgSize: 0x%04x", maxmsgsize);
+
+ conn->state = SAP_STATE_CONNECT_IN_PROGRESS;
+
+ if (maxmsgsize <= SAP_BUF_SIZE) {
+ conn->processing_req = SAP_CONNECT_REQ;
+ sap_connect_req(conn, maxmsgsize);
+ } else {
+ sap_connect_rsp(conn, SAP_STATUS_MAX_MSG_SIZE_NOT_SUPPORTED,
+ SAP_BUF_SIZE);
+ }
+
+ return;
+
+error_rsp:
+ error("Processing error (param %p state %d pr 0x%02x)", param,
+ conn->state, conn->processing_req);
+ sap_error_rsp(conn);
+}
+
+static int disconnect_req(struct sap_connection *conn, uint8_t disc_type)
+{
+ DBG("conn %p state %d disc_type 0x%02x", conn, conn->state, disc_type);
+
+ switch (disc_type) {
+ case SAP_DISCONNECTION_TYPE_GRACEFUL:
+ if (conn->state == SAP_STATE_DISCONNECTED)
+ goto error_req;
+
+ if (conn->state == SAP_STATE_CONNECT_IN_PROGRESS)
+ goto error_req;
+
+ if (conn->state == SAP_STATE_CONNECTED) {
+ conn->state = SAP_STATE_GRACEFUL_DISCONNECT;
+ conn->processing_req = SAP_NO_REQ;
+
+ disconnect_ind(conn, disc_type);
+ /* Timer will disconnect if client won't do.*/
+ start_guard_timer(conn, SAP_TIMER_GRACEFUL_DISCONNECT);
+ }
+
+ return 0;
+
+ case SAP_DISCONNECTION_TYPE_IMMEDIATE:
+ if (conn->state == SAP_STATE_DISCONNECTED)
+ goto error_req;
+
+ if (conn->state == SAP_STATE_CONNECT_IN_PROGRESS)
+ goto error_req;
+
+ if (conn->state == SAP_STATE_CONNECTED ||
+ conn->state == SAP_STATE_GRACEFUL_DISCONNECT) {
+ conn->state = SAP_STATE_IMMEDIATE_DISCONNECT;
+ conn->processing_req = SAP_NO_REQ;
+
+ stop_guard_timer(conn);
+ disconnect_ind(conn, disc_type);
+ sap_disconnect_req(conn, 0);
+ }
+
+ return 0;
+
+ case SAP_DISCONNECTION_TYPE_CLIENT:
+ if (conn->state != SAP_STATE_CONNECTED &&
+ conn->state != SAP_STATE_GRACEFUL_DISCONNECT)
+ goto error_rsp;
+
+ conn->state = SAP_STATE_CLIENT_DISCONNECT;
+ conn->processing_req = SAP_NO_REQ;
+
+ stop_guard_timer(conn);
+ sap_disconnect_req(conn, 0);
+
+ return 0;
+
+ default:
+ error("Unknown disconnection type (0x%02x).", disc_type);
+ return -EINVAL;
+ }
+
+error_rsp:
+ sap_error_rsp(conn);
+error_req:
+ error("Processing error (state %d pr 0x%02x)", conn->state,
+ conn->processing_req);
+ return -EPERM;
+}
+
+static void transfer_apdu_req(struct sap_connection *conn,
+ struct sap_parameter *param)
+{
+ DBG("conn %p state %d", conn, conn->state);
+
+ if (!param)
+ goto error_rsp;
+
+ param->len = ntohs(param->len);
+
+ if (conn->state != SAP_STATE_CONNECTED &&
+ conn->state != SAP_STATE_GRACEFUL_DISCONNECT)
+ goto error_rsp;
+
+ if (conn->processing_req != SAP_NO_REQ)
+ goto error_rsp;
+
+ conn->processing_req = SAP_TRANSFER_APDU_REQ;
+ sap_transfer_apdu_req(conn, param);
+
+ return;
+
+error_rsp:
+ error("Processing error (param %p state %d pr 0x%02x)", param,
+ conn->state, conn->processing_req);
+ sap_error_rsp(conn);
+}
+
+static void transfer_atr_req(struct sap_connection *conn)
+{
+ DBG("conn %p state %d", conn, conn->state);
+
+ if (conn->state != SAP_STATE_CONNECTED)
+ goto error_rsp;
+
+ if (conn->processing_req != SAP_NO_REQ)
+ goto error_rsp;
+
+ conn->processing_req = SAP_TRANSFER_ATR_REQ;
+ sap_transfer_atr_req(conn);
+
+ return;
+
+error_rsp:
+ error("Processing error (state %d pr 0x%02x)", conn->state,
+ conn->processing_req);
+ sap_error_rsp(conn);
+}
+
+static void power_sim_off_req(struct sap_connection *conn)
+{
+ DBG("conn %p state %d", conn, conn->state);
+
+ if (conn->state != SAP_STATE_CONNECTED)
+ goto error_rsp;
+
+ if (!is_power_sim_off_req_allowed(conn->processing_req))
+ goto error_rsp;
+
+ conn->processing_req = SAP_POWER_SIM_OFF_REQ;
+ sap_power_sim_off_req(conn);
+
+ return;
+
+error_rsp:
+ error("Processing error (state %d pr 0x%02x)", conn->state,
+ conn->processing_req);
+ sap_error_rsp(conn);
+}
+
+static void power_sim_on_req(struct sap_connection *conn)
+{
+ DBG("conn %p state %d", conn, conn->state);
+
+ if (conn->state != SAP_STATE_CONNECTED)
+ goto error_rsp;
+
+ if (conn->processing_req != SAP_NO_REQ)
+ goto error_rsp;
+
+ conn->processing_req = SAP_POWER_SIM_ON_REQ;
+ sap_power_sim_on_req(conn);
+
+ return;
+
+error_rsp:
+ error("Processing error (state %d pr 0x%02x)", conn->state,
+ conn->processing_req);
+ sap_error_rsp(conn);
+}
+
+static void reset_sim_req(struct sap_connection *conn)
+{
+ DBG("conn %p state %d", conn, conn->state);
+
+ if (conn->state != SAP_STATE_CONNECTED)
+ goto error_rsp;
+
+ if (!is_reset_sim_req_allowed(conn->processing_req))
+ goto error_rsp;
+
+ conn->processing_req = SAP_RESET_SIM_REQ;
+ sap_reset_sim_req(conn);
+
+ return;
+
+error_rsp:
+ error("Processing error (state %d pr 0x%02x param)", conn->state,
+ conn->processing_req);
+ sap_error_rsp(conn);
+}
+
+static void transfer_card_reader_status_req(struct sap_connection *conn)
+{
+ DBG("conn %p state %d", conn, conn->state);
+
+ if (conn->state != SAP_STATE_CONNECTED)
+ goto error_rsp;
+
+ if (conn->processing_req != SAP_NO_REQ)
+ goto error_rsp;
+
+ conn->processing_req = SAP_TRANSFER_CARD_READER_STATUS_REQ;
+ sap_transfer_card_reader_status_req(conn);
+
+ return;
+
+error_rsp:
+ error("Processing error (state %d pr 0x%02x)", conn->state,
+ conn->processing_req);
+ sap_error_rsp(conn);
+}
+
+static void set_transport_protocol_req(struct sap_connection *conn,
+ struct sap_parameter *param)
+{
+ if (!param)
+ goto error_rsp;
+
+ DBG("conn %p state %d param %p", conn, conn->state, param);
+
+ if (conn->state != SAP_STATE_CONNECTED)
+ goto error_rsp;
+
+ if (conn->processing_req != SAP_NO_REQ)
+ goto error_rsp;
+
+ conn->processing_req = SAP_SET_TRANSPORT_PROTOCOL_REQ;
+ sap_set_transport_protocol_req(conn, param);
+
+ return;
+
+error_rsp:
+ error("Processing error (param %p state %d pr 0x%02x)", param,
+ conn->state, conn->processing_req);
+ sap_error_rsp(conn);
+}
+
+static void start_guard_timer(struct sap_connection *conn, guint interval)
+{
+ if (!conn)
+ return;
+
+ if (!conn->timer_id)
+ conn->timer_id = g_timeout_add_seconds(interval, guard_timeout,
+ conn);
+ else
+ error("Timer is already active.");
+}
+
+static void stop_guard_timer(struct sap_connection *conn)
+{
+ if (conn && conn->timer_id) {
+ g_source_remove(conn->timer_id);
+ conn->timer_id = 0;
+ }
+}
+
+static gboolean guard_timeout(gpointer data)
+{
+ struct sap_connection *conn = data;
+
+ if (!conn)
+ return FALSE;
+
+ DBG("conn %p state %d pr 0x%02x", conn, conn->state,
+ conn->processing_req);
+
+ conn->timer_id = 0;
+
+ switch (conn->state) {
+ case SAP_STATE_DISCONNECTED:
+ /* Client opened RFCOMM channel but didn't send CONNECT_REQ,
+ * in fixed time or client disconnected SAP connection but
+ * didn't closed RFCOMM channel in fixed time.*/
+ if (conn->io) {
+ g_io_channel_shutdown(conn->io, TRUE, NULL);
+ g_io_channel_unref(conn->io);
+ }
+ break;
+
+ case SAP_STATE_GRACEFUL_DISCONNECT:
+ /* Client didn't disconnect SAP connection in fixed time,
+ * so close SAP connection immediately. */
+ disconnect_req(conn, SAP_DISCONNECTION_TYPE_IMMEDIATE);
+ break;
+
+ default:
+ error("Unexpected state (%d).", conn->state);
+ break;
+ }
+
+ return FALSE;
+}
+
+int sap_connect_rsp(void *sap_device, uint8_t status, uint16_t maxmsgsize)
+{
+ struct sap_connection *conn = sap_device;
+ char buf[SAP_BUF_SIZE];
+ struct sap_message *msg = (struct sap_message *) buf;
+ struct sap_parameter *param = (struct sap_parameter *) msg->param;
+ size_t size = sizeof(struct sap_message);
+
+ if (!conn)
+ return -EINVAL;
+
+ DBG("state %d pr 0x%02x status 0x%02x", conn->state,
+ conn->processing_req, status);
+
+ if (conn->state != SAP_STATE_CONNECT_IN_PROGRESS)
+ return -EPERM;
+
+ memset(buf, 0, sizeof(buf));
+ msg->id = SAP_CONNECT_RESP;
+ msg->nparam = 0x01;
+
+ /* Add connection status */
+ param->id = SAP_PARAM_ID_CONN_STATUS;
+ param->len = htons(SAP_PARAM_ID_CONN_STATUS_LEN);
+ *param->val = status;
+ size += PARAMETER_SIZE(SAP_PARAM_ID_CONN_STATUS_LEN);
+
+ /* Add MaxMsgSize */
+ if (maxmsgsize && (status == SAP_STATUS_MAX_MSG_SIZE_NOT_SUPPORTED ||
+ status == SAP_STATUS_MAX_MSG_SIZE_TOO_SMALL)) {
+ uint16_t *len;
+
+ msg->nparam++;
+ param = (struct sap_parameter *) &buf[size];
+ param->id = SAP_PARAM_ID_MAX_MSG_SIZE;
+ param->len = htons(SAP_PARAM_ID_MAX_MSG_SIZE_LEN);
+ len = (uint16_t *) &param->val;
+ *len = htons(maxmsgsize);
+ size += PARAMETER_SIZE(SAP_PARAM_ID_MAX_MSG_SIZE_LEN);
+ }
+
+ if (status == SAP_STATUS_OK) {
+ gboolean connected = TRUE;
+
+ emit_property_changed(connection, server->path,
+ SAP_SERVER_INTERFACE,
+ "Connected", DBUS_TYPE_BOOLEAN, &connected);
+
+ conn->state = SAP_STATE_CONNECTED;
+ } else {
+ conn->state = SAP_STATE_DISCONNECTED;
+
+ /* Timer will shutdown channel if client doesn't send
+ * CONNECT_REQ or doesn't shutdown channel itself.*/
+ start_guard_timer(conn, SAP_TIMER_NO_ACTIVITY);
+ }
+
+ conn->processing_req = SAP_NO_REQ;
+
+ return send_message(sap_device, buf, size);
+}
+
+int sap_disconnect_rsp(void *sap_device)
+{
+ struct sap_connection *conn = sap_device;
+ struct sap_message msg;
+
+ if (!conn)
+ return -EINVAL;
+
+ DBG("state %d pr 0x%02x", conn->state, conn->processing_req);
+
+ switch (conn->state) {
+ case SAP_STATE_CLIENT_DISCONNECT:
+ memset(&msg, 0, sizeof(msg));
+ msg.id = SAP_DISCONNECT_RESP;
+
+ conn->state = SAP_STATE_DISCONNECTED;
+ conn->processing_req = SAP_NO_REQ;
+
+ /* Timer will close channel if client doesn't do it.*/
+ start_guard_timer(conn, SAP_TIMER_NO_ACTIVITY);
+
+ return send_message(sap_device, &msg, sizeof(msg));
+
+ case SAP_STATE_IMMEDIATE_DISCONNECT:
+ conn->state = SAP_STATE_DISCONNECTED;
+ conn->processing_req = SAP_NO_REQ;
+
+ if (conn->io) {
+ g_io_channel_shutdown(conn->io, TRUE, NULL);
+ g_io_channel_unref(conn->io);
+ }
+
+ return 0;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+int sap_transfer_apdu_rsp(void *sap_device, uint8_t result, uint8_t *apdu,
+ uint16_t length)
+{
+ struct sap_connection *conn = sap_device;
+ char buf[SAP_BUF_SIZE];
+ struct sap_message *msg = (struct sap_message *) buf;
+ struct sap_parameter *param = (struct sap_parameter *) msg->param;
+ size_t size = sizeof(struct sap_message);
+
+ if (!conn)
+ return -EINVAL;
+
+ DBG("state %d pr 0x%02x", conn->state, conn->processing_req);
+
+ if (conn->processing_req != SAP_TRANSFER_APDU_REQ)
+ return 0;
+
+ if (result == SAP_RESULT_OK && (!apdu || (apdu && length == 0x00)))
+ return -EINVAL;
+
+ memset(buf, 0, sizeof(buf));
+ msg->id = SAP_TRANSFER_APDU_RESP;
+ msg->nparam = 0x01;
+ size += add_result_parameter(result, param);
+
+ /* Add APDU response. */
+ if (result == SAP_RESULT_OK) {
+ msg->nparam++;
+ param = (struct sap_parameter *) &buf[size];
+ param->id = SAP_PARAM_ID_RESPONSE_APDU;
+ param->len = htons(length);
+
+ size += PARAMETER_SIZE(length);
+
+ if (size > SAP_BUF_SIZE)
+ return -EOVERFLOW;
+
+ memcpy(param->val, apdu, length);
+ }
+
+ conn->processing_req = SAP_NO_REQ;
+
+ return send_message(sap_device, buf, size);
+}
+
+int sap_transfer_atr_rsp(void *sap_device, uint8_t result, uint8_t *atr,
+ uint16_t length)
+{
+ struct sap_connection *conn = sap_device;
+ char buf[SAP_BUF_SIZE];
+ struct sap_message *msg = (struct sap_message *) buf;
+ struct sap_parameter *param = (struct sap_parameter *) msg->param;
+ size_t size = sizeof(struct sap_message);
+
+ if (!conn)
+ return -EINVAL;
+
+ DBG("result 0x%02x state %d pr 0x%02x len %d", result, conn->state,
+ conn->processing_req, length);
+
+ if (conn->processing_req != SAP_TRANSFER_ATR_REQ)
+ return 0;
+
+ if (result == SAP_RESULT_OK && (!atr || (atr && length == 0x00)))
+ return -EINVAL;
+
+ memset(buf, 0, sizeof(buf));
+ msg->id = SAP_TRANSFER_ATR_RESP;
+ msg->nparam = 0x01;
+ size += add_result_parameter(result, param);
+
+ /* Add ATR response */
+ if (result == SAP_RESULT_OK) {
+ msg->nparam++;
+ param = (struct sap_parameter *) &buf[size];
+ param->id = SAP_PARAM_ID_ATR;
+ param->len = htons(length);
+ size += PARAMETER_SIZE(length);
+
+ if (size > SAP_BUF_SIZE)
+ return -EOVERFLOW;
+
+ memcpy(param->val, atr, length);
+ }
+
+ conn->processing_req = SAP_NO_REQ;
+
+ return send_message(sap_device, buf, size);
+}
+
+int sap_power_sim_off_rsp(void *sap_device, uint8_t result)
+{
+ struct sap_connection *conn = sap_device;
+ char buf[SAP_BUF_SIZE];
+ struct sap_message *msg = (struct sap_message *) buf;
+ size_t size = sizeof(struct sap_message);
+
+ if (!conn)
+ return -EINVAL;
+
+ DBG("state %d pr 0x%02x", conn->state, conn->processing_req);
+
+ if (conn->processing_req != SAP_POWER_SIM_OFF_REQ)
+ return 0;
+
+ memset(buf, 0, sizeof(buf));
+ msg->id = SAP_POWER_SIM_OFF_RESP;
+ msg->nparam = 0x01;
+ size += add_result_parameter(result, msg->param);
+
+ conn->processing_req = SAP_NO_REQ;
+
+ return send_message(sap_device, buf, size);
+}
+
+int sap_power_sim_on_rsp(void *sap_device, uint8_t result)
+{
+ struct sap_connection *conn = sap_device;
+ char buf[SAP_BUF_SIZE];
+ struct sap_message *msg = (struct sap_message *) buf;
+ size_t size = sizeof(struct sap_message);
+
+ if (!conn)
+ return -EINVAL;
+
+ DBG("state %d pr 0x%02x", conn->state, conn->processing_req);
+
+ if (conn->processing_req != SAP_POWER_SIM_ON_REQ)
+ return 0;
+
+ memset(buf, 0, sizeof(buf));
+ msg->id = SAP_POWER_SIM_ON_RESP;
+ msg->nparam = 0x01;
+ size += add_result_parameter(result, msg->param);
+
+ conn->processing_req = SAP_NO_REQ;
+
+ return send_message(sap_device, buf, size);
+}
+
+int sap_reset_sim_rsp(void *sap_device, uint8_t result)
+{
+ struct sap_connection *conn = sap_device;
+ char buf[SAP_BUF_SIZE];
+ struct sap_message *msg = (struct sap_message *) buf;
+ size_t size = sizeof(struct sap_message);
+
+ if (!conn)
+ return -EINVAL;
+
+ DBG("state %d pr 0x%02x result 0x%02x", conn->state,
+ conn->processing_req, result);
+
+ if (conn->processing_req != SAP_RESET_SIM_REQ)
+ return 0;
+
+ memset(buf, 0, sizeof(buf));
+ msg->id = SAP_RESET_SIM_RESP;
+ msg->nparam = 0x01;
+ size += add_result_parameter(result, msg->param);
+
+ conn->processing_req = SAP_NO_REQ;
+
+ return send_message(sap_device, buf, size);
+}
+
+int sap_transfer_card_reader_status_rsp(void *sap_device, uint8_t result,
+ uint8_t status)
+{
+ struct sap_connection *conn = sap_device;
+ char buf[SAP_BUF_SIZE];
+ struct sap_message *msg = (struct sap_message *) buf;
+ struct sap_parameter *param = (struct sap_parameter *) msg->param;
+ size_t size = sizeof(struct sap_message);
+
+ if (!conn)
+ return -EINVAL;
+
+ DBG("state %d pr 0x%02x result 0x%02x", conn->state,
+ conn->processing_req, result);
+
+ if (conn->processing_req != SAP_TRANSFER_CARD_READER_STATUS_REQ)
+ return 0;
+
+ memset(buf, 0, sizeof(buf));
+ msg->id = SAP_TRANSFER_CARD_READER_STATUS_RESP;
+ msg->nparam = 0x01;
+ size += add_result_parameter(result, param);
+
+ /* Add card reader status. */
+ if (result == SAP_RESULT_OK) {
+ msg->nparam++;
+ param = (struct sap_parameter *) &buf[size];
+ param->id = SAP_PARAM_ID_CARD_READER_STATUS;
+ param->len = htons(SAP_PARAM_ID_CARD_READER_STATUS_LEN);
+ *param->val = status;
+ size += PARAMETER_SIZE(SAP_PARAM_ID_CARD_READER_STATUS_LEN);
+ }
+
+ conn->processing_req = SAP_NO_REQ;
+
+ return send_message(sap_device, buf, size);
+}
+
+int sap_transport_protocol_rsp(void *sap_device, uint8_t result)
+{
+ struct sap_connection *conn = sap_device;
+ char buf[SAP_BUF_SIZE];
+ struct sap_message *msg = (struct sap_message *) buf;
+ size_t size = sizeof(struct sap_message);
+
+ if (!conn)
+ return -EINVAL;
+
+ DBG("state %d pr 0x%02x result 0x%02x", conn->state,
+ conn->processing_req, result);
+
+ if (conn->processing_req != SAP_SET_TRANSPORT_PROTOCOL_REQ)
+ return 0;
+
+ memset(buf, 0, sizeof(buf));
+ msg->id = SAP_SET_TRANSPORT_PROTOCOL_RESP;
+ msg->nparam = 0x01;
+ size += add_result_parameter(result, msg->param);
+
+ conn->processing_req = SAP_NO_REQ;
+
+ return send_message(sap_device, buf, size);
+}
+
+int sap_error_rsp(void *sap_device)
+{
+ struct sap_message msg;
+ struct sap_connection *conn = sap_device;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.id = SAP_ERROR_RESP;
+
+ return send_message(conn, &msg, sizeof(msg));
+}
+
+int sap_status_ind(void *sap_device, uint8_t status_change)
+{
+ struct sap_connection *conn = sap_device;
+ char buf[SAP_BUF_SIZE];
+ struct sap_message *msg = (struct sap_message *) buf;
+ struct sap_parameter *param = (struct sap_parameter *) msg->param;
+ size_t size = sizeof(struct sap_message);
+
+ if (!conn)
+ return -EINVAL;
+
+ DBG("state %d pr 0x%02x sc 0x%02x", conn->state, conn->processing_req,
+ status_change);
+
+ if (conn->state != SAP_STATE_CONNECTED &&
+ conn->state != SAP_STATE_GRACEFUL_DISCONNECT)
+ return 0;
+
+ memset(buf, 0, sizeof(buf));
+ msg->id = SAP_STATUS_IND;
+ msg->nparam = 0x01;
+
+ /* Add status change. */
+ param->id = SAP_PARAM_ID_STATUS_CHANGE;
+ param->len = htons(SAP_PARAM_ID_STATUS_CHANGE_LEN);
+ *param->val = status_change;
+ size += PARAMETER_SIZE(SAP_PARAM_ID_STATUS_CHANGE_LEN);
+
+ return send_message(sap_device, buf, size);
+}
+
+static int handle_cmd(void *data, void *buf, size_t size)
+{
+ struct sap_message *msg = buf;
+ struct sap_connection *conn = data;
+
+ if (!conn)
+ return -EINVAL;
+
+ if (size < sizeof(struct sap_message))
+ goto error_rsp;
+
+ if (msg->nparam != 0 && size < (sizeof(struct sap_message) +
+ sizeof(struct sap_parameter) + 4))
+ goto error_rsp;
+
+ if (check_msg(msg) < 0)
+ goto error_rsp;
+
+ switch (msg->id) {
+ case SAP_CONNECT_REQ:
+ connect_req(conn, msg->param);
+ return 0;
+ case SAP_DISCONNECT_REQ:
+ disconnect_req(conn, SAP_DISCONNECTION_TYPE_CLIENT);
+ return 0;
+ case SAP_TRANSFER_APDU_REQ:
+ transfer_apdu_req(conn, msg->param);
+ return 0;
+ case SAP_TRANSFER_ATR_REQ:
+ transfer_atr_req(conn);
+ return 0;
+ case SAP_POWER_SIM_OFF_REQ:
+ power_sim_off_req(conn);
+ return 0;
+ case SAP_POWER_SIM_ON_REQ:
+ power_sim_on_req(conn);
+ return 0;
+ case SAP_RESET_SIM_REQ:
+ reset_sim_req(conn);
+ return 0;
+ case SAP_TRANSFER_CARD_READER_STATUS_REQ:
+ transfer_card_reader_status_req(conn);
+ return 0;
+ case SAP_SET_TRANSPORT_PROTOCOL_REQ:
+ set_transport_protocol_req(conn, msg->param);
+ return 0;
+ default:
+ DBG("SAP unknown message.");
+ break;
+ }
+
+error_rsp:
+ DBG("Bad request message format.");
+ sap_error_rsp(conn);
+ return -EBADMSG;
+}
+
+static void sap_conn_remove(struct sap_connection *conn)
+{
+ DBG("conn %p", conn);
+
+ if (!conn)
+ return;
+
+ if (conn->io) {
+ g_io_channel_shutdown(conn->io, TRUE, NULL);
+ g_io_channel_unref(conn->io);
+ }
+
+ conn->io = NULL;
+ g_free(conn);
+ server->conn = NULL;
+}
+
+static gboolean sap_io_cb(GIOChannel *io, GIOCondition cond, gpointer data)
+{
+ char buf[SAP_BUF_SIZE];
+ size_t bytes_read = 0;
+ GError *gerr = NULL;
+ GIOStatus gstatus;
+
+ DBG("io %p", io);
+
+ if (cond & G_IO_NVAL) {
+ DBG("ERR (G_IO_NVAL) on rfcomm socket.");
+ return FALSE;
+ }
+
+ if (cond & G_IO_ERR) {
+ DBG("ERR (G_IO_ERR) on rfcomm socket.");
+ return FALSE;
+ }
+
+ if (cond & G_IO_HUP) {
+ DBG("HUP on rfcomm socket.");
+ return FALSE;
+ }
+
+ gstatus = g_io_channel_read_chars(io, buf, sizeof(buf) - 1,
+ &bytes_read, &gerr);
+ if (gstatus != G_IO_STATUS_NORMAL) {
+ if (gerr)
+ g_error_free(gerr);
+
+ return TRUE;
+ }
+
+ if (handle_cmd(data, buf, bytes_read) < 0)
+ error("Invalid SAP message.");
+
+ return TRUE;
+}
+
+static void sap_io_destroy(void *data)
+{
+ struct sap_connection *conn = data;
+
+ DBG("conn %p", conn);
+
+ if (conn && conn->io) {
+ gboolean connected = FALSE;
+
+ stop_guard_timer(conn);
+
+ if (conn->state != SAP_STATE_CONNECT_IN_PROGRESS)
+ emit_property_changed(connection, server->path,
+ SAP_SERVER_INTERFACE, "Connected",
+ DBUS_TYPE_BOOLEAN, &connected);
+
+ if (conn->state == SAP_STATE_CONNECT_IN_PROGRESS ||
+ conn->state == SAP_STATE_CONNECTED ||
+ conn->state == SAP_STATE_GRACEFUL_DISCONNECT)
+ sap_disconnect_req(NULL, 1);
+
+ conn->io = NULL;
+ sap_conn_remove(conn);
+ }
+}
+
+static void sap_connect_cb(GIOChannel *io, GError *gerr, gpointer data)
+{
+ struct sap_connection *conn = data;
+
+ DBG("io %p gerr %p data %p ", io, gerr, data);
+
+ if (!conn)
+ return;
+
+ /* Timer will shutdown the channel in case of lack of client
+ activity */
+ start_guard_timer(conn, SAP_TIMER_NO_ACTIVITY);
+
+ g_io_add_watch_full(io, G_PRIORITY_DEFAULT,
+ G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ sap_io_cb, conn, sap_io_destroy);
+}
+
+static void connect_auth_cb(DBusError *derr, void *data)
+{
+ struct sap_connection *conn = data;
+ GError *gerr = NULL;
+
+ DBG("derr %p data %p ", derr, data);
+
+ if (!conn)
+ return;
+
+ if (derr && dbus_error_is_set(derr)) {
+ error("Access denied: %s", derr->message);
+ sap_conn_remove(conn);
+ return;
+ }
+
+ if (!bt_io_accept(conn->io, sap_connect_cb, conn, NULL, &gerr)) {
+ error("bt_io_accept: %s", gerr->message);
+ g_error_free(gerr);
+ sap_conn_remove(conn);
+ return;
+ }
+
+ DBG("Client has been authorized.");
+}
+
+static void connect_confirm_cb(GIOChannel *io, gpointer data)
+{
+ struct sap_connection *conn = server->conn;
+ GError *gerr = NULL;
+ bdaddr_t src, dst;
+ int err;
+
+ DBG("io %p data %p ", io, data);
+
+ if (!io)
+ return;
+
+ if (conn) {
+ g_io_channel_shutdown(io, TRUE, NULL);
+ return;
+ }
+
+ conn = g_try_new0(struct sap_connection, 1);
+ if (!conn) {
+ error("Can't allocate memory for incomming SAP connection.");
+ g_io_channel_shutdown(io, TRUE, NULL);
+ return;
+ }
+
+ g_io_channel_set_encoding(io, NULL, NULL);
+ g_io_channel_set_buffered(io, FALSE);
+
+ server->conn = conn;
+ conn->io = g_io_channel_ref(io);
+ conn->state = SAP_STATE_DISCONNECTED;
+
+ bt_io_get(io, BT_IO_RFCOMM, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, &src,
+ BT_IO_OPT_DEST_BDADDR, &dst,
+ BT_IO_OPT_INVALID);
+ if (gerr) {
+ error("%s", gerr->message);
+ g_error_free(gerr);
+ sap_conn_remove(conn);
+ return;
+ }
+
+ err = btd_request_authorization(&src, &dst, SAP_UUID,
+ connect_auth_cb, conn);
+ if (err < 0) {
+ DBG("Authorization denied: %d %s", err, strerror(err));
+ sap_conn_remove(conn);
+ return;
+ }
+
+ DBG("SAP incoming connection (sock %d) authorization.",
+ g_io_channel_unix_get_fd(io));
+}
+
+static inline DBusMessage *message_failed(DBusMessage *msg,
+ const char *description)
+{
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
+ "%s", description);
+}
+
+static DBusMessage *disconnect(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct sap_server *server = data;
+
+ DBG("server %p", server);
+
+ if (!server)
+ return message_failed(msg, "Server internal error.");
+
+ DBG("conn %p", server->conn);
+
+ if (!server->conn)
+ return message_failed(msg, "Client already disconnected");
+
+ if (disconnect_req(server->conn, SAP_DISCONNECTION_TYPE_GRACEFUL) < 0)
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
+ "There is no active connection");
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *get_properties(DBusConnection *c,
+ DBusMessage *msg, void *data)
+{
+ struct sap_connection *conn = data;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ dbus_bool_t connected;
+
+ if (!conn)
+ return message_failed(msg, "Server internal error.");
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ connected = (conn->state == SAP_STATE_CONNECTED ||
+ conn->state == SAP_STATE_GRACEFUL_DISCONNECT);
+ dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &connected);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static GDBusMethodTable server_methods[] = {
+ {"GetProperties", "", "a{sv}", get_properties},
+ {"Disconnect", "", "", disconnect},
+ { }
+};
+
+static GDBusSignalTable server_signals[] = {
+ { "PropertyChanged", "sv"},
+ { }
+};
+
+static void server_free(struct sap_server *server)
+{
+ if (!server)
+ return;
+
+ sap_conn_remove(server->conn);
+ g_free(server->path);
+ g_free(server);
+ server = NULL;
+}
+
+static void destroy_sap_interface(void *data)
+{
+ struct sap_server *server = data;
+
+ DBG("Unregistered interface %s on path %s",
+ SAP_SERVER_INTERFACE, server->path);
+
+ server_free(server);
+}
+
+int sap_server_register(const char *path, bdaddr_t *src)
+{
+ sdp_record_t *record = NULL;
+ GError *gerr = NULL;
+ GIOChannel *io;
+
+ if (sap_init() < 0) {
+ error("Sap driver initialization failed.");
+ return -1;
+ }
+
+ server = g_try_new0(struct sap_server, 1);
+ if (!server) {
+ sap_exit();
+ return -ENOMEM;
+ }
+
+ bacpy(&server->src, src);
+ server->path = g_strdup(path);
+
+ record = create_sap_record(SAP_SERVER_CHANNEL);
+ if (!record) {
+ error("Creating SAP SDP record failed.");
+ goto sdp_err;
+ }
+
+ if (add_record_to_server(&server->src, record) < 0) {
+ error("Adding SAP SDP record to the SDP server failed.");
+ sdp_record_free(record);
+ goto sdp_err;
+ }
+
+ server->record_id = record->handle;
+
+ io = bt_io_listen(BT_IO_RFCOMM, NULL, connect_confirm_cb, server,
+ NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, &server->src,
+ BT_IO_OPT_CHANNEL, SAP_SERVER_CHANNEL,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_HIGH,
+ BT_IO_OPT_MASTER, TRUE,
+ BT_IO_OPT_INVALID);
+ if (!io) {
+ error("Can't listen at channel %d.", SAP_SERVER_CHANNEL);
+ g_error_free(gerr);
+ goto server_err;
+ }
+
+ DBG("Listen socket 0x%02x", g_io_channel_unix_get_fd(io));
+
+ server->listen_io = io;
+ server->conn = NULL;
+
+ if (!g_dbus_register_interface(connection, path, SAP_SERVER_INTERFACE,
+ server_methods, server_signals, NULL,
+ server, destroy_sap_interface)) {
+ error("D-Bus failed to register %s interface",
+ SAP_SERVER_INTERFACE);
+ goto server_err;
+ }
+
+ return 0;
+
+server_err:
+ remove_record_from_server(server->record_id);
+sdp_err:
+ server_free(server);
+ sap_exit();
+
+ return -1;
+}
+
+int sap_server_unregister(const char *path)
+{
+ if (!server)
+ return -EINVAL;
+
+ remove_record_from_server(server->record_id);
+
+ if (server->conn)
+ sap_conn_remove(server->conn);
+
+ if (server->listen_io) {
+ g_io_channel_shutdown(server->listen_io, TRUE, NULL);
+ g_io_channel_unref(server->listen_io);
+ server->listen_io = NULL;
+ }
+
+ g_dbus_unregister_interface(connection, path, SAP_SERVER_INTERFACE);
+
+ sap_exit();
+
+ return 0;
+}
+
+int sap_server_init(DBusConnection *conn)
+{
+ connection = dbus_connection_ref(conn);
+ return 0;
+}
+
+void sap_server_exit(void)
+{
+ dbus_connection_unref(connection);
+ connection = NULL;
+}
diff --git a/sap/server.h b/sap/server.h
new file mode 100644
index 0000000..ef2b7b8
--- /dev/null
+++ b/sap/server.h
@@ -0,0 +1,26 @@
+/*
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 ST-Ericsson SA
+ *
+ * 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
+ */
+
+#include <gdbus.h>
+
+int sap_server_init(DBusConnection *conn);
+void sap_server_exit(void);
+int sap_server_register(const char *path, bdaddr_t *src);
+int sap_server_unregister(const char *path);
diff --git a/sbc/formats.h b/sbc/formats.h
new file mode 100644
index 0000000..3050b25
--- /dev/null
+++ b/sbc/formats.h
@@ -0,0 +1,55 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2004-2010 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
+ *
+ */
+
+#include <byteswap.h>
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define COMPOSE_ID(a,b,c,d) ((a) | ((b)<<8) | ((c)<<16) | ((d)<<24))
+#define LE_SHORT(v) (v)
+#define LE_INT(v) (v)
+#define BE_SHORT(v) bswap_16(v)
+#define BE_INT(v) bswap_32(v)
+#elif __BYTE_ORDER == __BIG_ENDIAN
+#define COMPOSE_ID(a,b,c,d) ((d) | ((c)<<8) | ((b)<<16) | ((a)<<24))
+#define LE_SHORT(v) bswap_16(v)
+#define LE_INT(v) bswap_32(v)
+#define BE_SHORT(v) (v)
+#define BE_INT(v) (v)
+#else
+#error "Wrong endian"
+#endif
+
+#define AU_MAGIC COMPOSE_ID('.','s','n','d')
+
+#define AU_FMT_ULAW 1
+#define AU_FMT_LIN8 2
+#define AU_FMT_LIN16 3
+
+struct au_header {
+ uint32_t magic; /* '.snd' */
+ uint32_t hdr_size; /* size of header (min 24) */
+ uint32_t data_size; /* size of data */
+ uint32_t encoding; /* see to AU_FMT_XXXX */
+ uint32_t sample_rate; /* sample rate */
+ uint32_t channels; /* number of channels (voices) */
+};
diff --git a/sbc/sbc.c b/sbc/sbc.c
new file mode 100644
index 0000000..77fcc5d
--- /dev/null
+++ b/sbc/sbc.c
@@ -0,0 +1,1234 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2008-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2005 Henryk Ploetz <henryk@ploetzli.ch>
+ * Copyright (C) 2005-2008 Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+/* todo items:
+
+ use a log2 table for byte integer scale factors calculation (sum log2 results
+ for high and low bytes) fill bitpool by 16 bits instead of one at a time in
+ bits allocation/bitpool generation port to the dsp
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <limits.h>
+
+#include "sbc_math.h"
+#include "sbc_tables.h"
+
+#include "sbc.h"
+#include "sbc_primitives.h"
+
+#define SBC_SYNCWORD 0x9C
+
+/* This structure contains an unpacked SBC frame.
+ Yes, there is probably quite some unused space herein */
+struct sbc_frame {
+ uint8_t frequency;
+ uint8_t block_mode;
+ uint8_t blocks;
+ enum {
+ MONO = SBC_MODE_MONO,
+ DUAL_CHANNEL = SBC_MODE_DUAL_CHANNEL,
+ STEREO = SBC_MODE_STEREO,
+ JOINT_STEREO = SBC_MODE_JOINT_STEREO
+ } mode;
+ uint8_t channels;
+ enum {
+ LOUDNESS = SBC_AM_LOUDNESS,
+ SNR = SBC_AM_SNR
+ } allocation;
+ uint8_t subband_mode;
+ uint8_t subbands;
+ uint8_t bitpool;
+ uint16_t codesize;
+ uint8_t length;
+
+ /* bit number x set means joint stereo has been used in subband x */
+ uint8_t joint;
+
+ /* only the lower 4 bits of every element are to be used */
+ uint32_t SBC_ALIGNED scale_factor[2][8];
+
+ /* raw integer subband samples in the frame */
+ int32_t SBC_ALIGNED sb_sample_f[16][2][8];
+
+ /* modified subband samples */
+ int32_t SBC_ALIGNED sb_sample[16][2][8];
+
+ /* original pcm audio samples */
+ int16_t SBC_ALIGNED pcm_sample[2][16*8];
+};
+
+struct sbc_decoder_state {
+ int subbands;
+ int32_t V[2][170];
+ int offset[2][16];
+};
+
+/*
+ * Calculates the CRC-8 of the first len bits in data
+ */
+static const uint8_t crc_table[256] = {
+ 0x00, 0x1D, 0x3A, 0x27, 0x74, 0x69, 0x4E, 0x53,
+ 0xE8, 0xF5, 0xD2, 0xCF, 0x9C, 0x81, 0xA6, 0xBB,
+ 0xCD, 0xD0, 0xF7, 0xEA, 0xB9, 0xA4, 0x83, 0x9E,
+ 0x25, 0x38, 0x1F, 0x02, 0x51, 0x4C, 0x6B, 0x76,
+ 0x87, 0x9A, 0xBD, 0xA0, 0xF3, 0xEE, 0xC9, 0xD4,
+ 0x6F, 0x72, 0x55, 0x48, 0x1B, 0x06, 0x21, 0x3C,
+ 0x4A, 0x57, 0x70, 0x6D, 0x3E, 0x23, 0x04, 0x19,
+ 0xA2, 0xBF, 0x98, 0x85, 0xD6, 0xCB, 0xEC, 0xF1,
+ 0x13, 0x0E, 0x29, 0x34, 0x67, 0x7A, 0x5D, 0x40,
+ 0xFB, 0xE6, 0xC1, 0xDC, 0x8F, 0x92, 0xB5, 0xA8,
+ 0xDE, 0xC3, 0xE4, 0xF9, 0xAA, 0xB7, 0x90, 0x8D,
+ 0x36, 0x2B, 0x0C, 0x11, 0x42, 0x5F, 0x78, 0x65,
+ 0x94, 0x89, 0xAE, 0xB3, 0xE0, 0xFD, 0xDA, 0xC7,
+ 0x7C, 0x61, 0x46, 0x5B, 0x08, 0x15, 0x32, 0x2F,
+ 0x59, 0x44, 0x63, 0x7E, 0x2D, 0x30, 0x17, 0x0A,
+ 0xB1, 0xAC, 0x8B, 0x96, 0xC5, 0xD8, 0xFF, 0xE2,
+ 0x26, 0x3B, 0x1C, 0x01, 0x52, 0x4F, 0x68, 0x75,
+ 0xCE, 0xD3, 0xF4, 0xE9, 0xBA, 0xA7, 0x80, 0x9D,
+ 0xEB, 0xF6, 0xD1, 0xCC, 0x9F, 0x82, 0xA5, 0xB8,
+ 0x03, 0x1E, 0x39, 0x24, 0x77, 0x6A, 0x4D, 0x50,
+ 0xA1, 0xBC, 0x9B, 0x86, 0xD5, 0xC8, 0xEF, 0xF2,
+ 0x49, 0x54, 0x73, 0x6E, 0x3D, 0x20, 0x07, 0x1A,
+ 0x6C, 0x71, 0x56, 0x4B, 0x18, 0x05, 0x22, 0x3F,
+ 0x84, 0x99, 0xBE, 0xA3, 0xF0, 0xED, 0xCA, 0xD7,
+ 0x35, 0x28, 0x0F, 0x12, 0x41, 0x5C, 0x7B, 0x66,
+ 0xDD, 0xC0, 0xE7, 0xFA, 0xA9, 0xB4, 0x93, 0x8E,
+ 0xF8, 0xE5, 0xC2, 0xDF, 0x8C, 0x91, 0xB6, 0xAB,
+ 0x10, 0x0D, 0x2A, 0x37, 0x64, 0x79, 0x5E, 0x43,
+ 0xB2, 0xAF, 0x88, 0x95, 0xC6, 0xDB, 0xFC, 0xE1,
+ 0x5A, 0x47, 0x60, 0x7D, 0x2E, 0x33, 0x14, 0x09,
+ 0x7F, 0x62, 0x45, 0x58, 0x0B, 0x16, 0x31, 0x2C,
+ 0x97, 0x8A, 0xAD, 0xB0, 0xE3, 0xFE, 0xD9, 0xC4
+};
+
+static uint8_t sbc_crc8(const uint8_t *data, size_t len)
+{
+ uint8_t crc = 0x0f;
+ size_t i;
+ uint8_t octet;
+
+ for (i = 0; i < len / 8; i++)
+ crc = crc_table[crc ^ data[i]];
+
+ octet = data[i];
+ for (i = 0; i < len % 8; i++) {
+ char bit = ((octet ^ crc) & 0x80) >> 7;
+
+ crc = ((crc & 0x7f) << 1) ^ (bit ? 0x1d : 0);
+
+ octet = octet << 1;
+ }
+
+ return crc;
+}
+
+/*
+ * Code straight from the spec to calculate the bits array
+ * Takes a pointer to the frame in question, a pointer to the bits array and
+ * the sampling frequency (as 2 bit integer)
+ */
+static SBC_ALWAYS_INLINE void sbc_calculate_bits_internal(
+ const struct sbc_frame *frame, int (*bits)[8], int subbands)
+{
+ uint8_t sf = frame->frequency;
+
+ if (frame->mode == MONO || frame->mode == DUAL_CHANNEL) {
+ int bitneed[2][8], loudness, max_bitneed, bitcount, slicecount, bitslice;
+ int ch, sb;
+
+ for (ch = 0; ch < frame->channels; ch++) {
+ max_bitneed = 0;
+ if (frame->allocation == SNR) {
+ for (sb = 0; sb < subbands; sb++) {
+ bitneed[ch][sb] = frame->scale_factor[ch][sb];
+ if (bitneed[ch][sb] > max_bitneed)
+ max_bitneed = bitneed[ch][sb];
+ }
+ } else {
+ for (sb = 0; sb < subbands; sb++) {
+ if (frame->scale_factor[ch][sb] == 0)
+ bitneed[ch][sb] = -5;
+ else {
+ if (subbands == 4)
+ loudness = frame->scale_factor[ch][sb] - sbc_offset4[sf][sb];
+ else
+ loudness = frame->scale_factor[ch][sb] - sbc_offset8[sf][sb];
+ if (loudness > 0)
+ bitneed[ch][sb] = loudness / 2;
+ else
+ bitneed[ch][sb] = loudness;
+ }
+ if (bitneed[ch][sb] > max_bitneed)
+ max_bitneed = bitneed[ch][sb];
+ }
+ }
+
+ bitcount = 0;
+ slicecount = 0;
+ bitslice = max_bitneed + 1;
+ do {
+ bitslice--;
+ bitcount += slicecount;
+ slicecount = 0;
+ for (sb = 0; sb < subbands; sb++) {
+ if ((bitneed[ch][sb] > bitslice + 1) && (bitneed[ch][sb] < bitslice + 16))
+ slicecount++;
+ else if (bitneed[ch][sb] == bitslice + 1)
+ slicecount += 2;
+ }
+ } while (bitcount + slicecount < frame->bitpool);
+
+ if (bitcount + slicecount == frame->bitpool) {
+ bitcount += slicecount;
+ bitslice--;
+ }
+
+ for (sb = 0; sb < subbands; sb++) {
+ if (bitneed[ch][sb] < bitslice + 2)
+ bits[ch][sb] = 0;
+ else {
+ bits[ch][sb] = bitneed[ch][sb] - bitslice;
+ if (bits[ch][sb] > 16)
+ bits[ch][sb] = 16;
+ }
+ }
+
+ for (sb = 0; bitcount < frame->bitpool &&
+ sb < subbands; sb++) {
+ if ((bits[ch][sb] >= 2) && (bits[ch][sb] < 16)) {
+ bits[ch][sb]++;
+ bitcount++;
+ } else if ((bitneed[ch][sb] == bitslice + 1) && (frame->bitpool > bitcount + 1)) {
+ bits[ch][sb] = 2;
+ bitcount += 2;
+ }
+ }
+
+ for (sb = 0; bitcount < frame->bitpool &&
+ sb < subbands; sb++) {
+ if (bits[ch][sb] < 16) {
+ bits[ch][sb]++;
+ bitcount++;
+ }
+ }
+
+ }
+
+ } else if (frame->mode == STEREO || frame->mode == JOINT_STEREO) {
+ int bitneed[2][8], loudness, max_bitneed, bitcount, slicecount, bitslice;
+ int ch, sb;
+
+ max_bitneed = 0;
+ if (frame->allocation == SNR) {
+ for (ch = 0; ch < 2; ch++) {
+ for (sb = 0; sb < subbands; sb++) {
+ bitneed[ch][sb] = frame->scale_factor[ch][sb];
+ if (bitneed[ch][sb] > max_bitneed)
+ max_bitneed = bitneed[ch][sb];
+ }
+ }
+ } else {
+ for (ch = 0; ch < 2; ch++) {
+ for (sb = 0; sb < subbands; sb++) {
+ if (frame->scale_factor[ch][sb] == 0)
+ bitneed[ch][sb] = -5;
+ else {
+ if (subbands == 4)
+ loudness = frame->scale_factor[ch][sb] - sbc_offset4[sf][sb];
+ else
+ loudness = frame->scale_factor[ch][sb] - sbc_offset8[sf][sb];
+ if (loudness > 0)
+ bitneed[ch][sb] = loudness / 2;
+ else
+ bitneed[ch][sb] = loudness;
+ }
+ if (bitneed[ch][sb] > max_bitneed)
+ max_bitneed = bitneed[ch][sb];
+ }
+ }
+ }
+
+ bitcount = 0;
+ slicecount = 0;
+ bitslice = max_bitneed + 1;
+ do {
+ bitslice--;
+ bitcount += slicecount;
+ slicecount = 0;
+ for (ch = 0; ch < 2; ch++) {
+ for (sb = 0; sb < subbands; sb++) {
+ if ((bitneed[ch][sb] > bitslice + 1) && (bitneed[ch][sb] < bitslice + 16))
+ slicecount++;
+ else if (bitneed[ch][sb] == bitslice + 1)
+ slicecount += 2;
+ }
+ }
+ } while (bitcount + slicecount < frame->bitpool);
+
+ if (bitcount + slicecount == frame->bitpool) {
+ bitcount += slicecount;
+ bitslice--;
+ }
+
+ for (ch = 0; ch < 2; ch++) {
+ for (sb = 0; sb < subbands; sb++) {
+ if (bitneed[ch][sb] < bitslice + 2) {
+ bits[ch][sb] = 0;
+ } else {
+ bits[ch][sb] = bitneed[ch][sb] - bitslice;
+ if (bits[ch][sb] > 16)
+ bits[ch][sb] = 16;
+ }
+ }
+ }
+
+ ch = 0;
+ sb = 0;
+ while (bitcount < frame->bitpool) {
+ if ((bits[ch][sb] >= 2) && (bits[ch][sb] < 16)) {
+ bits[ch][sb]++;
+ bitcount++;
+ } else if ((bitneed[ch][sb] == bitslice + 1) && (frame->bitpool > bitcount + 1)) {
+ bits[ch][sb] = 2;
+ bitcount += 2;
+ }
+ if (ch == 1) {
+ ch = 0;
+ sb++;
+ if (sb >= subbands)
+ break;
+ } else
+ ch = 1;
+ }
+
+ ch = 0;
+ sb = 0;
+ while (bitcount < frame->bitpool) {
+ if (bits[ch][sb] < 16) {
+ bits[ch][sb]++;
+ bitcount++;
+ }
+ if (ch == 1) {
+ ch = 0;
+ sb++;
+ if (sb >= subbands)
+ break;
+ } else
+ ch = 1;
+ }
+
+ }
+
+}
+
+static void sbc_calculate_bits(const struct sbc_frame *frame, int (*bits)[8])
+{
+ if (frame->subbands == 4)
+ sbc_calculate_bits_internal(frame, bits, 4);
+ else
+ sbc_calculate_bits_internal(frame, bits, 8);
+}
+
+/*
+ * Unpacks a SBC frame at the beginning of the stream in data,
+ * which has at most len bytes into frame.
+ * Returns the length in bytes of the packed frame, or a negative
+ * value on error. The error codes are:
+ *
+ * -1 Data stream too short
+ * -2 Sync byte incorrect
+ * -3 CRC8 incorrect
+ * -4 Bitpool value out of bounds
+ */
+static int sbc_unpack_frame(const uint8_t *data, struct sbc_frame *frame,
+ size_t len)
+{
+ unsigned int consumed;
+ /* Will copy the parts of the header that are relevant to crc
+ * calculation here */
+ uint8_t crc_header[11] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ int crc_pos = 0;
+ int32_t temp;
+
+ int audio_sample;
+ int ch, sb, blk, bit; /* channel, subband, block and bit standard
+ counters */
+ int bits[2][8]; /* bits distribution */
+ uint32_t levels[2][8]; /* levels derived from that */
+
+ if (len < 4)
+ return -1;
+
+ if (data[0] != SBC_SYNCWORD)
+ return -2;
+
+ frame->frequency = (data[1] >> 6) & 0x03;
+
+ frame->block_mode = (data[1] >> 4) & 0x03;
+ switch (frame->block_mode) {
+ case SBC_BLK_4:
+ frame->blocks = 4;
+ break;
+ case SBC_BLK_8:
+ frame->blocks = 8;
+ break;
+ case SBC_BLK_12:
+ frame->blocks = 12;
+ break;
+ case SBC_BLK_16:
+ frame->blocks = 16;
+ break;
+ }
+
+ frame->mode = (data[1] >> 2) & 0x03;
+ switch (frame->mode) {
+ case MONO:
+ frame->channels = 1;
+ break;
+ case DUAL_CHANNEL: /* fall-through */
+ case STEREO:
+ case JOINT_STEREO:
+ frame->channels = 2;
+ break;
+ }
+
+ frame->allocation = (data[1] >> 1) & 0x01;
+
+ frame->subband_mode = (data[1] & 0x01);
+ frame->subbands = frame->subband_mode ? 8 : 4;
+
+ frame->bitpool = data[2];
+
+ if ((frame->mode == MONO || frame->mode == DUAL_CHANNEL) &&
+ frame->bitpool > 16 * frame->subbands)
+ return -4;
+
+ if ((frame->mode == STEREO || frame->mode == JOINT_STEREO) &&
+ frame->bitpool > 32 * frame->subbands)
+ return -4;
+
+ /* data[3] is crc, we're checking it later */
+
+ consumed = 32;
+
+ crc_header[0] = data[1];
+ crc_header[1] = data[2];
+ crc_pos = 16;
+
+ if (frame->mode == JOINT_STEREO) {
+ if (len * 8 < consumed + frame->subbands)
+ return -1;
+
+ frame->joint = 0x00;
+ for (sb = 0; sb < frame->subbands - 1; sb++)
+ frame->joint |= ((data[4] >> (7 - sb)) & 0x01) << sb;
+ if (frame->subbands == 4)
+ crc_header[crc_pos / 8] = data[4] & 0xf0;
+ else
+ crc_header[crc_pos / 8] = data[4];
+
+ consumed += frame->subbands;
+ crc_pos += frame->subbands;
+ }
+
+ if (len * 8 < consumed + (4 * frame->subbands * frame->channels))
+ return -1;
+
+ for (ch = 0; ch < frame->channels; ch++) {
+ for (sb = 0; sb < frame->subbands; sb++) {
+ /* FIXME assert(consumed % 4 == 0); */
+ frame->scale_factor[ch][sb] =
+ (data[consumed >> 3] >> (4 - (consumed & 0x7))) & 0x0F;
+ crc_header[crc_pos >> 3] |=
+ frame->scale_factor[ch][sb] << (4 - (crc_pos & 0x7));
+
+ consumed += 4;
+ crc_pos += 4;
+ }
+ }
+
+ if (data[3] != sbc_crc8(crc_header, crc_pos))
+ return -3;
+
+ sbc_calculate_bits(frame, bits);
+
+ for (ch = 0; ch < frame->channels; ch++) {
+ for (sb = 0; sb < frame->subbands; sb++)
+ levels[ch][sb] = (1 << bits[ch][sb]) - 1;
+ }
+
+ for (blk = 0; blk < frame->blocks; blk++) {
+ for (ch = 0; ch < frame->channels; ch++) {
+ for (sb = 0; sb < frame->subbands; sb++) {
+ if (levels[ch][sb] > 0) {
+ audio_sample = 0;
+ for (bit = 0; bit < bits[ch][sb]; bit++) {
+ if (consumed > len * 8)
+ return -1;
+
+ if ((data[consumed >> 3] >> (7 - (consumed & 0x7))) & 0x01)
+ audio_sample |= 1 << (bits[ch][sb] - bit - 1);
+
+ consumed++;
+ }
+
+ frame->sb_sample[blk][ch][sb] =
+ (((audio_sample << 1) | 1) << frame->scale_factor[ch][sb]) /
+ levels[ch][sb] - (1 << frame->scale_factor[ch][sb]);
+ } else
+ frame->sb_sample[blk][ch][sb] = 0;
+ }
+ }
+ }
+
+ if (frame->mode == JOINT_STEREO) {
+ for (blk = 0; blk < frame->blocks; blk++) {
+ for (sb = 0; sb < frame->subbands; sb++) {
+ if (frame->joint & (0x01 << sb)) {
+ temp = frame->sb_sample[blk][0][sb] +
+ frame->sb_sample[blk][1][sb];
+ frame->sb_sample[blk][1][sb] =
+ frame->sb_sample[blk][0][sb] -
+ frame->sb_sample[blk][1][sb];
+ frame->sb_sample[blk][0][sb] = temp;
+ }
+ }
+ }
+ }
+
+ if ((consumed & 0x7) != 0)
+ consumed += 8 - (consumed & 0x7);
+
+ return consumed >> 3;
+}
+
+static void sbc_decoder_init(struct sbc_decoder_state *state,
+ const struct sbc_frame *frame)
+{
+ int i, ch;
+
+ memset(state->V, 0, sizeof(state->V));
+ state->subbands = frame->subbands;
+
+ for (ch = 0; ch < 2; ch++)
+ for (i = 0; i < frame->subbands * 2; i++)
+ state->offset[ch][i] = (10 * i + 10);
+}
+
+static SBC_ALWAYS_INLINE int16_t sbc_clip16(int32_t s)
+{
+ if (s > 0x7FFF)
+ return 0x7FFF;
+ else if (s < -0x8000)
+ return -0x8000;
+ else
+ return s;
+}
+
+static inline void sbc_synthesize_four(struct sbc_decoder_state *state,
+ struct sbc_frame *frame, int ch, int blk)
+{
+ int i, k, idx;
+ int32_t *v = state->V[ch];
+ int *offset = state->offset[ch];
+
+ for (i = 0; i < 8; i++) {
+ /* Shifting */
+ offset[i]--;
+ if (offset[i] < 0) {
+ offset[i] = 79;
+ memcpy(v + 80, v, 9 * sizeof(*v));
+ }
+
+ /* Distribute the new matrix value to the shifted position */
+ v[offset[i]] = SCALE4_STAGED1(
+ MULA(synmatrix4[i][0], frame->sb_sample[blk][ch][0],
+ MULA(synmatrix4[i][1], frame->sb_sample[blk][ch][1],
+ MULA(synmatrix4[i][2], frame->sb_sample[blk][ch][2],
+ MUL (synmatrix4[i][3], frame->sb_sample[blk][ch][3])))));
+ }
+
+ /* Compute the samples */
+ for (idx = 0, i = 0; i < 4; i++, idx += 5) {
+ k = (i + 4) & 0xf;
+
+ /* Store in output, Q0 */
+ frame->pcm_sample[ch][blk * 4 + i] = sbc_clip16(SCALE4_STAGED1(
+ MULA(v[offset[i] + 0], sbc_proto_4_40m0[idx + 0],
+ MULA(v[offset[k] + 1], sbc_proto_4_40m1[idx + 0],
+ MULA(v[offset[i] + 2], sbc_proto_4_40m0[idx + 1],
+ MULA(v[offset[k] + 3], sbc_proto_4_40m1[idx + 1],
+ MULA(v[offset[i] + 4], sbc_proto_4_40m0[idx + 2],
+ MULA(v[offset[k] + 5], sbc_proto_4_40m1[idx + 2],
+ MULA(v[offset[i] + 6], sbc_proto_4_40m0[idx + 3],
+ MULA(v[offset[k] + 7], sbc_proto_4_40m1[idx + 3],
+ MULA(v[offset[i] + 8], sbc_proto_4_40m0[idx + 4],
+ MUL( v[offset[k] + 9], sbc_proto_4_40m1[idx + 4]))))))))))));
+ }
+}
+
+static inline void sbc_synthesize_eight(struct sbc_decoder_state *state,
+ struct sbc_frame *frame, int ch, int blk)
+{
+ int i, j, k, idx;
+ int *offset = state->offset[ch];
+
+ for (i = 0; i < 16; i++) {
+ /* Shifting */
+ offset[i]--;
+ if (offset[i] < 0) {
+ offset[i] = 159;
+ for (j = 0; j < 9; j++)
+ state->V[ch][j + 160] = state->V[ch][j];
+ }
+
+ /* Distribute the new matrix value to the shifted position */
+ state->V[ch][offset[i]] = SCALE8_STAGED1(
+ MULA(synmatrix8[i][0], frame->sb_sample[blk][ch][0],
+ MULA(synmatrix8[i][1], frame->sb_sample[blk][ch][1],
+ MULA(synmatrix8[i][2], frame->sb_sample[blk][ch][2],
+ MULA(synmatrix8[i][3], frame->sb_sample[blk][ch][3],
+ MULA(synmatrix8[i][4], frame->sb_sample[blk][ch][4],
+ MULA(synmatrix8[i][5], frame->sb_sample[blk][ch][5],
+ MULA(synmatrix8[i][6], frame->sb_sample[blk][ch][6],
+ MUL( synmatrix8[i][7], frame->sb_sample[blk][ch][7])))))))));
+ }
+
+ /* Compute the samples */
+ for (idx = 0, i = 0; i < 8; i++, idx += 5) {
+ k = (i + 8) & 0xf;
+
+ /* Store in output, Q0 */
+ frame->pcm_sample[ch][blk * 8 + i] = sbc_clip16(SCALE8_STAGED1(
+ MULA(state->V[ch][offset[i] + 0], sbc_proto_8_80m0[idx + 0],
+ MULA(state->V[ch][offset[k] + 1], sbc_proto_8_80m1[idx + 0],
+ MULA(state->V[ch][offset[i] + 2], sbc_proto_8_80m0[idx + 1],
+ MULA(state->V[ch][offset[k] + 3], sbc_proto_8_80m1[idx + 1],
+ MULA(state->V[ch][offset[i] + 4], sbc_proto_8_80m0[idx + 2],
+ MULA(state->V[ch][offset[k] + 5], sbc_proto_8_80m1[idx + 2],
+ MULA(state->V[ch][offset[i] + 6], sbc_proto_8_80m0[idx + 3],
+ MULA(state->V[ch][offset[k] + 7], sbc_proto_8_80m1[idx + 3],
+ MULA(state->V[ch][offset[i] + 8], sbc_proto_8_80m0[idx + 4],
+ MUL( state->V[ch][offset[k] + 9], sbc_proto_8_80m1[idx + 4]))))))))))));
+ }
+}
+
+static int sbc_synthesize_audio(struct sbc_decoder_state *state,
+ struct sbc_frame *frame)
+{
+ int ch, blk;
+
+ switch (frame->subbands) {
+ case 4:
+ for (ch = 0; ch < frame->channels; ch++) {
+ for (blk = 0; blk < frame->blocks; blk++)
+ sbc_synthesize_four(state, frame, ch, blk);
+ }
+ return frame->blocks * 4;
+
+ case 8:
+ for (ch = 0; ch < frame->channels; ch++) {
+ for (blk = 0; blk < frame->blocks; blk++)
+ sbc_synthesize_eight(state, frame, ch, blk);
+ }
+ return frame->blocks * 8;
+
+ default:
+ return -EIO;
+ }
+}
+
+static int sbc_analyze_audio(struct sbc_encoder_state *state,
+ struct sbc_frame *frame)
+{
+ int ch, blk;
+ int16_t *x;
+
+ switch (frame->subbands) {
+ case 4:
+ for (ch = 0; ch < frame->channels; ch++) {
+ x = &state->X[ch][state->position - 16 +
+ frame->blocks * 4];
+ for (blk = 0; blk < frame->blocks; blk += 4) {
+ state->sbc_analyze_4b_4s(
+ x,
+ frame->sb_sample_f[blk][ch],
+ frame->sb_sample_f[blk + 1][ch] -
+ frame->sb_sample_f[blk][ch]);
+ x -= 16;
+ }
+ }
+ return frame->blocks * 4;
+
+ case 8:
+ for (ch = 0; ch < frame->channels; ch++) {
+ x = &state->X[ch][state->position - 32 +
+ frame->blocks * 8];
+ for (blk = 0; blk < frame->blocks; blk += 4) {
+ state->sbc_analyze_4b_8s(
+ x,
+ frame->sb_sample_f[blk][ch],
+ frame->sb_sample_f[blk + 1][ch] -
+ frame->sb_sample_f[blk][ch]);
+ x -= 32;
+ }
+ }
+ return frame->blocks * 8;
+
+ default:
+ return -EIO;
+ }
+}
+
+/* Supplementary bitstream writing macros for 'sbc_pack_frame' */
+
+#define PUT_BITS(data_ptr, bits_cache, bits_count, v, n) \
+ do { \
+ bits_cache = (v) | (bits_cache << (n)); \
+ bits_count += (n); \
+ if (bits_count >= 16) { \
+ bits_count -= 8; \
+ *data_ptr++ = (uint8_t) \
+ (bits_cache >> bits_count); \
+ bits_count -= 8; \
+ *data_ptr++ = (uint8_t) \
+ (bits_cache >> bits_count); \
+ } \
+ } while (0)
+
+#define FLUSH_BITS(data_ptr, bits_cache, bits_count) \
+ do { \
+ while (bits_count >= 8) { \
+ bits_count -= 8; \
+ *data_ptr++ = (uint8_t) \
+ (bits_cache >> bits_count); \
+ } \
+ if (bits_count > 0) \
+ *data_ptr++ = (uint8_t) \
+ (bits_cache << (8 - bits_count)); \
+ } while (0)
+
+/*
+ * Packs the SBC frame from frame into the memory at data. At most len
+ * bytes will be used, should more memory be needed an appropriate
+ * error code will be returned. Returns the length of the packed frame
+ * on success or a negative value on error.
+ *
+ * The error codes are:
+ * -1 Not enough memory reserved
+ * -2 Unsupported sampling rate
+ * -3 Unsupported number of blocks
+ * -4 Unsupported number of subbands
+ * -5 Bitpool value out of bounds
+ * -99 not implemented
+ */
+
+static SBC_ALWAYS_INLINE ssize_t sbc_pack_frame_internal(uint8_t *data,
+ struct sbc_frame *frame, size_t len,
+ int frame_subbands, int frame_channels,
+ int joint)
+{
+ /* Bitstream writer starts from the fourth byte */
+ uint8_t *data_ptr = data + 4;
+ uint32_t bits_cache = 0;
+ uint32_t bits_count = 0;
+
+ /* Will copy the header parts for CRC-8 calculation here */
+ uint8_t crc_header[11] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ int crc_pos = 0;
+
+ uint32_t audio_sample;
+
+ int ch, sb, blk; /* channel, subband, block and bit counters */
+ int bits[2][8]; /* bits distribution */
+ uint32_t levels[2][8]; /* levels are derived from that */
+ uint32_t sb_sample_delta[2][8];
+
+ data[0] = SBC_SYNCWORD;
+
+ data[1] = (frame->frequency & 0x03) << 6;
+
+ data[1] |= (frame->block_mode & 0x03) << 4;
+
+ data[1] |= (frame->mode & 0x03) << 2;
+
+ data[1] |= (frame->allocation & 0x01) << 1;
+
+ switch (frame_subbands) {
+ case 4:
+ /* Nothing to do */
+ break;
+ case 8:
+ data[1] |= 0x01;
+ break;
+ default:
+ return -4;
+ break;
+ }
+
+ data[2] = frame->bitpool;
+
+ if ((frame->mode == MONO || frame->mode == DUAL_CHANNEL) &&
+ frame->bitpool > frame_subbands << 4)
+ return -5;
+
+ if ((frame->mode == STEREO || frame->mode == JOINT_STEREO) &&
+ frame->bitpool > frame_subbands << 5)
+ return -5;
+
+ /* Can't fill in crc yet */
+
+ crc_header[0] = data[1];
+ crc_header[1] = data[2];
+ crc_pos = 16;
+
+ if (frame->mode == JOINT_STEREO) {
+ PUT_BITS(data_ptr, bits_cache, bits_count,
+ joint, frame_subbands);
+ crc_header[crc_pos >> 3] = joint;
+ crc_pos += frame_subbands;
+ }
+
+ for (ch = 0; ch < frame_channels; ch++) {
+ for (sb = 0; sb < frame_subbands; sb++) {
+ PUT_BITS(data_ptr, bits_cache, bits_count,
+ frame->scale_factor[ch][sb] & 0x0F, 4);
+ crc_header[crc_pos >> 3] <<= 4;
+ crc_header[crc_pos >> 3] |= frame->scale_factor[ch][sb] & 0x0F;
+ crc_pos += 4;
+ }
+ }
+
+ /* align the last crc byte */
+ if (crc_pos % 8)
+ crc_header[crc_pos >> 3] <<= 8 - (crc_pos % 8);
+
+ data[3] = sbc_crc8(crc_header, crc_pos);
+
+ sbc_calculate_bits(frame, bits);
+
+ for (ch = 0; ch < frame_channels; ch++) {
+ for (sb = 0; sb < frame_subbands; sb++) {
+ levels[ch][sb] = ((1 << bits[ch][sb]) - 1) <<
+ (32 - (frame->scale_factor[ch][sb] +
+ SCALE_OUT_BITS + 2));
+ sb_sample_delta[ch][sb] = (uint32_t) 1 <<
+ (frame->scale_factor[ch][sb] +
+ SCALE_OUT_BITS + 1);
+ }
+ }
+
+ for (blk = 0; blk < frame->blocks; blk++) {
+ for (ch = 0; ch < frame_channels; ch++) {
+ for (sb = 0; sb < frame_subbands; sb++) {
+
+ if (bits[ch][sb] == 0)
+ continue;
+
+ audio_sample = ((uint64_t) levels[ch][sb] *
+ (sb_sample_delta[ch][sb] +
+ frame->sb_sample_f[blk][ch][sb])) >> 32;
+
+ PUT_BITS(data_ptr, bits_cache, bits_count,
+ audio_sample, bits[ch][sb]);
+ }
+ }
+ }
+
+ FLUSH_BITS(data_ptr, bits_cache, bits_count);
+
+ return data_ptr - data;
+}
+
+static ssize_t sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len,
+ int joint)
+{
+ if (frame->subbands == 4) {
+ if (frame->channels == 1)
+ return sbc_pack_frame_internal(
+ data, frame, len, 4, 1, joint);
+ else
+ return sbc_pack_frame_internal(
+ data, frame, len, 4, 2, joint);
+ } else {
+ if (frame->channels == 1)
+ return sbc_pack_frame_internal(
+ data, frame, len, 8, 1, joint);
+ else
+ return sbc_pack_frame_internal(
+ data, frame, len, 8, 2, joint);
+ }
+}
+
+static void sbc_encoder_init(struct sbc_encoder_state *state,
+ const struct sbc_frame *frame)
+{
+ memset(&state->X, 0, sizeof(state->X));
+ state->position = (SBC_X_BUFFER_SIZE - frame->subbands * 9) & ~7;
+
+ sbc_init_primitives(state);
+}
+
+struct sbc_priv {
+ int init;
+ struct SBC_ALIGNED sbc_frame frame;
+ struct SBC_ALIGNED sbc_decoder_state dec_state;
+ struct SBC_ALIGNED sbc_encoder_state enc_state;
+};
+
+static void sbc_set_defaults(sbc_t *sbc, unsigned long flags)
+{
+ sbc->frequency = SBC_FREQ_44100;
+ sbc->mode = SBC_MODE_STEREO;
+ sbc->subbands = SBC_SB_8;
+ sbc->blocks = SBC_BLK_16;
+ sbc->bitpool = 32;
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ sbc->endian = SBC_LE;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ sbc->endian = SBC_BE;
+#else
+#error "Unknown byte order"
+#endif
+}
+
+int sbc_init(sbc_t *sbc, unsigned long flags)
+{
+ if (!sbc)
+ return -EIO;
+
+ memset(sbc, 0, sizeof(sbc_t));
+
+ sbc->priv_alloc_base = malloc(sizeof(struct sbc_priv) + SBC_ALIGN_MASK);
+ if (!sbc->priv_alloc_base)
+ return -ENOMEM;
+
+ sbc->priv = (void *) (((uintptr_t) sbc->priv_alloc_base +
+ SBC_ALIGN_MASK) & ~((uintptr_t) SBC_ALIGN_MASK));
+
+ memset(sbc->priv, 0, sizeof(struct sbc_priv));
+
+ sbc_set_defaults(sbc, flags);
+
+ return 0;
+}
+
+ssize_t sbc_parse(sbc_t *sbc, const void *input, size_t input_len)
+{
+ return sbc_decode(sbc, input, input_len, NULL, 0, NULL);
+}
+
+ssize_t sbc_decode(sbc_t *sbc, const void *input, size_t input_len,
+ void *output, size_t output_len, size_t *written)
+{
+ struct sbc_priv *priv;
+ char *ptr;
+ int i, ch, framelen, samples;
+
+ if (!sbc || !input)
+ return -EIO;
+
+ priv = sbc->priv;
+
+ framelen = sbc_unpack_frame(input, &priv->frame, input_len);
+
+ if (!priv->init) {
+ sbc_decoder_init(&priv->dec_state, &priv->frame);
+ priv->init = 1;
+
+ sbc->frequency = priv->frame.frequency;
+ sbc->mode = priv->frame.mode;
+ sbc->subbands = priv->frame.subband_mode;
+ sbc->blocks = priv->frame.block_mode;
+ sbc->allocation = priv->frame.allocation;
+ sbc->bitpool = priv->frame.bitpool;
+
+ priv->frame.codesize = sbc_get_codesize(sbc);
+ priv->frame.length = framelen;
+ } else if (priv->frame.bitpool != sbc->bitpool) {
+ priv->frame.length = framelen;
+ sbc->bitpool = priv->frame.bitpool;
+ }
+
+ if (!output)
+ return framelen;
+
+ if (written)
+ *written = 0;
+
+ if (framelen <= 0)
+ return framelen;
+
+ samples = sbc_synthesize_audio(&priv->dec_state, &priv->frame);
+
+ ptr = output;
+
+ if (output_len < (size_t) (samples * priv->frame.channels * 2))
+ samples = output_len / (priv->frame.channels * 2);
+
+ for (i = 0; i < samples; i++) {
+ for (ch = 0; ch < priv->frame.channels; ch++) {
+ int16_t s;
+ s = priv->frame.pcm_sample[ch][i];
+
+ if (sbc->endian == SBC_BE) {
+ *ptr++ = (s & 0xff00) >> 8;
+ *ptr++ = (s & 0x00ff);
+ } else {
+ *ptr++ = (s & 0x00ff);
+ *ptr++ = (s & 0xff00) >> 8;
+ }
+ }
+ }
+
+ if (written)
+ *written = samples * priv->frame.channels * 2;
+
+ return framelen;
+}
+
+ssize_t sbc_encode(sbc_t *sbc, const void *input, size_t input_len,
+ void *output, size_t output_len, ssize_t *written)
+{
+ struct sbc_priv *priv;
+ int samples;
+ ssize_t framelen;
+ int (*sbc_enc_process_input)(int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels);
+
+ if (!sbc || !input)
+ return -EIO;
+
+ priv = sbc->priv;
+
+ if (written)
+ *written = 0;
+
+ if (!priv->init) {
+ priv->frame.frequency = sbc->frequency;
+ priv->frame.mode = sbc->mode;
+ priv->frame.channels = sbc->mode == SBC_MODE_MONO ? 1 : 2;
+ priv->frame.allocation = sbc->allocation;
+ priv->frame.subband_mode = sbc->subbands;
+ priv->frame.subbands = sbc->subbands ? 8 : 4;
+ priv->frame.block_mode = sbc->blocks;
+ priv->frame.blocks = 4 + (sbc->blocks * 4);
+ priv->frame.bitpool = sbc->bitpool;
+ priv->frame.codesize = sbc_get_codesize(sbc);
+ priv->frame.length = sbc_get_frame_length(sbc);
+
+ sbc_encoder_init(&priv->enc_state, &priv->frame);
+ priv->init = 1;
+ } else if (priv->frame.bitpool != sbc->bitpool) {
+ priv->frame.length = sbc_get_frame_length(sbc);
+ priv->frame.bitpool = sbc->bitpool;
+ }
+
+ /* input must be large enough to encode a complete frame */
+ if (input_len < priv->frame.codesize)
+ return 0;
+
+ /* output must be large enough to receive the encoded frame */
+ if (!output || output_len < priv->frame.length)
+ return -ENOSPC;
+
+ /* Select the needed input data processing function and call it */
+ if (priv->frame.subbands == 8) {
+ if (sbc->endian == SBC_BE)
+ sbc_enc_process_input =
+ priv->enc_state.sbc_enc_process_input_8s_be;
+ else
+ sbc_enc_process_input =
+ priv->enc_state.sbc_enc_process_input_8s_le;
+ } else {
+ if (sbc->endian == SBC_BE)
+ sbc_enc_process_input =
+ priv->enc_state.sbc_enc_process_input_4s_be;
+ else
+ sbc_enc_process_input =
+ priv->enc_state.sbc_enc_process_input_4s_le;
+ }
+
+ priv->enc_state.position = sbc_enc_process_input(
+ priv->enc_state.position, (const uint8_t *) input,
+ priv->enc_state.X, priv->frame.subbands * priv->frame.blocks,
+ priv->frame.channels);
+
+ samples = sbc_analyze_audio(&priv->enc_state, &priv->frame);
+
+ if (priv->frame.mode == JOINT_STEREO) {
+ int j = priv->enc_state.sbc_calc_scalefactors_j(
+ priv->frame.sb_sample_f, priv->frame.scale_factor,
+ priv->frame.blocks, priv->frame.subbands);
+ framelen = sbc_pack_frame(output, &priv->frame, output_len, j);
+ } else {
+ priv->enc_state.sbc_calc_scalefactors(
+ priv->frame.sb_sample_f, priv->frame.scale_factor,
+ priv->frame.blocks, priv->frame.channels,
+ priv->frame.subbands);
+ framelen = sbc_pack_frame(output, &priv->frame, output_len, 0);
+ }
+
+ if (written)
+ *written = framelen;
+
+ return samples * priv->frame.channels * 2;
+}
+
+void sbc_finish(sbc_t *sbc)
+{
+ if (!sbc)
+ return;
+
+ free(sbc->priv_alloc_base);
+
+ memset(sbc, 0, sizeof(sbc_t));
+}
+
+size_t sbc_get_frame_length(sbc_t *sbc)
+{
+ int ret;
+ uint8_t subbands, channels, blocks, joint, bitpool;
+ struct sbc_priv *priv;
+
+ priv = sbc->priv;
+ if (priv->init && priv->frame.bitpool == sbc->bitpool)
+ return priv->frame.length;
+
+ subbands = sbc->subbands ? 8 : 4;
+ blocks = 4 + (sbc->blocks * 4);
+ channels = sbc->mode == SBC_MODE_MONO ? 1 : 2;
+ joint = sbc->mode == SBC_MODE_JOINT_STEREO ? 1 : 0;
+ bitpool = sbc->bitpool;
+
+ ret = 4 + (4 * subbands * channels) / 8;
+ /* This term is not always evenly divide so we round it up */
+ if (channels == 1)
+ ret += ((blocks * channels * bitpool) + 7) / 8;
+ else
+ ret += (((joint ? subbands : 0) + blocks * bitpool) + 7) / 8;
+
+ return ret;
+}
+
+unsigned sbc_get_frame_duration(sbc_t *sbc)
+{
+ uint8_t subbands, blocks;
+ uint16_t frequency;
+ struct sbc_priv *priv;
+
+ priv = sbc->priv;
+ if (!priv->init) {
+ subbands = sbc->subbands ? 8 : 4;
+ blocks = 4 + (sbc->blocks * 4);
+ } else {
+ subbands = priv->frame.subbands;
+ blocks = priv->frame.blocks;
+ }
+
+ switch (sbc->frequency) {
+ case SBC_FREQ_16000:
+ frequency = 16000;
+ break;
+
+ case SBC_FREQ_32000:
+ frequency = 32000;
+ break;
+
+ case SBC_FREQ_44100:
+ frequency = 44100;
+ break;
+
+ case SBC_FREQ_48000:
+ frequency = 48000;
+ break;
+ default:
+ return 0;
+ }
+
+ return (1000000 * blocks * subbands) / frequency;
+}
+
+size_t sbc_get_codesize(sbc_t *sbc)
+{
+ uint16_t subbands, channels, blocks;
+ struct sbc_priv *priv;
+
+ priv = sbc->priv;
+ if (!priv->init) {
+ subbands = sbc->subbands ? 8 : 4;
+ blocks = 4 + (sbc->blocks * 4);
+ channels = sbc->mode == SBC_MODE_MONO ? 1 : 2;
+ } else {
+ subbands = priv->frame.subbands;
+ blocks = priv->frame.blocks;
+ channels = priv->frame.channels;
+ }
+
+ return subbands * blocks * channels * 2;
+}
+
+const char *sbc_get_implementation_info(sbc_t *sbc)
+{
+ struct sbc_priv *priv;
+
+ if (!sbc)
+ return NULL;
+
+ priv = sbc->priv;
+ if (!priv)
+ return NULL;
+
+ return priv->enc_state.implementation_info;
+}
+
+int sbc_reinit(sbc_t *sbc, unsigned long flags)
+{
+ struct sbc_priv *priv;
+
+ if (!sbc || !sbc->priv)
+ return -EIO;
+
+ priv = sbc->priv;
+
+ if (priv->init == 1)
+ memset(sbc->priv, 0, sizeof(struct sbc_priv));
+
+ sbc_set_defaults(sbc, flags);
+
+ return 0;
+}
diff --git a/sbc/sbc.h b/sbc/sbc.h
new file mode 100644
index 0000000..2f830ad
--- /dev/null
+++ b/sbc/sbc.h
@@ -0,0 +1,113 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2008-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2005 Henryk Ploetz <henryk@ploetzli.ch>
+ * Copyright (C) 2005-2006 Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef __SBC_H
+#define __SBC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <sys/types.h>
+
+/* sampling frequency */
+#define SBC_FREQ_16000 0x00
+#define SBC_FREQ_32000 0x01
+#define SBC_FREQ_44100 0x02
+#define SBC_FREQ_48000 0x03
+
+/* blocks */
+#define SBC_BLK_4 0x00
+#define SBC_BLK_8 0x01
+#define SBC_BLK_12 0x02
+#define SBC_BLK_16 0x03
+
+/* channel mode */
+#define SBC_MODE_MONO 0x00
+#define SBC_MODE_DUAL_CHANNEL 0x01
+#define SBC_MODE_STEREO 0x02
+#define SBC_MODE_JOINT_STEREO 0x03
+
+/* allocation method */
+#define SBC_AM_LOUDNESS 0x00
+#define SBC_AM_SNR 0x01
+
+/* subbands */
+#define SBC_SB_4 0x00
+#define SBC_SB_8 0x01
+
+/* Data endianess */
+#define SBC_LE 0x00
+#define SBC_BE 0x01
+
+struct sbc_struct {
+ unsigned long flags;
+
+ uint8_t frequency;
+ uint8_t blocks;
+ uint8_t subbands;
+ uint8_t mode;
+ uint8_t allocation;
+ uint8_t bitpool;
+ uint8_t endian;
+
+ void *priv;
+ void *priv_alloc_base;
+};
+
+typedef struct sbc_struct sbc_t;
+
+int sbc_init(sbc_t *sbc, unsigned long flags);
+int sbc_reinit(sbc_t *sbc, unsigned long flags);
+
+ssize_t sbc_parse(sbc_t *sbc, const void *input, size_t input_len);
+
+/* Decodes ONE input block into ONE output block */
+ssize_t sbc_decode(sbc_t *sbc, const void *input, size_t input_len,
+ void *output, size_t output_len, size_t *written);
+
+/* Encodes ONE input block into ONE output block */
+ssize_t sbc_encode(sbc_t *sbc, const void *input, size_t input_len,
+ void *output, size_t output_len, ssize_t *written);
+
+/* Returns the output block size in bytes */
+size_t sbc_get_frame_length(sbc_t *sbc);
+
+/* Returns the time one input/output block takes to play in msec*/
+unsigned sbc_get_frame_duration(sbc_t *sbc);
+
+/* Returns the input block size in bytes */
+size_t sbc_get_codesize(sbc_t *sbc);
+
+const char *sbc_get_implementation_info(sbc_t *sbc);
+void sbc_finish(sbc_t *sbc);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SBC_H */
diff --git a/sbc/sbc_math.h b/sbc/sbc_math.h
new file mode 100644
index 0000000..5476860
--- /dev/null
+++ b/sbc/sbc_math.h
@@ -0,0 +1,61 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2008-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2005 Henryk Ploetz <henryk@ploetzli.ch>
+ * Copyright (C) 2005-2008 Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#define fabs(x) ((x) < 0 ? -(x) : (x))
+/* C does not provide an explicit arithmetic shift right but this will
+ always be correct and every compiler *should* generate optimal code */
+#define ASR(val, bits) ((-2 >> 1 == -1) ? \
+ ((int32_t)(val)) >> (bits) : ((int32_t) (val)) / (1 << (bits)))
+
+#define SCALE_SPROTO4_TBL 12
+#define SCALE_SPROTO8_TBL 14
+#define SCALE_NPROTO4_TBL 11
+#define SCALE_NPROTO8_TBL 11
+#define SCALE4_STAGED1_BITS 15
+#define SCALE4_STAGED2_BITS 16
+#define SCALE8_STAGED1_BITS 15
+#define SCALE8_STAGED2_BITS 16
+
+typedef int32_t sbc_fixed_t;
+
+#define SCALE4_STAGED1(src) ASR(src, SCALE4_STAGED1_BITS)
+#define SCALE4_STAGED2(src) ASR(src, SCALE4_STAGED2_BITS)
+#define SCALE8_STAGED1(src) ASR(src, SCALE8_STAGED1_BITS)
+#define SCALE8_STAGED2(src) ASR(src, SCALE8_STAGED2_BITS)
+
+#define SBC_FIXED_0(val) { val = 0; }
+#define MUL(a, b) ((a) * (b))
+#if defined(__arm__) && (!defined(__thumb__) || defined(__thumb2__))
+#define MULA(a, b, res) ({ \
+ int tmp = res; \
+ __asm__( \
+ "mla %0, %2, %3, %0" \
+ : "=&r" (tmp) \
+ : "0" (tmp), "r" (a), "r" (b)); \
+ tmp; })
+#else
+#define MULA(a, b, res) ((a) * (b) + (res))
+#endif
diff --git a/sbc/sbc_primitives.c b/sbc/sbc_primitives.c
new file mode 100644
index 0000000..ad780d0
--- /dev/null
+++ b/sbc/sbc_primitives.c
@@ -0,0 +1,554 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2008-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2005 Henryk Ploetz <henryk@ploetzli.ch>
+ * Copyright (C) 2005-2006 Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdint.h>
+#include <limits.h>
+#include <string.h>
+#include "sbc.h"
+#include "sbc_math.h"
+#include "sbc_tables.h"
+
+#include "sbc_primitives.h"
+#include "sbc_primitives_mmx.h"
+#include "sbc_primitives_iwmmxt.h"
+#include "sbc_primitives_neon.h"
+#include "sbc_primitives_armv6.h"
+
+/*
+ * A reference C code of analysis filter with SIMD-friendly tables
+ * reordering and code layout. This code can be used to develop platform
+ * specific SIMD optimizations. Also it may be used as some kind of test
+ * for compiler autovectorization capabilities (who knows, if the compiler
+ * is very good at this stuff, hand optimized assembly may be not strictly
+ * needed for some platform).
+ *
+ * Note: It is also possible to make a simple variant of analysis filter,
+ * which needs only a single constants table without taking care about
+ * even/odd cases. This simple variant of filter can be implemented without
+ * input data permutation. The only thing that would be lost is the
+ * possibility to use pairwise SIMD multiplications. But for some simple
+ * CPU cores without SIMD extensions it can be useful. If anybody is
+ * interested in implementing such variant of a filter, sourcecode from
+ * bluez versions 4.26/4.27 can be used as a reference and the history of
+ * the changes in git repository done around that time may be worth checking.
+ */
+
+static inline void sbc_analyze_four_simd(const int16_t *in, int32_t *out,
+ const FIXED_T *consts)
+{
+ FIXED_A t1[4];
+ FIXED_T t2[4];
+ int hop = 0;
+
+ /* rounding coefficient */
+ t1[0] = t1[1] = t1[2] = t1[3] =
+ (FIXED_A) 1 << (SBC_PROTO_FIXED4_SCALE - 1);
+
+ /* low pass polyphase filter */
+ for (hop = 0; hop < 40; hop += 8) {
+ t1[0] += (FIXED_A) in[hop] * consts[hop];
+ t1[0] += (FIXED_A) in[hop + 1] * consts[hop + 1];
+ t1[1] += (FIXED_A) in[hop + 2] * consts[hop + 2];
+ t1[1] += (FIXED_A) in[hop + 3] * consts[hop + 3];
+ t1[2] += (FIXED_A) in[hop + 4] * consts[hop + 4];
+ t1[2] += (FIXED_A) in[hop + 5] * consts[hop + 5];
+ t1[3] += (FIXED_A) in[hop + 6] * consts[hop + 6];
+ t1[3] += (FIXED_A) in[hop + 7] * consts[hop + 7];
+ }
+
+ /* scaling */
+ t2[0] = t1[0] >> SBC_PROTO_FIXED4_SCALE;
+ t2[1] = t1[1] >> SBC_PROTO_FIXED4_SCALE;
+ t2[2] = t1[2] >> SBC_PROTO_FIXED4_SCALE;
+ t2[3] = t1[3] >> SBC_PROTO_FIXED4_SCALE;
+
+ /* do the cos transform */
+ t1[0] = (FIXED_A) t2[0] * consts[40 + 0];
+ t1[0] += (FIXED_A) t2[1] * consts[40 + 1];
+ t1[1] = (FIXED_A) t2[0] * consts[40 + 2];
+ t1[1] += (FIXED_A) t2[1] * consts[40 + 3];
+ t1[2] = (FIXED_A) t2[0] * consts[40 + 4];
+ t1[2] += (FIXED_A) t2[1] * consts[40 + 5];
+ t1[3] = (FIXED_A) t2[0] * consts[40 + 6];
+ t1[3] += (FIXED_A) t2[1] * consts[40 + 7];
+
+ t1[0] += (FIXED_A) t2[2] * consts[40 + 8];
+ t1[0] += (FIXED_A) t2[3] * consts[40 + 9];
+ t1[1] += (FIXED_A) t2[2] * consts[40 + 10];
+ t1[1] += (FIXED_A) t2[3] * consts[40 + 11];
+ t1[2] += (FIXED_A) t2[2] * consts[40 + 12];
+ t1[2] += (FIXED_A) t2[3] * consts[40 + 13];
+ t1[3] += (FIXED_A) t2[2] * consts[40 + 14];
+ t1[3] += (FIXED_A) t2[3] * consts[40 + 15];
+
+ out[0] = t1[0] >>
+ (SBC_COS_TABLE_FIXED4_SCALE - SCALE_OUT_BITS);
+ out[1] = t1[1] >>
+ (SBC_COS_TABLE_FIXED4_SCALE - SCALE_OUT_BITS);
+ out[2] = t1[2] >>
+ (SBC_COS_TABLE_FIXED4_SCALE - SCALE_OUT_BITS);
+ out[3] = t1[3] >>
+ (SBC_COS_TABLE_FIXED4_SCALE - SCALE_OUT_BITS);
+}
+
+static inline void sbc_analyze_eight_simd(const int16_t *in, int32_t *out,
+ const FIXED_T *consts)
+{
+ FIXED_A t1[8];
+ FIXED_T t2[8];
+ int i, hop;
+
+ /* rounding coefficient */
+ t1[0] = t1[1] = t1[2] = t1[3] = t1[4] = t1[5] = t1[6] = t1[7] =
+ (FIXED_A) 1 << (SBC_PROTO_FIXED8_SCALE-1);
+
+ /* low pass polyphase filter */
+ for (hop = 0; hop < 80; hop += 16) {
+ t1[0] += (FIXED_A) in[hop] * consts[hop];
+ t1[0] += (FIXED_A) in[hop + 1] * consts[hop + 1];
+ t1[1] += (FIXED_A) in[hop + 2] * consts[hop + 2];
+ t1[1] += (FIXED_A) in[hop + 3] * consts[hop + 3];
+ t1[2] += (FIXED_A) in[hop + 4] * consts[hop + 4];
+ t1[2] += (FIXED_A) in[hop + 5] * consts[hop + 5];
+ t1[3] += (FIXED_A) in[hop + 6] * consts[hop + 6];
+ t1[3] += (FIXED_A) in[hop + 7] * consts[hop + 7];
+ t1[4] += (FIXED_A) in[hop + 8] * consts[hop + 8];
+ t1[4] += (FIXED_A) in[hop + 9] * consts[hop + 9];
+ t1[5] += (FIXED_A) in[hop + 10] * consts[hop + 10];
+ t1[5] += (FIXED_A) in[hop + 11] * consts[hop + 11];
+ t1[6] += (FIXED_A) in[hop + 12] * consts[hop + 12];
+ t1[6] += (FIXED_A) in[hop + 13] * consts[hop + 13];
+ t1[7] += (FIXED_A) in[hop + 14] * consts[hop + 14];
+ t1[7] += (FIXED_A) in[hop + 15] * consts[hop + 15];
+ }
+
+ /* scaling */
+ t2[0] = t1[0] >> SBC_PROTO_FIXED8_SCALE;
+ t2[1] = t1[1] >> SBC_PROTO_FIXED8_SCALE;
+ t2[2] = t1[2] >> SBC_PROTO_FIXED8_SCALE;
+ t2[3] = t1[3] >> SBC_PROTO_FIXED8_SCALE;
+ t2[4] = t1[4] >> SBC_PROTO_FIXED8_SCALE;
+ t2[5] = t1[5] >> SBC_PROTO_FIXED8_SCALE;
+ t2[6] = t1[6] >> SBC_PROTO_FIXED8_SCALE;
+ t2[7] = t1[7] >> SBC_PROTO_FIXED8_SCALE;
+
+
+ /* do the cos transform */
+ t1[0] = t1[1] = t1[2] = t1[3] = t1[4] = t1[5] = t1[6] = t1[7] = 0;
+
+ for (i = 0; i < 4; i++) {
+ t1[0] += (FIXED_A) t2[i * 2 + 0] * consts[80 + i * 16 + 0];
+ t1[0] += (FIXED_A) t2[i * 2 + 1] * consts[80 + i * 16 + 1];
+ t1[1] += (FIXED_A) t2[i * 2 + 0] * consts[80 + i * 16 + 2];
+ t1[1] += (FIXED_A) t2[i * 2 + 1] * consts[80 + i * 16 + 3];
+ t1[2] += (FIXED_A) t2[i * 2 + 0] * consts[80 + i * 16 + 4];
+ t1[2] += (FIXED_A) t2[i * 2 + 1] * consts[80 + i * 16 + 5];
+ t1[3] += (FIXED_A) t2[i * 2 + 0] * consts[80 + i * 16 + 6];
+ t1[3] += (FIXED_A) t2[i * 2 + 1] * consts[80 + i * 16 + 7];
+ t1[4] += (FIXED_A) t2[i * 2 + 0] * consts[80 + i * 16 + 8];
+ t1[4] += (FIXED_A) t2[i * 2 + 1] * consts[80 + i * 16 + 9];
+ t1[5] += (FIXED_A) t2[i * 2 + 0] * consts[80 + i * 16 + 10];
+ t1[5] += (FIXED_A) t2[i * 2 + 1] * consts[80 + i * 16 + 11];
+ t1[6] += (FIXED_A) t2[i * 2 + 0] * consts[80 + i * 16 + 12];
+ t1[6] += (FIXED_A) t2[i * 2 + 1] * consts[80 + i * 16 + 13];
+ t1[7] += (FIXED_A) t2[i * 2 + 0] * consts[80 + i * 16 + 14];
+ t1[7] += (FIXED_A) t2[i * 2 + 1] * consts[80 + i * 16 + 15];
+ }
+
+ for (i = 0; i < 8; i++)
+ out[i] = t1[i] >>
+ (SBC_COS_TABLE_FIXED8_SCALE - SCALE_OUT_BITS);
+}
+
+static inline void sbc_analyze_4b_4s_simd(int16_t *x,
+ int32_t *out, int out_stride)
+{
+ /* Analyze blocks */
+ sbc_analyze_four_simd(x + 12, out, analysis_consts_fixed4_simd_odd);
+ out += out_stride;
+ sbc_analyze_four_simd(x + 8, out, analysis_consts_fixed4_simd_even);
+ out += out_stride;
+ sbc_analyze_four_simd(x + 4, out, analysis_consts_fixed4_simd_odd);
+ out += out_stride;
+ sbc_analyze_four_simd(x + 0, out, analysis_consts_fixed4_simd_even);
+}
+
+static inline void sbc_analyze_4b_8s_simd(int16_t *x,
+ int32_t *out, int out_stride)
+{
+ /* Analyze blocks */
+ sbc_analyze_eight_simd(x + 24, out, analysis_consts_fixed8_simd_odd);
+ out += out_stride;
+ sbc_analyze_eight_simd(x + 16, out, analysis_consts_fixed8_simd_even);
+ out += out_stride;
+ sbc_analyze_eight_simd(x + 8, out, analysis_consts_fixed8_simd_odd);
+ out += out_stride;
+ sbc_analyze_eight_simd(x + 0, out, analysis_consts_fixed8_simd_even);
+}
+
+static inline int16_t unaligned16_be(const uint8_t *ptr)
+{
+ return (int16_t) ((ptr[0] << 8) | ptr[1]);
+}
+
+static inline int16_t unaligned16_le(const uint8_t *ptr)
+{
+ return (int16_t) (ptr[0] | (ptr[1] << 8));
+}
+
+/*
+ * Internal helper functions for input data processing. In order to get
+ * optimal performance, it is important to have "nsamples", "nchannels"
+ * and "big_endian" arguments used with this inline function as compile
+ * time constants.
+ */
+
+static SBC_ALWAYS_INLINE int sbc_encoder_process_input_s4_internal(
+ int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels, int big_endian)
+{
+ /* handle X buffer wraparound */
+ if (position < nsamples) {
+ if (nchannels > 0)
+ memcpy(&X[0][SBC_X_BUFFER_SIZE - 40], &X[0][position],
+ 36 * sizeof(int16_t));
+ if (nchannels > 1)
+ memcpy(&X[1][SBC_X_BUFFER_SIZE - 40], &X[1][position],
+ 36 * sizeof(int16_t));
+ position = SBC_X_BUFFER_SIZE - 40;
+ }
+
+ #define PCM(i) (big_endian ? \
+ unaligned16_be(pcm + (i) * 2) : unaligned16_le(pcm + (i) * 2))
+
+ /* copy/permutate audio samples */
+ while ((nsamples -= 8) >= 0) {
+ position -= 8;
+ if (nchannels > 0) {
+ int16_t *x = &X[0][position];
+ x[0] = PCM(0 + 7 * nchannels);
+ x[1] = PCM(0 + 3 * nchannels);
+ x[2] = PCM(0 + 6 * nchannels);
+ x[3] = PCM(0 + 4 * nchannels);
+ x[4] = PCM(0 + 0 * nchannels);
+ x[5] = PCM(0 + 2 * nchannels);
+ x[6] = PCM(0 + 1 * nchannels);
+ x[7] = PCM(0 + 5 * nchannels);
+ }
+ if (nchannels > 1) {
+ int16_t *x = &X[1][position];
+ x[0] = PCM(1 + 7 * nchannels);
+ x[1] = PCM(1 + 3 * nchannels);
+ x[2] = PCM(1 + 6 * nchannels);
+ x[3] = PCM(1 + 4 * nchannels);
+ x[4] = PCM(1 + 0 * nchannels);
+ x[5] = PCM(1 + 2 * nchannels);
+ x[6] = PCM(1 + 1 * nchannels);
+ x[7] = PCM(1 + 5 * nchannels);
+ }
+ pcm += 16 * nchannels;
+ }
+ #undef PCM
+
+ return position;
+}
+
+static SBC_ALWAYS_INLINE int sbc_encoder_process_input_s8_internal(
+ int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels, int big_endian)
+{
+ /* handle X buffer wraparound */
+ if (position < nsamples) {
+ if (nchannels > 0)
+ memcpy(&X[0][SBC_X_BUFFER_SIZE - 72], &X[0][position],
+ 72 * sizeof(int16_t));
+ if (nchannels > 1)
+ memcpy(&X[1][SBC_X_BUFFER_SIZE - 72], &X[1][position],
+ 72 * sizeof(int16_t));
+ position = SBC_X_BUFFER_SIZE - 72;
+ }
+
+ #define PCM(i) (big_endian ? \
+ unaligned16_be(pcm + (i) * 2) : unaligned16_le(pcm + (i) * 2))
+
+ /* copy/permutate audio samples */
+ while ((nsamples -= 16) >= 0) {
+ position -= 16;
+ if (nchannels > 0) {
+ int16_t *x = &X[0][position];
+ x[0] = PCM(0 + 15 * nchannels);
+ x[1] = PCM(0 + 7 * nchannels);
+ x[2] = PCM(0 + 14 * nchannels);
+ x[3] = PCM(0 + 8 * nchannels);
+ x[4] = PCM(0 + 13 * nchannels);
+ x[5] = PCM(0 + 9 * nchannels);
+ x[6] = PCM(0 + 12 * nchannels);
+ x[7] = PCM(0 + 10 * nchannels);
+ x[8] = PCM(0 + 11 * nchannels);
+ x[9] = PCM(0 + 3 * nchannels);
+ x[10] = PCM(0 + 6 * nchannels);
+ x[11] = PCM(0 + 0 * nchannels);
+ x[12] = PCM(0 + 5 * nchannels);
+ x[13] = PCM(0 + 1 * nchannels);
+ x[14] = PCM(0 + 4 * nchannels);
+ x[15] = PCM(0 + 2 * nchannels);
+ }
+ if (nchannels > 1) {
+ int16_t *x = &X[1][position];
+ x[0] = PCM(1 + 15 * nchannels);
+ x[1] = PCM(1 + 7 * nchannels);
+ x[2] = PCM(1 + 14 * nchannels);
+ x[3] = PCM(1 + 8 * nchannels);
+ x[4] = PCM(1 + 13 * nchannels);
+ x[5] = PCM(1 + 9 * nchannels);
+ x[6] = PCM(1 + 12 * nchannels);
+ x[7] = PCM(1 + 10 * nchannels);
+ x[8] = PCM(1 + 11 * nchannels);
+ x[9] = PCM(1 + 3 * nchannels);
+ x[10] = PCM(1 + 6 * nchannels);
+ x[11] = PCM(1 + 0 * nchannels);
+ x[12] = PCM(1 + 5 * nchannels);
+ x[13] = PCM(1 + 1 * nchannels);
+ x[14] = PCM(1 + 4 * nchannels);
+ x[15] = PCM(1 + 2 * nchannels);
+ }
+ pcm += 32 * nchannels;
+ }
+ #undef PCM
+
+ return position;
+}
+
+/*
+ * Input data processing functions. The data is endian converted if needed,
+ * channels are deintrleaved and audio samples are reordered for use in
+ * SIMD-friendly analysis filter function. The results are put into "X"
+ * array, getting appended to the previous data (or it is better to say
+ * prepended, as the buffer is filled from top to bottom). Old data is
+ * discarded when neededed, but availability of (10 * nrof_subbands)
+ * contiguous samples is always guaranteed for the input to the analysis
+ * filter. This is achieved by copying a sufficient part of old data
+ * to the top of the buffer on buffer wraparound.
+ */
+
+static int sbc_enc_process_input_4s_le(int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels)
+{
+ if (nchannels > 1)
+ return sbc_encoder_process_input_s4_internal(
+ position, pcm, X, nsamples, 2, 0);
+ else
+ return sbc_encoder_process_input_s4_internal(
+ position, pcm, X, nsamples, 1, 0);
+}
+
+static int sbc_enc_process_input_4s_be(int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels)
+{
+ if (nchannels > 1)
+ return sbc_encoder_process_input_s4_internal(
+ position, pcm, X, nsamples, 2, 1);
+ else
+ return sbc_encoder_process_input_s4_internal(
+ position, pcm, X, nsamples, 1, 1);
+}
+
+static int sbc_enc_process_input_8s_le(int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels)
+{
+ if (nchannels > 1)
+ return sbc_encoder_process_input_s8_internal(
+ position, pcm, X, nsamples, 2, 0);
+ else
+ return sbc_encoder_process_input_s8_internal(
+ position, pcm, X, nsamples, 1, 0);
+}
+
+static int sbc_enc_process_input_8s_be(int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels)
+{
+ if (nchannels > 1)
+ return sbc_encoder_process_input_s8_internal(
+ position, pcm, X, nsamples, 2, 1);
+ else
+ return sbc_encoder_process_input_s8_internal(
+ position, pcm, X, nsamples, 1, 1);
+}
+
+/* Supplementary function to count the number of leading zeros */
+
+static inline int sbc_clz(uint32_t x)
+{
+#ifdef __GNUC__
+ return __builtin_clz(x);
+#else
+ /* TODO: this should be replaced with something better if good
+ * performance is wanted when using compilers other than gcc */
+ int cnt = 0;
+ while (x) {
+ cnt++;
+ x >>= 1;
+ }
+ return 32 - cnt;
+#endif
+}
+
+static void sbc_calc_scalefactors(
+ int32_t sb_sample_f[16][2][8],
+ uint32_t scale_factor[2][8],
+ int blocks, int channels, int subbands)
+{
+ int ch, sb, blk;
+ for (ch = 0; ch < channels; ch++) {
+ for (sb = 0; sb < subbands; sb++) {
+ uint32_t x = 1 << SCALE_OUT_BITS;
+ for (blk = 0; blk < blocks; blk++) {
+ int32_t tmp = fabs(sb_sample_f[blk][ch][sb]);
+ if (tmp != 0)
+ x |= tmp - 1;
+ }
+ scale_factor[ch][sb] = (31 - SCALE_OUT_BITS) -
+ sbc_clz(x);
+ }
+ }
+}
+
+static int sbc_calc_scalefactors_j(
+ int32_t sb_sample_f[16][2][8],
+ uint32_t scale_factor[2][8],
+ int blocks, int subbands)
+{
+ int blk, joint = 0;
+ int32_t tmp0, tmp1;
+ uint32_t x, y;
+
+ /* last subband does not use joint stereo */
+ int sb = subbands - 1;
+ x = 1 << SCALE_OUT_BITS;
+ y = 1 << SCALE_OUT_BITS;
+ for (blk = 0; blk < blocks; blk++) {
+ tmp0 = fabs(sb_sample_f[blk][0][sb]);
+ tmp1 = fabs(sb_sample_f[blk][1][sb]);
+ if (tmp0 != 0)
+ x |= tmp0 - 1;
+ if (tmp1 != 0)
+ y |= tmp1 - 1;
+ }
+ scale_factor[0][sb] = (31 - SCALE_OUT_BITS) - sbc_clz(x);
+ scale_factor[1][sb] = (31 - SCALE_OUT_BITS) - sbc_clz(y);
+
+ /* the rest of subbands can use joint stereo */
+ while (--sb >= 0) {
+ int32_t sb_sample_j[16][2];
+ x = 1 << SCALE_OUT_BITS;
+ y = 1 << SCALE_OUT_BITS;
+ for (blk = 0; blk < blocks; blk++) {
+ tmp0 = sb_sample_f[blk][0][sb];
+ tmp1 = sb_sample_f[blk][1][sb];
+ sb_sample_j[blk][0] = ASR(tmp0, 1) + ASR(tmp1, 1);
+ sb_sample_j[blk][1] = ASR(tmp0, 1) - ASR(tmp1, 1);
+ tmp0 = fabs(tmp0);
+ tmp1 = fabs(tmp1);
+ if (tmp0 != 0)
+ x |= tmp0 - 1;
+ if (tmp1 != 0)
+ y |= tmp1 - 1;
+ }
+ scale_factor[0][sb] = (31 - SCALE_OUT_BITS) -
+ sbc_clz(x);
+ scale_factor[1][sb] = (31 - SCALE_OUT_BITS) -
+ sbc_clz(y);
+ x = 1 << SCALE_OUT_BITS;
+ y = 1 << SCALE_OUT_BITS;
+ for (blk = 0; blk < blocks; blk++) {
+ tmp0 = fabs(sb_sample_j[blk][0]);
+ tmp1 = fabs(sb_sample_j[blk][1]);
+ if (tmp0 != 0)
+ x |= tmp0 - 1;
+ if (tmp1 != 0)
+ y |= tmp1 - 1;
+ }
+ x = (31 - SCALE_OUT_BITS) - sbc_clz(x);
+ y = (31 - SCALE_OUT_BITS) - sbc_clz(y);
+
+ /* decide whether to use joint stereo for this subband */
+ if ((scale_factor[0][sb] + scale_factor[1][sb]) > x + y) {
+ joint |= 1 << (subbands - 1 - sb);
+ scale_factor[0][sb] = x;
+ scale_factor[1][sb] = y;
+ for (blk = 0; blk < blocks; blk++) {
+ sb_sample_f[blk][0][sb] = sb_sample_j[blk][0];
+ sb_sample_f[blk][1][sb] = sb_sample_j[blk][1];
+ }
+ }
+ }
+
+ /* bitmask with the information about subbands using joint stereo */
+ return joint;
+}
+
+/*
+ * Detect CPU features and setup function pointers
+ */
+void sbc_init_primitives(struct sbc_encoder_state *state)
+{
+ /* Default implementation for analyze functions */
+ state->sbc_analyze_4b_4s = sbc_analyze_4b_4s_simd;
+ state->sbc_analyze_4b_8s = sbc_analyze_4b_8s_simd;
+
+ /* Default implementation for input reordering / deinterleaving */
+ state->sbc_enc_process_input_4s_le = sbc_enc_process_input_4s_le;
+ state->sbc_enc_process_input_4s_be = sbc_enc_process_input_4s_be;
+ state->sbc_enc_process_input_8s_le = sbc_enc_process_input_8s_le;
+ state->sbc_enc_process_input_8s_be = sbc_enc_process_input_8s_be;
+
+ /* Default implementation for scale factors calculation */
+ state->sbc_calc_scalefactors = sbc_calc_scalefactors;
+ state->sbc_calc_scalefactors_j = sbc_calc_scalefactors_j;
+ state->implementation_info = "Generic C";
+
+ /* X86/AMD64 optimizations */
+#ifdef SBC_BUILD_WITH_MMX_SUPPORT
+ sbc_init_primitives_mmx(state);
+#endif
+
+ /* ARM optimizations */
+#ifdef SBC_BUILD_WITH_ARMV6_SUPPORT
+ sbc_init_primitives_armv6(state);
+#endif
+#ifdef SBC_BUILD_WITH_IWMMXT_SUPPORT
+ sbc_init_primitives_iwmmxt(state);
+#endif
+#ifdef SBC_BUILD_WITH_NEON_SUPPORT
+ sbc_init_primitives_neon(state);
+#endif
+}
diff --git a/sbc/sbc_primitives.h b/sbc/sbc_primitives.h
new file mode 100644
index 0000000..3fec8d5
--- /dev/null
+++ b/sbc/sbc_primitives.h
@@ -0,0 +1,80 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2008-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2005 Henryk Ploetz <henryk@ploetzli.ch>
+ * Copyright (C) 2005-2006 Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef __SBC_PRIMITIVES_H
+#define __SBC_PRIMITIVES_H
+
+#define SCALE_OUT_BITS 15
+#define SBC_X_BUFFER_SIZE 328
+
+#ifdef __GNUC__
+#define SBC_ALWAYS_INLINE __attribute__((always_inline))
+#else
+#define SBC_ALWAYS_INLINE inline
+#endif
+
+struct sbc_encoder_state {
+ int position;
+ int16_t SBC_ALIGNED X[2][SBC_X_BUFFER_SIZE];
+ /* Polyphase analysis filter for 4 subbands configuration,
+ * it handles 4 blocks at once */
+ void (*sbc_analyze_4b_4s)(int16_t *x, int32_t *out, int out_stride);
+ /* Polyphase analysis filter for 8 subbands configuration,
+ * it handles 4 blocks at once */
+ void (*sbc_analyze_4b_8s)(int16_t *x, int32_t *out, int out_stride);
+ /* Process input data (deinterleave, endian conversion, reordering),
+ * depending on the number of subbands and input data byte order */
+ int (*sbc_enc_process_input_4s_le)(int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels);
+ int (*sbc_enc_process_input_4s_be)(int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels);
+ int (*sbc_enc_process_input_8s_le)(int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels);
+ int (*sbc_enc_process_input_8s_be)(int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels);
+ /* Scale factors calculation */
+ void (*sbc_calc_scalefactors)(int32_t sb_sample_f[16][2][8],
+ uint32_t scale_factor[2][8],
+ int blocks, int channels, int subbands);
+ /* Scale factors calculation with joint stereo support */
+ int (*sbc_calc_scalefactors_j)(int32_t sb_sample_f[16][2][8],
+ uint32_t scale_factor[2][8],
+ int blocks, int subbands);
+ const char *implementation_info;
+};
+
+/*
+ * Initialize pointers to the functions which are the basic "building bricks"
+ * of SBC codec. Best implementation is selected based on target CPU
+ * capabilities.
+ */
+void sbc_init_primitives(struct sbc_encoder_state *encoder_state);
+
+#endif
diff --git a/sbc/sbc_primitives_armv6.c b/sbc/sbc_primitives_armv6.c
new file mode 100644
index 0000000..9586098
--- /dev/null
+++ b/sbc/sbc_primitives_armv6.c
@@ -0,0 +1,299 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2008-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2005 Henryk Ploetz <henryk@ploetzli.ch>
+ * Copyright (C) 2005-2006 Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdint.h>
+#include <limits.h>
+#include "sbc.h"
+#include "sbc_math.h"
+#include "sbc_tables.h"
+
+#include "sbc_primitives_armv6.h"
+
+/*
+ * ARMv6 optimizations. The instructions are scheduled for ARM11 pipeline.
+ */
+
+#ifdef SBC_BUILD_WITH_ARMV6_SUPPORT
+
+static void __attribute__((naked)) sbc_analyze_four_armv6()
+{
+ /* r0 = in, r1 = out, r2 = consts */
+ asm volatile (
+ "push {r1, r4-r7, lr}\n"
+ "push {r8-r11}\n"
+ "ldrd r4, r5, [r0, #0]\n"
+ "ldrd r6, r7, [r2, #0]\n"
+ "ldrd r8, r9, [r0, #16]\n"
+ "ldrd r10, r11, [r2, #16]\n"
+ "mov r14, #0x8000\n"
+ "smlad r3, r4, r6, r14\n"
+ "smlad r12, r5, r7, r14\n"
+ "ldrd r4, r5, [r0, #32]\n"
+ "ldrd r6, r7, [r2, #32]\n"
+ "smlad r3, r8, r10, r3\n"
+ "smlad r12, r9, r11, r12\n"
+ "ldrd r8, r9, [r0, #48]\n"
+ "ldrd r10, r11, [r2, #48]\n"
+ "smlad r3, r4, r6, r3\n"
+ "smlad r12, r5, r7, r12\n"
+ "ldrd r4, r5, [r0, #64]\n"
+ "ldrd r6, r7, [r2, #64]\n"
+ "smlad r3, r8, r10, r3\n"
+ "smlad r12, r9, r11, r12\n"
+ "ldrd r8, r9, [r0, #8]\n"
+ "ldrd r10, r11, [r2, #8]\n"
+ "smlad r3, r4, r6, r3\n" /* t1[0] is done */
+ "smlad r12, r5, r7, r12\n" /* t1[1] is done */
+ "ldrd r4, r5, [r0, #24]\n"
+ "ldrd r6, r7, [r2, #24]\n"
+ "pkhtb r3, r12, r3, asr #16\n" /* combine t1[0] and t1[1] */
+ "smlad r12, r8, r10, r14\n"
+ "smlad r14, r9, r11, r14\n"
+ "ldrd r8, r9, [r0, #40]\n"
+ "ldrd r10, r11, [r2, #40]\n"
+ "smlad r12, r4, r6, r12\n"
+ "smlad r14, r5, r7, r14\n"
+ "ldrd r4, r5, [r0, #56]\n"
+ "ldrd r6, r7, [r2, #56]\n"
+ "smlad r12, r8, r10, r12\n"
+ "smlad r14, r9, r11, r14\n"
+ "ldrd r8, r9, [r0, #72]\n"
+ "ldrd r10, r11, [r2, #72]\n"
+ "smlad r12, r4, r6, r12\n"
+ "smlad r14, r5, r7, r14\n"
+ "ldrd r4, r5, [r2, #80]\n" /* start loading cos table */
+ "smlad r12, r8, r10, r12\n" /* t1[2] is done */
+ "smlad r14, r9, r11, r14\n" /* t1[3] is done */
+ "ldrd r6, r7, [r2, #88]\n"
+ "ldrd r8, r9, [r2, #96]\n"
+ "ldrd r10, r11, [r2, #104]\n" /* cos table fully loaded */
+ "pkhtb r12, r14, r12, asr #16\n" /* combine t1[2] and t1[3] */
+ "smuad r4, r3, r4\n"
+ "smuad r5, r3, r5\n"
+ "smlad r4, r12, r8, r4\n"
+ "smlad r5, r12, r9, r5\n"
+ "smuad r6, r3, r6\n"
+ "smuad r7, r3, r7\n"
+ "smlad r6, r12, r10, r6\n"
+ "smlad r7, r12, r11, r7\n"
+ "pop {r8-r11}\n"
+ "stmia r1, {r4, r5, r6, r7}\n"
+ "pop {r1, r4-r7, pc}\n"
+ );
+}
+
+#define sbc_analyze_four(in, out, consts) \
+ ((void (*)(int16_t *, int32_t *, const FIXED_T*)) \
+ sbc_analyze_four_armv6)((in), (out), (consts))
+
+static void __attribute__((naked)) sbc_analyze_eight_armv6()
+{
+ /* r0 = in, r1 = out, r2 = consts */
+ asm volatile (
+ "push {r1, r4-r7, lr}\n"
+ "push {r8-r11}\n"
+ "ldrd r4, r5, [r0, #24]\n"
+ "ldrd r6, r7, [r2, #24]\n"
+ "ldrd r8, r9, [r0, #56]\n"
+ "ldrd r10, r11, [r2, #56]\n"
+ "mov r14, #0x8000\n"
+ "smlad r3, r4, r6, r14\n"
+ "smlad r12, r5, r7, r14\n"
+ "ldrd r4, r5, [r0, #88]\n"
+ "ldrd r6, r7, [r2, #88]\n"
+ "smlad r3, r8, r10, r3\n"
+ "smlad r12, r9, r11, r12\n"
+ "ldrd r8, r9, [r0, #120]\n"
+ "ldrd r10, r11, [r2, #120]\n"
+ "smlad r3, r4, r6, r3\n"
+ "smlad r12, r5, r7, r12\n"
+ "ldrd r4, r5, [r0, #152]\n"
+ "ldrd r6, r7, [r2, #152]\n"
+ "smlad r3, r8, r10, r3\n"
+ "smlad r12, r9, r11, r12\n"
+ "ldrd r8, r9, [r0, #16]\n"
+ "ldrd r10, r11, [r2, #16]\n"
+ "smlad r3, r4, r6, r3\n" /* t1[6] is done */
+ "smlad r12, r5, r7, r12\n" /* t1[7] is done */
+ "ldrd r4, r5, [r0, #48]\n"
+ "ldrd r6, r7, [r2, #48]\n"
+ "pkhtb r3, r12, r3, asr #16\n" /* combine t1[6] and t1[7] */
+ "str r3, [sp, #-4]!\n" /* save to stack */
+ "smlad r3, r8, r10, r14\n"
+ "smlad r12, r9, r11, r14\n"
+ "ldrd r8, r9, [r0, #80]\n"
+ "ldrd r10, r11, [r2, #80]\n"
+ "smlad r3, r4, r6, r3\n"
+ "smlad r12, r5, r7, r12\n"
+ "ldrd r4, r5, [r0, #112]\n"
+ "ldrd r6, r7, [r2, #112]\n"
+ "smlad r3, r8, r10, r3\n"
+ "smlad r12, r9, r11, r12\n"
+ "ldrd r8, r9, [r0, #144]\n"
+ "ldrd r10, r11, [r2, #144]\n"
+ "smlad r3, r4, r6, r3\n"
+ "smlad r12, r5, r7, r12\n"
+ "ldrd r4, r5, [r0, #0]\n"
+ "ldrd r6, r7, [r2, #0]\n"
+ "smlad r3, r8, r10, r3\n" /* t1[4] is done */
+ "smlad r12, r9, r11, r12\n" /* t1[5] is done */
+ "ldrd r8, r9, [r0, #32]\n"
+ "ldrd r10, r11, [r2, #32]\n"
+ "pkhtb r3, r12, r3, asr #16\n" /* combine t1[4] and t1[5] */
+ "str r3, [sp, #-4]!\n" /* save to stack */
+ "smlad r3, r4, r6, r14\n"
+ "smlad r12, r5, r7, r14\n"
+ "ldrd r4, r5, [r0, #64]\n"
+ "ldrd r6, r7, [r2, #64]\n"
+ "smlad r3, r8, r10, r3\n"
+ "smlad r12, r9, r11, r12\n"
+ "ldrd r8, r9, [r0, #96]\n"
+ "ldrd r10, r11, [r2, #96]\n"
+ "smlad r3, r4, r6, r3\n"
+ "smlad r12, r5, r7, r12\n"
+ "ldrd r4, r5, [r0, #128]\n"
+ "ldrd r6, r7, [r2, #128]\n"
+ "smlad r3, r8, r10, r3\n"
+ "smlad r12, r9, r11, r12\n"
+ "ldrd r8, r9, [r0, #8]\n"
+ "ldrd r10, r11, [r2, #8]\n"
+ "smlad r3, r4, r6, r3\n" /* t1[0] is done */
+ "smlad r12, r5, r7, r12\n" /* t1[1] is done */
+ "ldrd r4, r5, [r0, #40]\n"
+ "ldrd r6, r7, [r2, #40]\n"
+ "pkhtb r3, r12, r3, asr #16\n" /* combine t1[0] and t1[1] */
+ "smlad r12, r8, r10, r14\n"
+ "smlad r14, r9, r11, r14\n"
+ "ldrd r8, r9, [r0, #72]\n"
+ "ldrd r10, r11, [r2, #72]\n"
+ "smlad r12, r4, r6, r12\n"
+ "smlad r14, r5, r7, r14\n"
+ "ldrd r4, r5, [r0, #104]\n"
+ "ldrd r6, r7, [r2, #104]\n"
+ "smlad r12, r8, r10, r12\n"
+ "smlad r14, r9, r11, r14\n"
+ "ldrd r8, r9, [r0, #136]\n"
+ "ldrd r10, r11, [r2, #136]!\n"
+ "smlad r12, r4, r6, r12\n"
+ "smlad r14, r5, r7, r14\n"
+ "ldrd r4, r5, [r2, #(160 - 136 + 0)]\n"
+ "smlad r12, r8, r10, r12\n" /* t1[2] is done */
+ "smlad r14, r9, r11, r14\n" /* t1[3] is done */
+ "ldrd r6, r7, [r2, #(160 - 136 + 8)]\n"
+ "smuad r4, r3, r4\n"
+ "smuad r5, r3, r5\n"
+ "pkhtb r12, r14, r12, asr #16\n" /* combine t1[2] and t1[3] */
+ /* r3 = t2[0:1] */
+ /* r12 = t2[2:3] */
+ "pop {r0, r14}\n" /* t2[4:5], t2[6:7] */
+ "ldrd r8, r9, [r2, #(160 - 136 + 32)]\n"
+ "smuad r6, r3, r6\n"
+ "smuad r7, r3, r7\n"
+ "ldrd r10, r11, [r2, #(160 - 136 + 40)]\n"
+ "smlad r4, r12, r8, r4\n"
+ "smlad r5, r12, r9, r5\n"
+ "ldrd r8, r9, [r2, #(160 - 136 + 64)]\n"
+ "smlad r6, r12, r10, r6\n"
+ "smlad r7, r12, r11, r7\n"
+ "ldrd r10, r11, [r2, #(160 - 136 + 72)]\n"
+ "smlad r4, r0, r8, r4\n"
+ "smlad r5, r0, r9, r5\n"
+ "ldrd r8, r9, [r2, #(160 - 136 + 96)]\n"
+ "smlad r6, r0, r10, r6\n"
+ "smlad r7, r0, r11, r7\n"
+ "ldrd r10, r11, [r2, #(160 - 136 + 104)]\n"
+ "smlad r4, r14, r8, r4\n"
+ "smlad r5, r14, r9, r5\n"
+ "ldrd r8, r9, [r2, #(160 - 136 + 16 + 0)]\n"
+ "smlad r6, r14, r10, r6\n"
+ "smlad r7, r14, r11, r7\n"
+ "ldrd r10, r11, [r2, #(160 - 136 + 16 + 8)]\n"
+ "stmia r1!, {r4, r5}\n"
+ "smuad r4, r3, r8\n"
+ "smuad r5, r3, r9\n"
+ "ldrd r8, r9, [r2, #(160 - 136 + 16 + 32)]\n"
+ "stmia r1!, {r6, r7}\n"
+ "smuad r6, r3, r10\n"
+ "smuad r7, r3, r11\n"
+ "ldrd r10, r11, [r2, #(160 - 136 + 16 + 40)]\n"
+ "smlad r4, r12, r8, r4\n"
+ "smlad r5, r12, r9, r5\n"
+ "ldrd r8, r9, [r2, #(160 - 136 + 16 + 64)]\n"
+ "smlad r6, r12, r10, r6\n"
+ "smlad r7, r12, r11, r7\n"
+ "ldrd r10, r11, [r2, #(160 - 136 + 16 + 72)]\n"
+ "smlad r4, r0, r8, r4\n"
+ "smlad r5, r0, r9, r5\n"
+ "ldrd r8, r9, [r2, #(160 - 136 + 16 + 96)]\n"
+ "smlad r6, r0, r10, r6\n"
+ "smlad r7, r0, r11, r7\n"
+ "ldrd r10, r11, [r2, #(160 - 136 + 16 + 104)]\n"
+ "smlad r4, r14, r8, r4\n"
+ "smlad r5, r14, r9, r5\n"
+ "smlad r6, r14, r10, r6\n"
+ "smlad r7, r14, r11, r7\n"
+ "pop {r8-r11}\n"
+ "stmia r1!, {r4, r5, r6, r7}\n"
+ "pop {r1, r4-r7, pc}\n"
+ );
+}
+
+#define sbc_analyze_eight(in, out, consts) \
+ ((void (*)(int16_t *, int32_t *, const FIXED_T*)) \
+ sbc_analyze_eight_armv6)((in), (out), (consts))
+
+static void sbc_analyze_4b_4s_armv6(int16_t *x, int32_t *out, int out_stride)
+{
+ /* Analyze blocks */
+ sbc_analyze_four(x + 12, out, analysis_consts_fixed4_simd_odd);
+ out += out_stride;
+ sbc_analyze_four(x + 8, out, analysis_consts_fixed4_simd_even);
+ out += out_stride;
+ sbc_analyze_four(x + 4, out, analysis_consts_fixed4_simd_odd);
+ out += out_stride;
+ sbc_analyze_four(x + 0, out, analysis_consts_fixed4_simd_even);
+}
+
+static void sbc_analyze_4b_8s_armv6(int16_t *x, int32_t *out, int out_stride)
+{
+ /* Analyze blocks */
+ sbc_analyze_eight(x + 24, out, analysis_consts_fixed8_simd_odd);
+ out += out_stride;
+ sbc_analyze_eight(x + 16, out, analysis_consts_fixed8_simd_even);
+ out += out_stride;
+ sbc_analyze_eight(x + 8, out, analysis_consts_fixed8_simd_odd);
+ out += out_stride;
+ sbc_analyze_eight(x + 0, out, analysis_consts_fixed8_simd_even);
+}
+
+void sbc_init_primitives_armv6(struct sbc_encoder_state *state)
+{
+ state->sbc_analyze_4b_4s = sbc_analyze_4b_4s_armv6;
+ state->sbc_analyze_4b_8s = sbc_analyze_4b_8s_armv6;
+ state->implementation_info = "ARMv6 SIMD";
+}
+
+#endif
diff --git a/sbc/sbc_primitives_armv6.h b/sbc/sbc_primitives_armv6.h
new file mode 100644
index 0000000..6a9efe5
--- /dev/null
+++ b/sbc/sbc_primitives_armv6.h
@@ -0,0 +1,52 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2008-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2005 Henryk Ploetz <henryk@ploetzli.ch>
+ * Copyright (C) 2005-2006 Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef __SBC_PRIMITIVES_ARMV6_H
+#define __SBC_PRIMITIVES_ARMV6_H
+
+#include "sbc_primitives.h"
+
+#if defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || \
+ defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || \
+ defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) || \
+ defined(__ARM_ARCH_6M__) || defined(__ARM_ARCH_7__) || \
+ defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || \
+ defined(__ARM_ARCH_7M__)
+#define SBC_HAVE_ARMV6 1
+#endif
+
+#if !defined(SBC_HIGH_PRECISION) && (SCALE_OUT_BITS == 15) && \
+ defined(__GNUC__) && defined(SBC_HAVE_ARMV6) && \
+ defined(__ARM_EABI__) && !defined(__ARM_NEON__) && \
+ (!defined(__thumb__) || defined(__thumb2__))
+
+#define SBC_BUILD_WITH_ARMV6_SUPPORT
+
+void sbc_init_primitives_armv6(struct sbc_encoder_state *encoder_state);
+
+#endif
+
+#endif
diff --git a/sbc/sbc_primitives_iwmmxt.c b/sbc/sbc_primitives_iwmmxt.c
new file mode 100644
index 0000000..213967e
--- /dev/null
+++ b/sbc/sbc_primitives_iwmmxt.c
@@ -0,0 +1,304 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2010 Keith Mok <ek9852@gmail.com>
+ * Copyright (C) 2008-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2005 Henryk Ploetz <henryk@ploetzli.ch>
+ * Copyright (C) 2005-2006 Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdint.h>
+#include <limits.h>
+#include "sbc.h"
+#include "sbc_math.h"
+#include "sbc_tables.h"
+
+#include "sbc_primitives_iwmmxt.h"
+
+/*
+ * IWMMXT optimizations
+ */
+
+#ifdef SBC_BUILD_WITH_IWMMXT_SUPPORT
+
+static inline void sbc_analyze_four_iwmmxt(const int16_t *in, int32_t *out,
+ const FIXED_T *consts)
+{
+ asm volatile (
+ "wldrd wr0, [%0]\n"
+ "tbcstw wr4, %2\n"
+ "wldrd wr2, [%1]\n"
+ "wldrd wr1, [%0, #8]\n"
+ "wldrd wr3, [%1, #8]\n"
+ "wmadds wr0, wr2, wr0\n"
+ " wldrd wr6, [%0, #16]\n"
+ "wmadds wr1, wr3, wr1\n"
+ " wldrd wr7, [%0, #24]\n"
+ "waddwss wr0, wr0, wr4\n"
+ " wldrd wr8, [%1, #16]\n"
+ "waddwss wr1, wr1, wr4\n"
+ " wldrd wr9, [%1, #24]\n"
+ " wmadds wr6, wr8, wr6\n"
+ " wldrd wr2, [%0, #32]\n"
+ " wmadds wr7, wr9, wr7\n"
+ " wldrd wr3, [%0, #40]\n"
+ " waddwss wr0, wr6, wr0\n"
+ " wldrd wr4, [%1, #32]\n"
+ " waddwss wr1, wr7, wr1\n"
+ " wldrd wr5, [%1, #40]\n"
+ " wmadds wr2, wr4, wr2\n"
+ "wldrd wr6, [%0, #48]\n"
+ " wmadds wr3, wr5, wr3\n"
+ "wldrd wr7, [%0, #56]\n"
+ " waddwss wr0, wr2, wr0\n"
+ "wldrd wr8, [%1, #48]\n"
+ " waddwss wr1, wr3, wr1\n"
+ "wldrd wr9, [%1, #56]\n"
+ "wmadds wr6, wr8, wr6\n"
+ " wldrd wr2, [%0, #64]\n"
+ "wmadds wr7, wr9, wr7\n"
+ " wldrd wr3, [%0, #72]\n"
+ "waddwss wr0, wr6, wr0\n"
+ " wldrd wr4, [%1, #64]\n"
+ "waddwss wr1, wr7, wr1\n"
+ " wldrd wr5, [%1, #72]\n"
+ " wmadds wr2, wr4, wr2\n"
+ "tmcr wcgr0, %4\n"
+ " wmadds wr3, wr5, wr3\n"
+ " waddwss wr0, wr2, wr0\n"
+ " waddwss wr1, wr3, wr1\n"
+ "\n"
+ "wsrawg wr0, wr0, wcgr0\n"
+ " wldrd wr4, [%1, #80]\n"
+ "wsrawg wr1, wr1, wcgr0\n"
+ " wldrd wr5, [%1, #88]\n"
+ "wpackwss wr0, wr0, wr0\n"
+ " wldrd wr6, [%1, #96]\n"
+ "wpackwss wr1, wr1, wr1\n"
+ "wmadds wr2, wr5, wr0\n"
+ " wldrd wr7, [%1, #104]\n"
+ "wmadds wr0, wr4, wr0\n"
+ "\n"
+ " wmadds wr3, wr7, wr1\n"
+ " wmadds wr1, wr6, wr1\n"
+ " waddwss wr2, wr3, wr2\n"
+ " waddwss wr0, wr1, wr0\n"
+ "\n"
+ "wstrd wr0, [%3]\n"
+ "wstrd wr2, [%3, #8]\n"
+ :
+ : "r" (in), "r" (consts),
+ "r" (1 << (SBC_PROTO_FIXED4_SCALE - 1)), "r" (out),
+ "r" (SBC_PROTO_FIXED4_SCALE)
+ : "wr0", "wr1", "wr2", "wr3", "wr4", "wr5", "wr6", "wr7",
+ "wr8", "wr9", "wcgr0", "memory");
+}
+
+static inline void sbc_analyze_eight_iwmmxt(const int16_t *in, int32_t *out,
+ const FIXED_T *consts)
+{
+ asm volatile (
+ "wldrd wr0, [%0]\n"
+ "tbcstw wr15, %2\n"
+ "wldrd wr1, [%0, #8]\n"
+ "wldrd wr2, [%0, #16]\n"
+ "wldrd wr3, [%0, #24]\n"
+ "wldrd wr4, [%1]\n"
+ "wldrd wr5, [%1, #8]\n"
+ "wldrd wr6, [%1, #16]\n"
+ "wldrd wr7, [%1, #24]\n"
+ "wmadds wr0, wr0, wr4\n"
+ " wldrd wr8, [%1, #32]\n"
+ "wmadds wr1, wr1, wr5\n"
+ " wldrd wr9, [%1, #40]\n"
+ "wmadds wr2, wr2, wr6\n"
+ " wldrd wr10, [%1, #48]\n"
+ "wmadds wr3, wr3, wr7\n"
+ " wldrd wr11, [%1, #56]\n"
+ "waddwss wr0, wr0, wr15\n"
+ " wldrd wr4, [%0, #32]\n"
+ "waddwss wr1, wr1, wr15\n"
+ " wldrd wr5, [%0, #40]\n"
+ "waddwss wr2, wr2, wr15\n"
+ " wldrd wr6, [%0, #48]\n"
+ "waddwss wr3, wr3, wr15\n"
+ " wldrd wr7, [%0, #56]\n"
+ " wmadds wr4, wr4, wr8\n"
+ " wldrd wr12, [%0, #64]\n"
+ " wmadds wr5, wr5, wr9\n"
+ " wldrd wr13, [%0, #72]\n"
+ " wmadds wr6, wr6, wr10\n"
+ " wldrd wr14, [%0, #80]\n"
+ " wmadds wr7, wr7, wr11\n"
+ " wldrd wr15, [%0, #88]\n"
+ " waddwss wr0, wr4, wr0\n"
+ " wldrd wr8, [%1, #64]\n"
+ " waddwss wr1, wr5, wr1\n"
+ " wldrd wr9, [%1, #72]\n"
+ " waddwss wr2, wr6, wr2\n"
+ " wldrd wr10, [%1, #80]\n"
+ " waddwss wr3, wr7, wr3\n"
+ " wldrd wr11, [%1, #88]\n"
+ " wmadds wr12, wr12, wr8\n"
+ "wldrd wr4, [%0, #96]\n"
+ " wmadds wr13, wr13, wr9\n"
+ "wldrd wr5, [%0, #104]\n"
+ " wmadds wr14, wr14, wr10\n"
+ "wldrd wr6, [%0, #112]\n"
+ " wmadds wr15, wr15, wr11\n"
+ "wldrd wr7, [%0, #120]\n"
+ " waddwss wr0, wr12, wr0\n"
+ "wldrd wr8, [%1, #96]\n"
+ " waddwss wr1, wr13, wr1\n"
+ "wldrd wr9, [%1, #104]\n"
+ " waddwss wr2, wr14, wr2\n"
+ "wldrd wr10, [%1, #112]\n"
+ " waddwss wr3, wr15, wr3\n"
+ "wldrd wr11, [%1, #120]\n"
+ "wmadds wr4, wr4, wr8\n"
+ " wldrd wr12, [%0, #128]\n"
+ "wmadds wr5, wr5, wr9\n"
+ " wldrd wr13, [%0, #136]\n"
+ "wmadds wr6, wr6, wr10\n"
+ " wldrd wr14, [%0, #144]\n"
+ "wmadds wr7, wr7, wr11\n"
+ " wldrd wr15, [%0, #152]\n"
+ "waddwss wr0, wr4, wr0\n"
+ " wldrd wr8, [%1, #128]\n"
+ "waddwss wr1, wr5, wr1\n"
+ " wldrd wr9, [%1, #136]\n"
+ "waddwss wr2, wr6, wr2\n"
+ " wldrd wr10, [%1, #144]\n"
+ " waddwss wr3, wr7, wr3\n"
+ " wldrd wr11, [%1, #152]\n"
+ " wmadds wr12, wr12, wr8\n"
+ "tmcr wcgr0, %4\n"
+ " wmadds wr13, wr13, wr9\n"
+ " wmadds wr14, wr14, wr10\n"
+ " wmadds wr15, wr15, wr11\n"
+ " waddwss wr0, wr12, wr0\n"
+ " waddwss wr1, wr13, wr1\n"
+ " waddwss wr2, wr14, wr2\n"
+ " waddwss wr3, wr15, wr3\n"
+ "\n"
+ "wsrawg wr0, wr0, wcgr0\n"
+ "wsrawg wr1, wr1, wcgr0\n"
+ "wsrawg wr2, wr2, wcgr0\n"
+ "wsrawg wr3, wr3, wcgr0\n"
+ "\n"
+ "wpackwss wr0, wr0, wr0\n"
+ "wpackwss wr1, wr1, wr1\n"
+ " wldrd wr4, [%1, #160]\n"
+ "wpackwss wr2, wr2, wr2\n"
+ " wldrd wr5, [%1, #168]\n"
+ "wpackwss wr3, wr3, wr3\n"
+ " wldrd wr6, [%1, #192]\n"
+ " wmadds wr4, wr4, wr0\n"
+ " wldrd wr7, [%1, #200]\n"
+ " wmadds wr5, wr5, wr0\n"
+ " wldrd wr8, [%1, #224]\n"
+ " wmadds wr6, wr6, wr1\n"
+ " wldrd wr9, [%1, #232]\n"
+ " wmadds wr7, wr7, wr1\n"
+ " waddwss wr4, wr6, wr4\n"
+ " waddwss wr5, wr7, wr5\n"
+ " wmadds wr8, wr8, wr2\n"
+ "wldrd wr6, [%1, #256]\n"
+ " wmadds wr9, wr9, wr2\n"
+ "wldrd wr7, [%1, #264]\n"
+ "waddwss wr4, wr8, wr4\n"
+ " waddwss wr5, wr9, wr5\n"
+ "wmadds wr6, wr6, wr3\n"
+ "wmadds wr7, wr7, wr3\n"
+ "waddwss wr4, wr6, wr4\n"
+ "waddwss wr5, wr7, wr5\n"
+ "\n"
+ "wstrd wr4, [%3]\n"
+ "wstrd wr5, [%3, #8]\n"
+ "\n"
+ "wldrd wr6, [%1, #176]\n"
+ "wldrd wr5, [%1, #184]\n"
+ "wmadds wr5, wr5, wr0\n"
+ "wldrd wr8, [%1, #208]\n"
+ "wmadds wr0, wr6, wr0\n"
+ "wldrd wr9, [%1, #216]\n"
+ "wmadds wr9, wr9, wr1\n"
+ "wldrd wr6, [%1, #240]\n"
+ "wmadds wr1, wr8, wr1\n"
+ "wldrd wr7, [%1, #248]\n"
+ "waddwss wr0, wr1, wr0\n"
+ "waddwss wr5, wr9, wr5\n"
+ "wmadds wr7, wr7, wr2\n"
+ "wldrd wr8, [%1, #272]\n"
+ "wmadds wr2, wr6, wr2\n"
+ "wldrd wr9, [%1, #280]\n"
+ "waddwss wr0, wr2, wr0\n"
+ "waddwss wr5, wr7, wr5\n"
+ "wmadds wr9, wr9, wr3\n"
+ "wmadds wr3, wr8, wr3\n"
+ "waddwss wr0, wr3, wr0\n"
+ "waddwss wr5, wr9, wr5\n"
+ "\n"
+ "wstrd wr0, [%3, #16]\n"
+ "wstrd wr5, [%3, #24]\n"
+ :
+ : "r" (in), "r" (consts),
+ "r" (1 << (SBC_PROTO_FIXED8_SCALE - 1)), "r" (out),
+ "r" (SBC_PROTO_FIXED8_SCALE)
+ : "wr0", "wr1", "wr2", "wr3", "wr4", "wr5", "wr6", "wr7",
+ "wr8", "wr9", "wr10", "wr11", "wr12", "wr13", "wr14", "wr15",
+ "wcgr0", "memory");
+}
+
+static inline void sbc_analyze_4b_4s_iwmmxt(int16_t *x, int32_t *out,
+ int out_stride)
+{
+ /* Analyze blocks */
+ sbc_analyze_four_iwmmxt(x + 12, out, analysis_consts_fixed4_simd_odd);
+ out += out_stride;
+ sbc_analyze_four_iwmmxt(x + 8, out, analysis_consts_fixed4_simd_even);
+ out += out_stride;
+ sbc_analyze_four_iwmmxt(x + 4, out, analysis_consts_fixed4_simd_odd);
+ out += out_stride;
+ sbc_analyze_four_iwmmxt(x + 0, out, analysis_consts_fixed4_simd_even);
+}
+
+static inline void sbc_analyze_4b_8s_iwmmxt(int16_t *x, int32_t *out,
+ int out_stride)
+{
+ /* Analyze blocks */
+ sbc_analyze_eight_iwmmxt(x + 24, out, analysis_consts_fixed8_simd_odd);
+ out += out_stride;
+ sbc_analyze_eight_iwmmxt(x + 16, out, analysis_consts_fixed8_simd_even);
+ out += out_stride;
+ sbc_analyze_eight_iwmmxt(x + 8, out, analysis_consts_fixed8_simd_odd);
+ out += out_stride;
+ sbc_analyze_eight_iwmmxt(x + 0, out, analysis_consts_fixed8_simd_even);
+}
+
+void sbc_init_primitives_iwmmxt(struct sbc_encoder_state *state)
+{
+ state->sbc_analyze_4b_4s = sbc_analyze_4b_4s_iwmmxt;
+ state->sbc_analyze_4b_8s = sbc_analyze_4b_8s_iwmmxt;
+ state->implementation_info = "IWMMXT";
+}
+
+#endif
diff --git a/sbc/sbc_primitives_iwmmxt.h b/sbc/sbc_primitives_iwmmxt.h
new file mode 100644
index 0000000..b535e68
--- /dev/null
+++ b/sbc/sbc_primitives_iwmmxt.h
@@ -0,0 +1,42 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2010 Keith Mok <ek9852@gmail.com>
+ * Copyright (C) 2008-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2005 Henryk Ploetz <henryk@ploetzli.ch>
+ * Copyright (C) 2005-2006 Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef __SBC_PRIMITIVES_IWMMXT_H
+#define __SBC_PRIMITIVES_IWMMXT_H
+
+#include "sbc_primitives.h"
+
+#if defined(__GNUC__) && defined(__IWMMXT__) && \
+ !defined(SBC_HIGH_PRECISION) && (SCALE_OUT_BITS == 15)
+
+#define SBC_BUILD_WITH_IWMMXT_SUPPORT
+
+void sbc_init_primitives_iwmmxt(struct sbc_encoder_state *encoder_state);
+
+#endif
+
+#endif
diff --git a/sbc/sbc_primitives_mmx.c b/sbc/sbc_primitives_mmx.c
new file mode 100644
index 0000000..7f2fbc3
--- /dev/null
+++ b/sbc/sbc_primitives_mmx.c
@@ -0,0 +1,375 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2008-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2005 Henryk Ploetz <henryk@ploetzli.ch>
+ * Copyright (C) 2005-2006 Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdint.h>
+#include <limits.h>
+#include "sbc.h"
+#include "sbc_math.h"
+#include "sbc_tables.h"
+
+#include "sbc_primitives_mmx.h"
+
+/*
+ * MMX optimizations
+ */
+
+#ifdef SBC_BUILD_WITH_MMX_SUPPORT
+
+static inline void sbc_analyze_four_mmx(const int16_t *in, int32_t *out,
+ const FIXED_T *consts)
+{
+ static const SBC_ALIGNED int32_t round_c[2] = {
+ 1 << (SBC_PROTO_FIXED4_SCALE - 1),
+ 1 << (SBC_PROTO_FIXED4_SCALE - 1),
+ };
+ asm volatile (
+ "movq (%0), %%mm0\n"
+ "movq 8(%0), %%mm1\n"
+ "pmaddwd (%1), %%mm0\n"
+ "pmaddwd 8(%1), %%mm1\n"
+ "paddd (%2), %%mm0\n"
+ "paddd (%2), %%mm1\n"
+ "\n"
+ "movq 16(%0), %%mm2\n"
+ "movq 24(%0), %%mm3\n"
+ "pmaddwd 16(%1), %%mm2\n"
+ "pmaddwd 24(%1), %%mm3\n"
+ "paddd %%mm2, %%mm0\n"
+ "paddd %%mm3, %%mm1\n"
+ "\n"
+ "movq 32(%0), %%mm2\n"
+ "movq 40(%0), %%mm3\n"
+ "pmaddwd 32(%1), %%mm2\n"
+ "pmaddwd 40(%1), %%mm3\n"
+ "paddd %%mm2, %%mm0\n"
+ "paddd %%mm3, %%mm1\n"
+ "\n"
+ "movq 48(%0), %%mm2\n"
+ "movq 56(%0), %%mm3\n"
+ "pmaddwd 48(%1), %%mm2\n"
+ "pmaddwd 56(%1), %%mm3\n"
+ "paddd %%mm2, %%mm0\n"
+ "paddd %%mm3, %%mm1\n"
+ "\n"
+ "movq 64(%0), %%mm2\n"
+ "movq 72(%0), %%mm3\n"
+ "pmaddwd 64(%1), %%mm2\n"
+ "pmaddwd 72(%1), %%mm3\n"
+ "paddd %%mm2, %%mm0\n"
+ "paddd %%mm3, %%mm1\n"
+ "\n"
+ "psrad %4, %%mm0\n"
+ "psrad %4, %%mm1\n"
+ "packssdw %%mm0, %%mm0\n"
+ "packssdw %%mm1, %%mm1\n"
+ "\n"
+ "movq %%mm0, %%mm2\n"
+ "pmaddwd 80(%1), %%mm0\n"
+ "pmaddwd 88(%1), %%mm2\n"
+ "\n"
+ "movq %%mm1, %%mm3\n"
+ "pmaddwd 96(%1), %%mm1\n"
+ "pmaddwd 104(%1), %%mm3\n"
+ "paddd %%mm1, %%mm0\n"
+ "paddd %%mm3, %%mm2\n"
+ "\n"
+ "movq %%mm0, (%3)\n"
+ "movq %%mm2, 8(%3)\n"
+ :
+ : "r" (in), "r" (consts), "r" (&round_c), "r" (out),
+ "i" (SBC_PROTO_FIXED4_SCALE)
+ : "cc", "memory");
+}
+
+static inline void sbc_analyze_eight_mmx(const int16_t *in, int32_t *out,
+ const FIXED_T *consts)
+{
+ static const SBC_ALIGNED int32_t round_c[2] = {
+ 1 << (SBC_PROTO_FIXED8_SCALE - 1),
+ 1 << (SBC_PROTO_FIXED8_SCALE - 1),
+ };
+ asm volatile (
+ "movq (%0), %%mm0\n"
+ "movq 8(%0), %%mm1\n"
+ "movq 16(%0), %%mm2\n"
+ "movq 24(%0), %%mm3\n"
+ "pmaddwd (%1), %%mm0\n"
+ "pmaddwd 8(%1), %%mm1\n"
+ "pmaddwd 16(%1), %%mm2\n"
+ "pmaddwd 24(%1), %%mm3\n"
+ "paddd (%2), %%mm0\n"
+ "paddd (%2), %%mm1\n"
+ "paddd (%2), %%mm2\n"
+ "paddd (%2), %%mm3\n"
+ "\n"
+ "movq 32(%0), %%mm4\n"
+ "movq 40(%0), %%mm5\n"
+ "movq 48(%0), %%mm6\n"
+ "movq 56(%0), %%mm7\n"
+ "pmaddwd 32(%1), %%mm4\n"
+ "pmaddwd 40(%1), %%mm5\n"
+ "pmaddwd 48(%1), %%mm6\n"
+ "pmaddwd 56(%1), %%mm7\n"
+ "paddd %%mm4, %%mm0\n"
+ "paddd %%mm5, %%mm1\n"
+ "paddd %%mm6, %%mm2\n"
+ "paddd %%mm7, %%mm3\n"
+ "\n"
+ "movq 64(%0), %%mm4\n"
+ "movq 72(%0), %%mm5\n"
+ "movq 80(%0), %%mm6\n"
+ "movq 88(%0), %%mm7\n"
+ "pmaddwd 64(%1), %%mm4\n"
+ "pmaddwd 72(%1), %%mm5\n"
+ "pmaddwd 80(%1), %%mm6\n"
+ "pmaddwd 88(%1), %%mm7\n"
+ "paddd %%mm4, %%mm0\n"
+ "paddd %%mm5, %%mm1\n"
+ "paddd %%mm6, %%mm2\n"
+ "paddd %%mm7, %%mm3\n"
+ "\n"
+ "movq 96(%0), %%mm4\n"
+ "movq 104(%0), %%mm5\n"
+ "movq 112(%0), %%mm6\n"
+ "movq 120(%0), %%mm7\n"
+ "pmaddwd 96(%1), %%mm4\n"
+ "pmaddwd 104(%1), %%mm5\n"
+ "pmaddwd 112(%1), %%mm6\n"
+ "pmaddwd 120(%1), %%mm7\n"
+ "paddd %%mm4, %%mm0\n"
+ "paddd %%mm5, %%mm1\n"
+ "paddd %%mm6, %%mm2\n"
+ "paddd %%mm7, %%mm3\n"
+ "\n"
+ "movq 128(%0), %%mm4\n"
+ "movq 136(%0), %%mm5\n"
+ "movq 144(%0), %%mm6\n"
+ "movq 152(%0), %%mm7\n"
+ "pmaddwd 128(%1), %%mm4\n"
+ "pmaddwd 136(%1), %%mm5\n"
+ "pmaddwd 144(%1), %%mm6\n"
+ "pmaddwd 152(%1), %%mm7\n"
+ "paddd %%mm4, %%mm0\n"
+ "paddd %%mm5, %%mm1\n"
+ "paddd %%mm6, %%mm2\n"
+ "paddd %%mm7, %%mm3\n"
+ "\n"
+ "psrad %4, %%mm0\n"
+ "psrad %4, %%mm1\n"
+ "psrad %4, %%mm2\n"
+ "psrad %4, %%mm3\n"
+ "\n"
+ "packssdw %%mm0, %%mm0\n"
+ "packssdw %%mm1, %%mm1\n"
+ "packssdw %%mm2, %%mm2\n"
+ "packssdw %%mm3, %%mm3\n"
+ "\n"
+ "movq %%mm0, %%mm4\n"
+ "movq %%mm0, %%mm5\n"
+ "pmaddwd 160(%1), %%mm4\n"
+ "pmaddwd 168(%1), %%mm5\n"
+ "\n"
+ "movq %%mm1, %%mm6\n"
+ "movq %%mm1, %%mm7\n"
+ "pmaddwd 192(%1), %%mm6\n"
+ "pmaddwd 200(%1), %%mm7\n"
+ "paddd %%mm6, %%mm4\n"
+ "paddd %%mm7, %%mm5\n"
+ "\n"
+ "movq %%mm2, %%mm6\n"
+ "movq %%mm2, %%mm7\n"
+ "pmaddwd 224(%1), %%mm6\n"
+ "pmaddwd 232(%1), %%mm7\n"
+ "paddd %%mm6, %%mm4\n"
+ "paddd %%mm7, %%mm5\n"
+ "\n"
+ "movq %%mm3, %%mm6\n"
+ "movq %%mm3, %%mm7\n"
+ "pmaddwd 256(%1), %%mm6\n"
+ "pmaddwd 264(%1), %%mm7\n"
+ "paddd %%mm6, %%mm4\n"
+ "paddd %%mm7, %%mm5\n"
+ "\n"
+ "movq %%mm4, (%3)\n"
+ "movq %%mm5, 8(%3)\n"
+ "\n"
+ "movq %%mm0, %%mm5\n"
+ "pmaddwd 176(%1), %%mm0\n"
+ "pmaddwd 184(%1), %%mm5\n"
+ "\n"
+ "movq %%mm1, %%mm7\n"
+ "pmaddwd 208(%1), %%mm1\n"
+ "pmaddwd 216(%1), %%mm7\n"
+ "paddd %%mm1, %%mm0\n"
+ "paddd %%mm7, %%mm5\n"
+ "\n"
+ "movq %%mm2, %%mm7\n"
+ "pmaddwd 240(%1), %%mm2\n"
+ "pmaddwd 248(%1), %%mm7\n"
+ "paddd %%mm2, %%mm0\n"
+ "paddd %%mm7, %%mm5\n"
+ "\n"
+ "movq %%mm3, %%mm7\n"
+ "pmaddwd 272(%1), %%mm3\n"
+ "pmaddwd 280(%1), %%mm7\n"
+ "paddd %%mm3, %%mm0\n"
+ "paddd %%mm7, %%mm5\n"
+ "\n"
+ "movq %%mm0, 16(%3)\n"
+ "movq %%mm5, 24(%3)\n"
+ :
+ : "r" (in), "r" (consts), "r" (&round_c), "r" (out),
+ "i" (SBC_PROTO_FIXED8_SCALE)
+ : "cc", "memory");
+}
+
+static inline void sbc_analyze_4b_4s_mmx(int16_t *x, int32_t *out,
+ int out_stride)
+{
+ /* Analyze blocks */
+ sbc_analyze_four_mmx(x + 12, out, analysis_consts_fixed4_simd_odd);
+ out += out_stride;
+ sbc_analyze_four_mmx(x + 8, out, analysis_consts_fixed4_simd_even);
+ out += out_stride;
+ sbc_analyze_four_mmx(x + 4, out, analysis_consts_fixed4_simd_odd);
+ out += out_stride;
+ sbc_analyze_four_mmx(x + 0, out, analysis_consts_fixed4_simd_even);
+
+ asm volatile ("emms\n");
+}
+
+static inline void sbc_analyze_4b_8s_mmx(int16_t *x, int32_t *out,
+ int out_stride)
+{
+ /* Analyze blocks */
+ sbc_analyze_eight_mmx(x + 24, out, analysis_consts_fixed8_simd_odd);
+ out += out_stride;
+ sbc_analyze_eight_mmx(x + 16, out, analysis_consts_fixed8_simd_even);
+ out += out_stride;
+ sbc_analyze_eight_mmx(x + 8, out, analysis_consts_fixed8_simd_odd);
+ out += out_stride;
+ sbc_analyze_eight_mmx(x + 0, out, analysis_consts_fixed8_simd_even);
+
+ asm volatile ("emms\n");
+}
+
+static void sbc_calc_scalefactors_mmx(
+ int32_t sb_sample_f[16][2][8],
+ uint32_t scale_factor[2][8],
+ int blocks, int channels, int subbands)
+{
+ static const SBC_ALIGNED int32_t consts[2] = {
+ 1 << SCALE_OUT_BITS,
+ 1 << SCALE_OUT_BITS,
+ };
+ int ch, sb;
+ intptr_t blk;
+ for (ch = 0; ch < channels; ch++) {
+ for (sb = 0; sb < subbands; sb += 2) {
+ blk = (blocks - 1) * (((char *) &sb_sample_f[1][0][0] -
+ (char *) &sb_sample_f[0][0][0]));
+ asm volatile (
+ "movq (%4), %%mm0\n"
+ "1:\n"
+ "movq (%1, %0), %%mm1\n"
+ "pxor %%mm2, %%mm2\n"
+ "pcmpgtd %%mm2, %%mm1\n"
+ "paddd (%1, %0), %%mm1\n"
+ "pcmpgtd %%mm1, %%mm2\n"
+ "pxor %%mm2, %%mm1\n"
+
+ "por %%mm1, %%mm0\n"
+
+ "sub %2, %0\n"
+ "jns 1b\n"
+
+ "movd %%mm0, %k0\n"
+ "psrlq $32, %%mm0\n"
+ "bsrl %k0, %k0\n"
+ "subl %5, %k0\n"
+ "movl %k0, (%3)\n"
+
+ "movd %%mm0, %k0\n"
+ "bsrl %k0, %k0\n"
+ "subl %5, %k0\n"
+ "movl %k0, 4(%3)\n"
+ : "+r" (blk)
+ : "r" (&sb_sample_f[0][ch][sb]),
+ "i" ((char *) &sb_sample_f[1][0][0] -
+ (char *) &sb_sample_f[0][0][0]),
+ "r" (&scale_factor[ch][sb]),
+ "r" (&consts),
+ "i" (SCALE_OUT_BITS)
+ : "cc", "memory");
+ }
+ }
+ asm volatile ("emms\n");
+}
+
+static int check_mmx_support(void)
+{
+#ifdef __amd64__
+ return 1; /* We assume that all 64-bit processors have MMX support */
+#else
+ int cpuid_feature_information;
+ asm volatile (
+ /* According to Intel manual, CPUID instruction is supported
+ * if the value of ID bit (bit 21) in EFLAGS can be modified */
+ "pushf\n"
+ "movl (%%esp), %0\n"
+ "xorl $0x200000, (%%esp)\n" /* try to modify ID bit */
+ "popf\n"
+ "pushf\n"
+ "xorl (%%esp), %0\n" /* check if ID bit changed */
+ "jz 1f\n"
+ "push %%eax\n"
+ "push %%ebx\n"
+ "push %%ecx\n"
+ "mov $1, %%eax\n"
+ "cpuid\n"
+ "pop %%ecx\n"
+ "pop %%ebx\n"
+ "pop %%eax\n"
+ "1:\n"
+ "popf\n"
+ : "=d" (cpuid_feature_information)
+ :
+ : "cc");
+ return cpuid_feature_information & (1 << 23);
+#endif
+}
+
+void sbc_init_primitives_mmx(struct sbc_encoder_state *state)
+{
+ if (check_mmx_support()) {
+ state->sbc_analyze_4b_4s = sbc_analyze_4b_4s_mmx;
+ state->sbc_analyze_4b_8s = sbc_analyze_4b_8s_mmx;
+ state->sbc_calc_scalefactors = sbc_calc_scalefactors_mmx;
+ state->implementation_info = "MMX";
+ }
+}
+
+#endif
diff --git a/sbc/sbc_primitives_mmx.h b/sbc/sbc_primitives_mmx.h
new file mode 100644
index 0000000..e0e728b
--- /dev/null
+++ b/sbc/sbc_primitives_mmx.h
@@ -0,0 +1,41 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2008-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2005 Henryk Ploetz <henryk@ploetzli.ch>
+ * Copyright (C) 2005-2006 Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef __SBC_PRIMITIVES_MMX_H
+#define __SBC_PRIMITIVES_MMX_H
+
+#include "sbc_primitives.h"
+
+#if defined(__GNUC__) && (defined(__i386__) || defined(__amd64__)) && \
+ !defined(SBC_HIGH_PRECISION) && (SCALE_OUT_BITS == 15)
+
+#define SBC_BUILD_WITH_MMX_SUPPORT
+
+void sbc_init_primitives_mmx(struct sbc_encoder_state *encoder_state);
+
+#endif
+
+#endif
diff --git a/sbc/sbc_primitives_neon.c b/sbc/sbc_primitives_neon.c
new file mode 100644
index 0000000..0572158
--- /dev/null
+++ b/sbc/sbc_primitives_neon.c
@@ -0,0 +1,893 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2008-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2005 Henryk Ploetz <henryk@ploetzli.ch>
+ * Copyright (C) 2005-2006 Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdint.h>
+#include <limits.h>
+#include "sbc.h"
+#include "sbc_math.h"
+#include "sbc_tables.h"
+
+#include "sbc_primitives_neon.h"
+
+/*
+ * ARM NEON optimizations
+ */
+
+#ifdef SBC_BUILD_WITH_NEON_SUPPORT
+
+static inline void _sbc_analyze_four_neon(const int16_t *in, int32_t *out,
+ const FIXED_T *consts)
+{
+ /* TODO: merge even and odd cases (or even merge all four calls to this
+ * function) in order to have only aligned reads from 'in' array
+ * and reduce number of load instructions */
+ asm volatile (
+ "vld1.16 {d4, d5}, [%0, :64]!\n"
+ "vld1.16 {d8, d9}, [%1, :128]!\n"
+
+ "vmull.s16 q0, d4, d8\n"
+ "vld1.16 {d6, d7}, [%0, :64]!\n"
+ "vmull.s16 q1, d5, d9\n"
+ "vld1.16 {d10, d11}, [%1, :128]!\n"
+
+ "vmlal.s16 q0, d6, d10\n"
+ "vld1.16 {d4, d5}, [%0, :64]!\n"
+ "vmlal.s16 q1, d7, d11\n"
+ "vld1.16 {d8, d9}, [%1, :128]!\n"
+
+ "vmlal.s16 q0, d4, d8\n"
+ "vld1.16 {d6, d7}, [%0, :64]!\n"
+ "vmlal.s16 q1, d5, d9\n"
+ "vld1.16 {d10, d11}, [%1, :128]!\n"
+
+ "vmlal.s16 q0, d6, d10\n"
+ "vld1.16 {d4, d5}, [%0, :64]!\n"
+ "vmlal.s16 q1, d7, d11\n"
+ "vld1.16 {d8, d9}, [%1, :128]!\n"
+
+ "vmlal.s16 q0, d4, d8\n"
+ "vmlal.s16 q1, d5, d9\n"
+
+ "vpadd.s32 d0, d0, d1\n"
+ "vpadd.s32 d1, d2, d3\n"
+
+ "vrshrn.s32 d0, q0, %3\n"
+
+ "vld1.16 {d2, d3, d4, d5}, [%1, :128]!\n"
+
+ "vdup.i32 d1, d0[1]\n" /* TODO: can be eliminated */
+ "vdup.i32 d0, d0[0]\n" /* TODO: can be eliminated */
+
+ "vmull.s16 q3, d2, d0\n"
+ "vmull.s16 q4, d3, d0\n"
+ "vmlal.s16 q3, d4, d1\n"
+ "vmlal.s16 q4, d5, d1\n"
+
+ "vpadd.s32 d0, d6, d7\n" /* TODO: can be eliminated */
+ "vpadd.s32 d1, d8, d9\n" /* TODO: can be eliminated */
+
+ "vst1.32 {d0, d1}, [%2, :128]\n"
+ : "+r" (in), "+r" (consts)
+ : "r" (out),
+ "i" (SBC_PROTO_FIXED4_SCALE)
+ : "memory",
+ "d0", "d1", "d2", "d3", "d4", "d5",
+ "d6", "d7", "d8", "d9", "d10", "d11");
+}
+
+static inline void _sbc_analyze_eight_neon(const int16_t *in, int32_t *out,
+ const FIXED_T *consts)
+{
+ /* TODO: merge even and odd cases (or even merge all four calls to this
+ * function) in order to have only aligned reads from 'in' array
+ * and reduce number of load instructions */
+ asm volatile (
+ "vld1.16 {d4, d5}, [%0, :64]!\n"
+ "vld1.16 {d8, d9}, [%1, :128]!\n"
+
+ "vmull.s16 q6, d4, d8\n"
+ "vld1.16 {d6, d7}, [%0, :64]!\n"
+ "vmull.s16 q7, d5, d9\n"
+ "vld1.16 {d10, d11}, [%1, :128]!\n"
+ "vmull.s16 q8, d6, d10\n"
+ "vld1.16 {d4, d5}, [%0, :64]!\n"
+ "vmull.s16 q9, d7, d11\n"
+ "vld1.16 {d8, d9}, [%1, :128]!\n"
+
+ "vmlal.s16 q6, d4, d8\n"
+ "vld1.16 {d6, d7}, [%0, :64]!\n"
+ "vmlal.s16 q7, d5, d9\n"
+ "vld1.16 {d10, d11}, [%1, :128]!\n"
+ "vmlal.s16 q8, d6, d10\n"
+ "vld1.16 {d4, d5}, [%0, :64]!\n"
+ "vmlal.s16 q9, d7, d11\n"
+ "vld1.16 {d8, d9}, [%1, :128]!\n"
+
+ "vmlal.s16 q6, d4, d8\n"
+ "vld1.16 {d6, d7}, [%0, :64]!\n"
+ "vmlal.s16 q7, d5, d9\n"
+ "vld1.16 {d10, d11}, [%1, :128]!\n"
+ "vmlal.s16 q8, d6, d10\n"
+ "vld1.16 {d4, d5}, [%0, :64]!\n"
+ "vmlal.s16 q9, d7, d11\n"
+ "vld1.16 {d8, d9}, [%1, :128]!\n"
+
+ "vmlal.s16 q6, d4, d8\n"
+ "vld1.16 {d6, d7}, [%0, :64]!\n"
+ "vmlal.s16 q7, d5, d9\n"
+ "vld1.16 {d10, d11}, [%1, :128]!\n"
+ "vmlal.s16 q8, d6, d10\n"
+ "vld1.16 {d4, d5}, [%0, :64]!\n"
+ "vmlal.s16 q9, d7, d11\n"
+ "vld1.16 {d8, d9}, [%1, :128]!\n"
+
+ "vmlal.s16 q6, d4, d8\n"
+ "vld1.16 {d6, d7}, [%0, :64]!\n"
+ "vmlal.s16 q7, d5, d9\n"
+ "vld1.16 {d10, d11}, [%1, :128]!\n"
+
+ "vmlal.s16 q8, d6, d10\n"
+ "vmlal.s16 q9, d7, d11\n"
+
+ "vpadd.s32 d0, d12, d13\n"
+ "vpadd.s32 d1, d14, d15\n"
+ "vpadd.s32 d2, d16, d17\n"
+ "vpadd.s32 d3, d18, d19\n"
+
+ "vrshr.s32 q0, q0, %3\n"
+ "vrshr.s32 q1, q1, %3\n"
+ "vmovn.s32 d0, q0\n"
+ "vmovn.s32 d1, q1\n"
+
+ "vdup.i32 d3, d1[1]\n" /* TODO: can be eliminated */
+ "vdup.i32 d2, d1[0]\n" /* TODO: can be eliminated */
+ "vdup.i32 d1, d0[1]\n" /* TODO: can be eliminated */
+ "vdup.i32 d0, d0[0]\n" /* TODO: can be eliminated */
+
+ "vld1.16 {d4, d5}, [%1, :128]!\n"
+ "vmull.s16 q6, d4, d0\n"
+ "vld1.16 {d6, d7}, [%1, :128]!\n"
+ "vmull.s16 q7, d5, d0\n"
+ "vmull.s16 q8, d6, d0\n"
+ "vmull.s16 q9, d7, d0\n"
+
+ "vld1.16 {d4, d5}, [%1, :128]!\n"
+ "vmlal.s16 q6, d4, d1\n"
+ "vld1.16 {d6, d7}, [%1, :128]!\n"
+ "vmlal.s16 q7, d5, d1\n"
+ "vmlal.s16 q8, d6, d1\n"
+ "vmlal.s16 q9, d7, d1\n"
+
+ "vld1.16 {d4, d5}, [%1, :128]!\n"
+ "vmlal.s16 q6, d4, d2\n"
+ "vld1.16 {d6, d7}, [%1, :128]!\n"
+ "vmlal.s16 q7, d5, d2\n"
+ "vmlal.s16 q8, d6, d2\n"
+ "vmlal.s16 q9, d7, d2\n"
+
+ "vld1.16 {d4, d5}, [%1, :128]!\n"
+ "vmlal.s16 q6, d4, d3\n"
+ "vld1.16 {d6, d7}, [%1, :128]!\n"
+ "vmlal.s16 q7, d5, d3\n"
+ "vmlal.s16 q8, d6, d3\n"
+ "vmlal.s16 q9, d7, d3\n"
+
+ "vpadd.s32 d0, d12, d13\n" /* TODO: can be eliminated */
+ "vpadd.s32 d1, d14, d15\n" /* TODO: can be eliminated */
+ "vpadd.s32 d2, d16, d17\n" /* TODO: can be eliminated */
+ "vpadd.s32 d3, d18, d19\n" /* TODO: can be eliminated */
+
+ "vst1.32 {d0, d1, d2, d3}, [%2, :128]\n"
+ : "+r" (in), "+r" (consts)
+ : "r" (out),
+ "i" (SBC_PROTO_FIXED8_SCALE)
+ : "memory",
+ "d0", "d1", "d2", "d3", "d4", "d5",
+ "d6", "d7", "d8", "d9", "d10", "d11",
+ "d12", "d13", "d14", "d15", "d16", "d17",
+ "d18", "d19");
+}
+
+static inline void sbc_analyze_4b_4s_neon(int16_t *x,
+ int32_t *out, int out_stride)
+{
+ /* Analyze blocks */
+ _sbc_analyze_four_neon(x + 12, out, analysis_consts_fixed4_simd_odd);
+ out += out_stride;
+ _sbc_analyze_four_neon(x + 8, out, analysis_consts_fixed4_simd_even);
+ out += out_stride;
+ _sbc_analyze_four_neon(x + 4, out, analysis_consts_fixed4_simd_odd);
+ out += out_stride;
+ _sbc_analyze_four_neon(x + 0, out, analysis_consts_fixed4_simd_even);
+}
+
+static inline void sbc_analyze_4b_8s_neon(int16_t *x,
+ int32_t *out, int out_stride)
+{
+ /* Analyze blocks */
+ _sbc_analyze_eight_neon(x + 24, out, analysis_consts_fixed8_simd_odd);
+ out += out_stride;
+ _sbc_analyze_eight_neon(x + 16, out, analysis_consts_fixed8_simd_even);
+ out += out_stride;
+ _sbc_analyze_eight_neon(x + 8, out, analysis_consts_fixed8_simd_odd);
+ out += out_stride;
+ _sbc_analyze_eight_neon(x + 0, out, analysis_consts_fixed8_simd_even);
+}
+
+static void sbc_calc_scalefactors_neon(
+ int32_t sb_sample_f[16][2][8],
+ uint32_t scale_factor[2][8],
+ int blocks, int channels, int subbands)
+{
+ int ch, sb;
+ for (ch = 0; ch < channels; ch++) {
+ for (sb = 0; sb < subbands; sb += 4) {
+ int blk = blocks;
+ int32_t *in = &sb_sample_f[0][ch][sb];
+ asm volatile (
+ "vmov.s32 q0, #0\n"
+ "vmov.s32 q1, %[c1]\n"
+ "vmov.s32 q14, #1\n"
+ "vmov.s32 q15, %[c2]\n"
+ "vadd.s32 q1, q1, q14\n"
+ "1:\n"
+ "vld1.32 {d16, d17}, [%[in], :128], %[inc]\n"
+ "vabs.s32 q8, q8\n"
+ "vld1.32 {d18, d19}, [%[in], :128], %[inc]\n"
+ "vabs.s32 q9, q9\n"
+ "vld1.32 {d20, d21}, [%[in], :128], %[inc]\n"
+ "vabs.s32 q10, q10\n"
+ "vld1.32 {d22, d23}, [%[in], :128], %[inc]\n"
+ "vabs.s32 q11, q11\n"
+ "vmax.s32 q0, q0, q8\n"
+ "vmax.s32 q1, q1, q9\n"
+ "vmax.s32 q0, q0, q10\n"
+ "vmax.s32 q1, q1, q11\n"
+ "subs %[blk], %[blk], #4\n"
+ "bgt 1b\n"
+ "vmax.s32 q0, q0, q1\n"
+ "vsub.s32 q0, q0, q14\n"
+ "vclz.s32 q0, q0\n"
+ "vsub.s32 q0, q15, q0\n"
+ "vst1.32 {d0, d1}, [%[out], :128]\n"
+ :
+ [blk] "+r" (blk),
+ [in] "+r" (in)
+ :
+ [inc] "r" ((char *) &sb_sample_f[1][0][0] -
+ (char *) &sb_sample_f[0][0][0]),
+ [out] "r" (&scale_factor[ch][sb]),
+ [c1] "i" (1 << SCALE_OUT_BITS),
+ [c2] "i" (31 - SCALE_OUT_BITS)
+ : "d0", "d1", "d2", "d3", "d16", "d17", "d18", "d19",
+ "d20", "d21", "d22", "d23", "d24", "d25", "d26",
+ "d27", "d28", "d29", "d30", "d31", "cc", "memory");
+ }
+ }
+}
+
+int sbc_calc_scalefactors_j_neon(
+ int32_t sb_sample_f[16][2][8],
+ uint32_t scale_factor[2][8],
+ int blocks, int subbands)
+{
+ static SBC_ALIGNED int32_t joint_bits_mask[8] = {
+ 8, 4, 2, 1, 128, 64, 32, 16
+ };
+ int joint, i;
+ int32_t *in0, *in1;
+ int32_t *in = &sb_sample_f[0][0][0];
+ uint32_t *out0, *out1;
+ uint32_t *out = &scale_factor[0][0];
+ int32_t *consts = joint_bits_mask;
+
+ i = subbands;
+
+ asm volatile (
+ /*
+ * constants: q13 = (31 - SCALE_OUT_BITS), q14 = 1
+ * input: q0 = ((1 << SCALE_OUT_BITS) + 1)
+ * %[in0] - samples for channel 0
+ * %[in1] - samples for shannel 1
+ * output: q0, q1 - scale factors without joint stereo
+ * q2, q3 - scale factors with joint stereo
+ * q15 - joint stereo selection mask
+ */
+ ".macro calc_scalefactors\n"
+ "vmov.s32 q1, q0\n"
+ "vmov.s32 q2, q0\n"
+ "vmov.s32 q3, q0\n"
+ "mov %[i], %[blocks]\n"
+ "1:\n"
+ "vld1.32 {d18, d19}, [%[in1], :128], %[inc]\n"
+ "vbic.s32 q11, q9, q14\n"
+ "vld1.32 {d16, d17}, [%[in0], :128], %[inc]\n"
+ "vhadd.s32 q10, q8, q11\n"
+ "vhsub.s32 q11, q8, q11\n"
+ "vabs.s32 q8, q8\n"
+ "vabs.s32 q9, q9\n"
+ "vabs.s32 q10, q10\n"
+ "vabs.s32 q11, q11\n"
+ "vmax.s32 q0, q0, q8\n"
+ "vmax.s32 q1, q1, q9\n"
+ "vmax.s32 q2, q2, q10\n"
+ "vmax.s32 q3, q3, q11\n"
+ "subs %[i], %[i], #1\n"
+ "bgt 1b\n"
+ "vsub.s32 q0, q0, q14\n"
+ "vsub.s32 q1, q1, q14\n"
+ "vsub.s32 q2, q2, q14\n"
+ "vsub.s32 q3, q3, q14\n"
+ "vclz.s32 q0, q0\n"
+ "vclz.s32 q1, q1\n"
+ "vclz.s32 q2, q2\n"
+ "vclz.s32 q3, q3\n"
+ "vsub.s32 q0, q13, q0\n"
+ "vsub.s32 q1, q13, q1\n"
+ "vsub.s32 q2, q13, q2\n"
+ "vsub.s32 q3, q13, q3\n"
+ ".endm\n"
+ /*
+ * constants: q14 = 1
+ * input: q15 - joint stereo selection mask
+ * %[in0] - value set by calc_scalefactors macro
+ * %[in1] - value set by calc_scalefactors macro
+ */
+ ".macro update_joint_stereo_samples\n"
+ "sub %[out1], %[in1], %[inc]\n"
+ "sub %[out0], %[in0], %[inc]\n"
+ "sub %[in1], %[in1], %[inc], asl #1\n"
+ "sub %[in0], %[in0], %[inc], asl #1\n"
+ "vld1.32 {d18, d19}, [%[in1], :128]\n"
+ "vbic.s32 q11, q9, q14\n"
+ "vld1.32 {d16, d17}, [%[in0], :128]\n"
+ "vld1.32 {d2, d3}, [%[out1], :128]\n"
+ "vbic.s32 q3, q1, q14\n"
+ "vld1.32 {d0, d1}, [%[out0], :128]\n"
+ "vhsub.s32 q10, q8, q11\n"
+ "vhadd.s32 q11, q8, q11\n"
+ "vhsub.s32 q2, q0, q3\n"
+ "vhadd.s32 q3, q0, q3\n"
+ "vbif.s32 q10, q9, q15\n"
+ "vbif.s32 d22, d16, d30\n"
+ "sub %[inc], %[zero], %[inc], asl #1\n"
+ "sub %[i], %[blocks], #2\n"
+ "2:\n"
+ "vbif.s32 d23, d17, d31\n"
+ "vst1.32 {d20, d21}, [%[in1], :128], %[inc]\n"
+ "vbif.s32 d4, d2, d30\n"
+ "vld1.32 {d18, d19}, [%[in1], :128]\n"
+ "vbif.s32 d5, d3, d31\n"
+ "vst1.32 {d22, d23}, [%[in0], :128], %[inc]\n"
+ "vbif.s32 d6, d0, d30\n"
+ "vld1.32 {d16, d17}, [%[in0], :128]\n"
+ "vbif.s32 d7, d1, d31\n"
+ "vst1.32 {d4, d5}, [%[out1], :128], %[inc]\n"
+ "vbic.s32 q11, q9, q14\n"
+ "vld1.32 {d2, d3}, [%[out1], :128]\n"
+ "vst1.32 {d6, d7}, [%[out0], :128], %[inc]\n"
+ "vbic.s32 q3, q1, q14\n"
+ "vld1.32 {d0, d1}, [%[out0], :128]\n"
+ "vhsub.s32 q10, q8, q11\n"
+ "vhadd.s32 q11, q8, q11\n"
+ "vhsub.s32 q2, q0, q3\n"
+ "vhadd.s32 q3, q0, q3\n"
+ "vbif.s32 q10, q9, q15\n"
+ "vbif.s32 d22, d16, d30\n"
+ "subs %[i], %[i], #2\n"
+ "bgt 2b\n"
+ "sub %[inc], %[zero], %[inc], asr #1\n"
+ "vbif.s32 d23, d17, d31\n"
+ "vst1.32 {d20, d21}, [%[in1], :128]\n"
+ "vbif.s32 q2, q1, q15\n"
+ "vst1.32 {d22, d23}, [%[in0], :128]\n"
+ "vbif.s32 q3, q0, q15\n"
+ "vst1.32 {d4, d5}, [%[out1], :128]\n"
+ "vst1.32 {d6, d7}, [%[out0], :128]\n"
+ ".endm\n"
+
+ "vmov.s32 q14, #1\n"
+ "vmov.s32 q13, %[c2]\n"
+
+ "cmp %[i], #4\n"
+ "bne 8f\n"
+
+ "4:\n" /* 4 subbands */
+ "add %[in0], %[in], #0\n"
+ "add %[in1], %[in], #32\n"
+ "add %[out0], %[out], #0\n"
+ "add %[out1], %[out], #32\n"
+ "vmov.s32 q0, %[c1]\n"
+ "vadd.s32 q0, q0, q14\n"
+
+ "calc_scalefactors\n"
+
+ /* check whether to use joint stereo for subbands 0, 1, 2 */
+ "vadd.s32 q15, q0, q1\n"
+ "vadd.s32 q9, q2, q3\n"
+ "vmov.s32 d31[1], %[zero]\n" /* last subband -> no joint */
+ "vld1.32 {d16, d17}, [%[consts], :128]!\n"
+ "vcgt.s32 q15, q15, q9\n"
+
+ /* calculate and save to memory 'joint' variable */
+ /* update and save scale factors to memory */
+ " vand.s32 q8, q8, q15\n"
+ "vbit.s32 q0, q2, q15\n"
+ " vpadd.s32 d16, d16, d17\n"
+ "vbit.s32 q1, q3, q15\n"
+ " vpadd.s32 d16, d16, d16\n"
+ "vst1.32 {d0, d1}, [%[out0], :128]\n"
+ "vst1.32 {d2, d3}, [%[out1], :128]\n"
+ " vst1.32 {d16[0]}, [%[joint]]\n"
+
+ "update_joint_stereo_samples\n"
+ "b 9f\n"
+
+ "8:\n" /* 8 subbands */
+ "add %[in0], %[in], #16\n\n"
+ "add %[in1], %[in], #48\n"
+ "add %[out0], %[out], #16\n\n"
+ "add %[out1], %[out], #48\n"
+ "vmov.s32 q0, %[c1]\n"
+ "vadd.s32 q0, q0, q14\n"
+
+ "calc_scalefactors\n"
+
+ /* check whether to use joint stereo for subbands 4, 5, 6 */
+ "vadd.s32 q15, q0, q1\n"
+ "vadd.s32 q9, q2, q3\n"
+ "vmov.s32 d31[1], %[zero]\n" /* last subband -> no joint */
+ "vld1.32 {d16, d17}, [%[consts], :128]!\n"
+ "vcgt.s32 q15, q15, q9\n"
+
+ /* calculate part of 'joint' variable and save it to d24 */
+ /* update and save scale factors to memory */
+ " vand.s32 q8, q8, q15\n"
+ "vbit.s32 q0, q2, q15\n"
+ " vpadd.s32 d16, d16, d17\n"
+ "vbit.s32 q1, q3, q15\n"
+ "vst1.32 {d0, d1}, [%[out0], :128]\n"
+ "vst1.32 {d2, d3}, [%[out1], :128]\n"
+ " vpadd.s32 d24, d16, d16\n"
+
+ "update_joint_stereo_samples\n"
+
+ "add %[in0], %[in], #0\n"
+ "add %[in1], %[in], #32\n"
+ "add %[out0], %[out], #0\n\n"
+ "add %[out1], %[out], #32\n"
+ "vmov.s32 q0, %[c1]\n"
+ "vadd.s32 q0, q0, q14\n"
+
+ "calc_scalefactors\n"
+
+ /* check whether to use joint stereo for subbands 0, 1, 2, 3 */
+ "vadd.s32 q15, q0, q1\n"
+ "vadd.s32 q9, q2, q3\n"
+ "vld1.32 {d16, d17}, [%[consts], :128]!\n"
+ "vcgt.s32 q15, q15, q9\n"
+
+ /* combine last part of 'joint' with d24 and save to memory */
+ /* update and save scale factors to memory */
+ " vand.s32 q8, q8, q15\n"
+ "vbit.s32 q0, q2, q15\n"
+ " vpadd.s32 d16, d16, d17\n"
+ "vbit.s32 q1, q3, q15\n"
+ " vpadd.s32 d16, d16, d16\n"
+ "vst1.32 {d0, d1}, [%[out0], :128]\n"
+ " vadd.s32 d16, d16, d24\n"
+ "vst1.32 {d2, d3}, [%[out1], :128]\n"
+ " vst1.32 {d16[0]}, [%[joint]]\n"
+
+ "update_joint_stereo_samples\n"
+ "9:\n"
+ ".purgem calc_scalefactors\n"
+ ".purgem update_joint_stereo_samples\n"
+ :
+ [i] "+&r" (i),
+ [in] "+&r" (in),
+ [in0] "=&r" (in0),
+ [in1] "=&r" (in1),
+ [out] "+&r" (out),
+ [out0] "=&r" (out0),
+ [out1] "=&r" (out1),
+ [consts] "+&r" (consts)
+ :
+ [inc] "r" ((char *) &sb_sample_f[1][0][0] -
+ (char *) &sb_sample_f[0][0][0]),
+ [blocks] "r" (blocks),
+ [joint] "r" (&joint),
+ [c1] "i" (1 << SCALE_OUT_BITS),
+ [c2] "i" (31 - SCALE_OUT_BITS),
+ [zero] "r" (0)
+ : "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
+ "d16", "d17", "d18", "d19", "d20", "d21", "d22",
+ "d23", "d24", "d25", "d26", "d27", "d28", "d29",
+ "d30", "d31", "cc", "memory");
+
+ return joint;
+}
+
+#define PERM_BE(a, b, c, d) { \
+ (a * 2) + 1, (a * 2) + 0, \
+ (b * 2) + 1, (b * 2) + 0, \
+ (c * 2) + 1, (c * 2) + 0, \
+ (d * 2) + 1, (d * 2) + 0 \
+ }
+#define PERM_LE(a, b, c, d) { \
+ (a * 2) + 0, (a * 2) + 1, \
+ (b * 2) + 0, (b * 2) + 1, \
+ (c * 2) + 0, (c * 2) + 1, \
+ (d * 2) + 0, (d * 2) + 1 \
+ }
+
+static SBC_ALWAYS_INLINE int sbc_enc_process_input_4s_neon_internal(
+ int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels, int big_endian)
+{
+ static SBC_ALIGNED uint8_t perm_be[2][8] = {
+ PERM_BE(7, 3, 6, 4),
+ PERM_BE(0, 2, 1, 5)
+ };
+ static SBC_ALIGNED uint8_t perm_le[2][8] = {
+ PERM_LE(7, 3, 6, 4),
+ PERM_LE(0, 2, 1, 5)
+ };
+ /* handle X buffer wraparound */
+ if (position < nsamples) {
+ int16_t *dst = &X[0][SBC_X_BUFFER_SIZE - 40];
+ int16_t *src = &X[0][position];
+ asm volatile (
+ "vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n"
+ "vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n"
+ "vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n"
+ "vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n"
+ "vld1.16 {d0}, [%[src], :64]!\n"
+ "vst1.16 {d0}, [%[dst], :64]!\n"
+ :
+ [dst] "+r" (dst),
+ [src] "+r" (src)
+ : : "memory", "d0", "d1", "d2", "d3");
+ if (nchannels > 1) {
+ dst = &X[1][SBC_X_BUFFER_SIZE - 40];
+ src = &X[1][position];
+ asm volatile (
+ "vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n"
+ "vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n"
+ "vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n"
+ "vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n"
+ "vld1.16 {d0}, [%[src], :64]!\n"
+ "vst1.16 {d0}, [%[dst], :64]!\n"
+ :
+ [dst] "+r" (dst),
+ [src] "+r" (src)
+ : : "memory", "d0", "d1", "d2", "d3");
+ }
+ position = SBC_X_BUFFER_SIZE - 40;
+ }
+
+ if ((nchannels > 1) && ((uintptr_t)pcm & 1)) {
+ /* poor 'pcm' alignment */
+ int16_t *x = &X[0][position];
+ int16_t *y = &X[1][position];
+ asm volatile (
+ "vld1.8 {d0, d1}, [%[perm], :128]\n"
+ "1:\n"
+ "sub %[x], %[x], #16\n"
+ "sub %[y], %[y], #16\n"
+ "sub %[position], %[position], #8\n"
+ "vld1.8 {d4, d5}, [%[pcm]]!\n"
+ "vuzp.16 d4, d5\n"
+ "vld1.8 {d20, d21}, [%[pcm]]!\n"
+ "vuzp.16 d20, d21\n"
+ "vswp d5, d20\n"
+ "vtbl.8 d16, {d4, d5}, d0\n"
+ "vtbl.8 d17, {d4, d5}, d1\n"
+ "vtbl.8 d18, {d20, d21}, d0\n"
+ "vtbl.8 d19, {d20, d21}, d1\n"
+ "vst1.16 {d16, d17}, [%[x], :128]\n"
+ "vst1.16 {d18, d19}, [%[y], :128]\n"
+ "subs %[nsamples], %[nsamples], #8\n"
+ "bgt 1b\n"
+ :
+ [x] "+r" (x),
+ [y] "+r" (y),
+ [pcm] "+r" (pcm),
+ [nsamples] "+r" (nsamples),
+ [position] "+r" (position)
+ :
+ [perm] "r" (big_endian ? perm_be : perm_le)
+ : "cc", "memory", "d0", "d1", "d2", "d3", "d4",
+ "d5", "d6", "d7", "d16", "d17", "d18", "d19",
+ "d20", "d21", "d22", "d23");
+ } else if (nchannels > 1) {
+ /* proper 'pcm' alignment */
+ int16_t *x = &X[0][position];
+ int16_t *y = &X[1][position];
+ asm volatile (
+ "vld1.8 {d0, d1}, [%[perm], :128]\n"
+ "1:\n"
+ "sub %[x], %[x], #16\n"
+ "sub %[y], %[y], #16\n"
+ "sub %[position], %[position], #8\n"
+ "vld2.16 {d4, d5}, [%[pcm]]!\n"
+ "vld2.16 {d20, d21}, [%[pcm]]!\n"
+ "vswp d5, d20\n"
+ "vtbl.8 d16, {d4, d5}, d0\n"
+ "vtbl.8 d17, {d4, d5}, d1\n"
+ "vtbl.8 d18, {d20, d21}, d0\n"
+ "vtbl.8 d19, {d20, d21}, d1\n"
+ "vst1.16 {d16, d17}, [%[x], :128]\n"
+ "vst1.16 {d18, d19}, [%[y], :128]\n"
+ "subs %[nsamples], %[nsamples], #8\n"
+ "bgt 1b\n"
+ :
+ [x] "+r" (x),
+ [y] "+r" (y),
+ [pcm] "+r" (pcm),
+ [nsamples] "+r" (nsamples),
+ [position] "+r" (position)
+ :
+ [perm] "r" (big_endian ? perm_be : perm_le)
+ : "cc", "memory", "d0", "d1", "d2", "d3", "d4",
+ "d5", "d6", "d7", "d16", "d17", "d18", "d19",
+ "d20", "d21", "d22", "d23");
+ } else {
+ int16_t *x = &X[0][position];
+ asm volatile (
+ "vld1.8 {d0, d1}, [%[perm], :128]\n"
+ "1:\n"
+ "sub %[x], %[x], #16\n"
+ "sub %[position], %[position], #8\n"
+ "vld1.8 {d4, d5}, [%[pcm]]!\n"
+ "vtbl.8 d16, {d4, d5}, d0\n"
+ "vtbl.8 d17, {d4, d5}, d1\n"
+ "vst1.16 {d16, d17}, [%[x], :128]\n"
+ "subs %[nsamples], %[nsamples], #8\n"
+ "bgt 1b\n"
+ :
+ [x] "+r" (x),
+ [pcm] "+r" (pcm),
+ [nsamples] "+r" (nsamples),
+ [position] "+r" (position)
+ :
+ [perm] "r" (big_endian ? perm_be : perm_le)
+ : "cc", "memory", "d0", "d1", "d2", "d3", "d4",
+ "d5", "d6", "d7", "d16", "d17", "d18", "d19");
+ }
+ return position;
+}
+
+static SBC_ALWAYS_INLINE int sbc_enc_process_input_8s_neon_internal(
+ int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels, int big_endian)
+{
+ static SBC_ALIGNED uint8_t perm_be[4][8] = {
+ PERM_BE(15, 7, 14, 8),
+ PERM_BE(13, 9, 12, 10),
+ PERM_BE(11, 3, 6, 0),
+ PERM_BE(5, 1, 4, 2)
+ };
+ static SBC_ALIGNED uint8_t perm_le[4][8] = {
+ PERM_LE(15, 7, 14, 8),
+ PERM_LE(13, 9, 12, 10),
+ PERM_LE(11, 3, 6, 0),
+ PERM_LE(5, 1, 4, 2)
+ };
+ /* handle X buffer wraparound */
+ if (position < nsamples) {
+ int16_t *dst = &X[0][SBC_X_BUFFER_SIZE - 72];
+ int16_t *src = &X[0][position];
+ asm volatile (
+ "vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n"
+ "vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n"
+ "vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n"
+ "vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n"
+ "vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n"
+ "vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n"
+ "vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n"
+ "vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n"
+ "vld1.16 {d0, d1}, [%[src], :128]!\n"
+ "vst1.16 {d0, d1}, [%[dst], :128]!\n"
+ :
+ [dst] "+r" (dst),
+ [src] "+r" (src)
+ : : "memory", "d0", "d1", "d2", "d3");
+ if (nchannels > 1) {
+ dst = &X[1][SBC_X_BUFFER_SIZE - 72];
+ src = &X[1][position];
+ asm volatile (
+ "vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n"
+ "vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n"
+ "vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n"
+ "vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n"
+ "vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n"
+ "vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n"
+ "vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n"
+ "vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n"
+ "vld1.16 {d0, d1}, [%[src], :128]!\n"
+ "vst1.16 {d0, d1}, [%[dst], :128]!\n"
+ :
+ [dst] "+r" (dst),
+ [src] "+r" (src)
+ : : "memory", "d0", "d1", "d2", "d3");
+ }
+ position = SBC_X_BUFFER_SIZE - 72;
+ }
+
+ if ((nchannels > 1) && ((uintptr_t)pcm & 1)) {
+ /* poor 'pcm' alignment */
+ int16_t *x = &X[0][position];
+ int16_t *y = &X[1][position];
+ asm volatile (
+ "vld1.8 {d0, d1, d2, d3}, [%[perm], :128]\n"
+ "1:\n"
+ "sub %[x], %[x], #32\n"
+ "sub %[y], %[y], #32\n"
+ "sub %[position], %[position], #16\n"
+ "vld1.8 {d4, d5, d6, d7}, [%[pcm]]!\n"
+ "vuzp.16 q2, q3\n"
+ "vld1.8 {d20, d21, d22, d23}, [%[pcm]]!\n"
+ "vuzp.16 q10, q11\n"
+ "vswp q3, q10\n"
+ "vtbl.8 d16, {d4, d5, d6, d7}, d0\n"
+ "vtbl.8 d17, {d4, d5, d6, d7}, d1\n"
+ "vtbl.8 d18, {d4, d5, d6, d7}, d2\n"
+ "vtbl.8 d19, {d4, d5, d6, d7}, d3\n"
+ "vst1.16 {d16, d17, d18, d19}, [%[x], :128]\n"
+ "vtbl.8 d16, {d20, d21, d22, d23}, d0\n"
+ "vtbl.8 d17, {d20, d21, d22, d23}, d1\n"
+ "vtbl.8 d18, {d20, d21, d22, d23}, d2\n"
+ "vtbl.8 d19, {d20, d21, d22, d23}, d3\n"
+ "vst1.16 {d16, d17, d18, d19}, [%[y], :128]\n"
+ "subs %[nsamples], %[nsamples], #16\n"
+ "bgt 1b\n"
+ :
+ [x] "+r" (x),
+ [y] "+r" (y),
+ [pcm] "+r" (pcm),
+ [nsamples] "+r" (nsamples),
+ [position] "+r" (position)
+ :
+ [perm] "r" (big_endian ? perm_be : perm_le)
+ : "cc", "memory", "d0", "d1", "d2", "d3", "d4",
+ "d5", "d6", "d7", "d16", "d17", "d18", "d19",
+ "d20", "d21", "d22", "d23");
+ } else if (nchannels > 1) {
+ /* proper 'pcm' alignment */
+ int16_t *x = &X[0][position];
+ int16_t *y = &X[1][position];
+ asm volatile (
+ "vld1.8 {d0, d1, d2, d3}, [%[perm], :128]\n"
+ "1:\n"
+ "sub %[x], %[x], #32\n"
+ "sub %[y], %[y], #32\n"
+ "sub %[position], %[position], #16\n"
+ "vld2.16 {d4, d5, d6, d7}, [%[pcm]]!\n"
+ "vld2.16 {d20, d21, d22, d23}, [%[pcm]]!\n"
+ "vswp q3, q10\n"
+ "vtbl.8 d16, {d4, d5, d6, d7}, d0\n"
+ "vtbl.8 d17, {d4, d5, d6, d7}, d1\n"
+ "vtbl.8 d18, {d4, d5, d6, d7}, d2\n"
+ "vtbl.8 d19, {d4, d5, d6, d7}, d3\n"
+ "vst1.16 {d16, d17, d18, d19}, [%[x], :128]\n"
+ "vtbl.8 d16, {d20, d21, d22, d23}, d0\n"
+ "vtbl.8 d17, {d20, d21, d22, d23}, d1\n"
+ "vtbl.8 d18, {d20, d21, d22, d23}, d2\n"
+ "vtbl.8 d19, {d20, d21, d22, d23}, d3\n"
+ "vst1.16 {d16, d17, d18, d19}, [%[y], :128]\n"
+ "subs %[nsamples], %[nsamples], #16\n"
+ "bgt 1b\n"
+ :
+ [x] "+r" (x),
+ [y] "+r" (y),
+ [pcm] "+r" (pcm),
+ [nsamples] "+r" (nsamples),
+ [position] "+r" (position)
+ :
+ [perm] "r" (big_endian ? perm_be : perm_le)
+ : "cc", "memory", "d0", "d1", "d2", "d3", "d4",
+ "d5", "d6", "d7", "d16", "d17", "d18", "d19",
+ "d20", "d21", "d22", "d23");
+ } else {
+ int16_t *x = &X[0][position];
+ asm volatile (
+ "vld1.8 {d0, d1, d2, d3}, [%[perm], :128]\n"
+ "1:\n"
+ "sub %[x], %[x], #32\n"
+ "sub %[position], %[position], #16\n"
+ "vld1.8 {d4, d5, d6, d7}, [%[pcm]]!\n"
+ "vtbl.8 d16, {d4, d5, d6, d7}, d0\n"
+ "vtbl.8 d17, {d4, d5, d6, d7}, d1\n"
+ "vtbl.8 d18, {d4, d5, d6, d7}, d2\n"
+ "vtbl.8 d19, {d4, d5, d6, d7}, d3\n"
+ "vst1.16 {d16, d17, d18, d19}, [%[x], :128]\n"
+ "subs %[nsamples], %[nsamples], #16\n"
+ "bgt 1b\n"
+ :
+ [x] "+r" (x),
+ [pcm] "+r" (pcm),
+ [nsamples] "+r" (nsamples),
+ [position] "+r" (position)
+ :
+ [perm] "r" (big_endian ? perm_be : perm_le)
+ : "cc", "memory", "d0", "d1", "d2", "d3", "d4",
+ "d5", "d6", "d7", "d16", "d17", "d18", "d19");
+ }
+ return position;
+}
+
+#undef PERM_BE
+#undef PERM_LE
+
+static int sbc_enc_process_input_4s_be_neon(int position, const uint8_t *pcm,
+ int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels)
+{
+ return sbc_enc_process_input_4s_neon_internal(
+ position, pcm, X, nsamples, nchannels, 1);
+}
+
+static int sbc_enc_process_input_4s_le_neon(int position, const uint8_t *pcm,
+ int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels)
+{
+ return sbc_enc_process_input_4s_neon_internal(
+ position, pcm, X, nsamples, nchannels, 0);
+}
+
+static int sbc_enc_process_input_8s_be_neon(int position, const uint8_t *pcm,
+ int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels)
+{
+ return sbc_enc_process_input_8s_neon_internal(
+ position, pcm, X, nsamples, nchannels, 1);
+}
+
+static int sbc_enc_process_input_8s_le_neon(int position, const uint8_t *pcm,
+ int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels)
+{
+ return sbc_enc_process_input_8s_neon_internal(
+ position, pcm, X, nsamples, nchannels, 0);
+}
+
+void sbc_init_primitives_neon(struct sbc_encoder_state *state)
+{
+ state->sbc_analyze_4b_4s = sbc_analyze_4b_4s_neon;
+ state->sbc_analyze_4b_8s = sbc_analyze_4b_8s_neon;
+ state->sbc_calc_scalefactors = sbc_calc_scalefactors_neon;
+ state->sbc_calc_scalefactors_j = sbc_calc_scalefactors_j_neon;
+ state->sbc_enc_process_input_4s_le = sbc_enc_process_input_4s_le_neon;
+ state->sbc_enc_process_input_4s_be = sbc_enc_process_input_4s_be_neon;
+ state->sbc_enc_process_input_8s_le = sbc_enc_process_input_8s_le_neon;
+ state->sbc_enc_process_input_8s_be = sbc_enc_process_input_8s_be_neon;
+ state->implementation_info = "NEON";
+}
+
+#endif
diff --git a/sbc/sbc_primitives_neon.h b/sbc/sbc_primitives_neon.h
new file mode 100644
index 0000000..ea3da06
--- /dev/null
+++ b/sbc/sbc_primitives_neon.h
@@ -0,0 +1,41 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2008-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2005 Henryk Ploetz <henryk@ploetzli.ch>
+ * Copyright (C) 2005-2006 Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef __SBC_PRIMITIVES_NEON_H
+#define __SBC_PRIMITIVES_NEON_H
+
+#include "sbc_primitives.h"
+
+#if defined(__GNUC__) && defined(__ARM_NEON__) && \
+ !defined(SBC_HIGH_PRECISION) && (SCALE_OUT_BITS == 15)
+
+#define SBC_BUILD_WITH_NEON_SUPPORT
+
+void sbc_init_primitives_neon(struct sbc_encoder_state *encoder_state);
+
+#endif
+
+#endif
diff --git a/sbc/sbc_tables.h b/sbc/sbc_tables.h
new file mode 100644
index 0000000..28c0d54
--- /dev/null
+++ b/sbc/sbc_tables.h
@@ -0,0 +1,660 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2008-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2005 Henryk Ploetz <henryk@ploetzli.ch>
+ * Copyright (C) 2005-2006 Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+/* A2DP specification: Appendix B, page 69 */
+static const int sbc_offset4[4][4] = {
+ { -1, 0, 0, 0 },
+ { -2, 0, 0, 1 },
+ { -2, 0, 0, 1 },
+ { -2, 0, 0, 1 }
+};
+
+/* A2DP specification: Appendix B, page 69 */
+static const int sbc_offset8[4][8] = {
+ { -2, 0, 0, 0, 0, 0, 0, 1 },
+ { -3, 0, 0, 0, 0, 0, 1, 2 },
+ { -4, 0, 0, 0, 0, 0, 1, 2 },
+ { -4, 0, 0, 0, 0, 0, 1, 2 }
+};
+
+
+#define SS4(val) ASR(val, SCALE_SPROTO4_TBL)
+#define SS8(val) ASR(val, SCALE_SPROTO8_TBL)
+#define SN4(val) ASR(val, SCALE_NPROTO4_TBL)
+#define SN8(val) ASR(val, SCALE_NPROTO8_TBL)
+
+static const int32_t sbc_proto_4_40m0[] = {
+ SS4(0x00000000), SS4(0xffa6982f), SS4(0xfba93848), SS4(0x0456c7b8),
+ SS4(0x005967d1), SS4(0xfffb9ac7), SS4(0xff589157), SS4(0xf9c2a8d8),
+ SS4(0x027c1434), SS4(0x0019118b), SS4(0xfff3c74c), SS4(0xff137330),
+ SS4(0xf81b8d70), SS4(0x00ec1b8b), SS4(0xfff0b71a), SS4(0xffe99b00),
+ SS4(0xfef84470), SS4(0xf6fb4370), SS4(0xffcdc351), SS4(0xffe01dc7)
+};
+
+static const int32_t sbc_proto_4_40m1[] = {
+ SS4(0xffe090ce), SS4(0xff2c0475), SS4(0xf694f800), SS4(0xff2c0475),
+ SS4(0xffe090ce), SS4(0xffe01dc7), SS4(0xffcdc351), SS4(0xf6fb4370),
+ SS4(0xfef84470), SS4(0xffe99b00), SS4(0xfff0b71a), SS4(0x00ec1b8b),
+ SS4(0xf81b8d70), SS4(0xff137330), SS4(0xfff3c74c), SS4(0x0019118b),
+ SS4(0x027c1434), SS4(0xf9c2a8d8), SS4(0xff589157), SS4(0xfffb9ac7)
+};
+
+static const int32_t sbc_proto_8_80m0[] = {
+ SS8(0x00000000), SS8(0xfe8d1970), SS8(0xee979f00), SS8(0x11686100),
+ SS8(0x0172e690), SS8(0xfff5bd1a), SS8(0xfdf1c8d4), SS8(0xeac182c0),
+ SS8(0x0d9daee0), SS8(0x00e530da), SS8(0xffe9811d), SS8(0xfd52986c),
+ SS8(0xe7054ca0), SS8(0x0a00d410), SS8(0x006c1de4), SS8(0xffdba705),
+ SS8(0xfcbc98e8), SS8(0xe3889d20), SS8(0x06af2308), SS8(0x000bb7db),
+ SS8(0xffca00ed), SS8(0xfc3fbb68), SS8(0xe071bc00), SS8(0x03bf7948),
+ SS8(0xffc4e05c), SS8(0xffb54b3b), SS8(0xfbedadc0), SS8(0xdde26200),
+ SS8(0x0142291c), SS8(0xff960e94), SS8(0xff9f3e17), SS8(0xfbd8f358),
+ SS8(0xdbf79400), SS8(0xff405e01), SS8(0xff7d4914), SS8(0xff8b1a31),
+ SS8(0xfc1417b8), SS8(0xdac7bb40), SS8(0xfdbb828c), SS8(0xff762170)
+};
+
+static const int32_t sbc_proto_8_80m1[] = {
+ SS8(0xff7c272c), SS8(0xfcb02620), SS8(0xda612700), SS8(0xfcb02620),
+ SS8(0xff7c272c), SS8(0xff762170), SS8(0xfdbb828c), SS8(0xdac7bb40),
+ SS8(0xfc1417b8), SS8(0xff8b1a31), SS8(0xff7d4914), SS8(0xff405e01),
+ SS8(0xdbf79400), SS8(0xfbd8f358), SS8(0xff9f3e17), SS8(0xff960e94),
+ SS8(0x0142291c), SS8(0xdde26200), SS8(0xfbedadc0), SS8(0xffb54b3b),
+ SS8(0xffc4e05c), SS8(0x03bf7948), SS8(0xe071bc00), SS8(0xfc3fbb68),
+ SS8(0xffca00ed), SS8(0x000bb7db), SS8(0x06af2308), SS8(0xe3889d20),
+ SS8(0xfcbc98e8), SS8(0xffdba705), SS8(0x006c1de4), SS8(0x0a00d410),
+ SS8(0xe7054ca0), SS8(0xfd52986c), SS8(0xffe9811d), SS8(0x00e530da),
+ SS8(0x0d9daee0), SS8(0xeac182c0), SS8(0xfdf1c8d4), SS8(0xfff5bd1a)
+};
+
+static const int32_t synmatrix4[8][4] = {
+ { SN4(0x05a82798), SN4(0xfa57d868), SN4(0xfa57d868), SN4(0x05a82798) },
+ { SN4(0x030fbc54), SN4(0xf89be510), SN4(0x07641af0), SN4(0xfcf043ac) },
+ { SN4(0x00000000), SN4(0x00000000), SN4(0x00000000), SN4(0x00000000) },
+ { SN4(0xfcf043ac), SN4(0x07641af0), SN4(0xf89be510), SN4(0x030fbc54) },
+ { SN4(0xfa57d868), SN4(0x05a82798), SN4(0x05a82798), SN4(0xfa57d868) },
+ { SN4(0xf89be510), SN4(0xfcf043ac), SN4(0x030fbc54), SN4(0x07641af0) },
+ { SN4(0xf8000000), SN4(0xf8000000), SN4(0xf8000000), SN4(0xf8000000) },
+ { SN4(0xf89be510), SN4(0xfcf043ac), SN4(0x030fbc54), SN4(0x07641af0) }
+};
+
+static const int32_t synmatrix8[16][8] = {
+ { SN8(0x05a82798), SN8(0xfa57d868), SN8(0xfa57d868), SN8(0x05a82798),
+ SN8(0x05a82798), SN8(0xfa57d868), SN8(0xfa57d868), SN8(0x05a82798) },
+ { SN8(0x0471ced0), SN8(0xf8275a10), SN8(0x018f8b84), SN8(0x06a6d988),
+ SN8(0xf9592678), SN8(0xfe70747c), SN8(0x07d8a5f0), SN8(0xfb8e3130) },
+ { SN8(0x030fbc54), SN8(0xf89be510), SN8(0x07641af0), SN8(0xfcf043ac),
+ SN8(0xfcf043ac), SN8(0x07641af0), SN8(0xf89be510), SN8(0x030fbc54) },
+ { SN8(0x018f8b84), SN8(0xfb8e3130), SN8(0x06a6d988), SN8(0xf8275a10),
+ SN8(0x07d8a5f0), SN8(0xf9592678), SN8(0x0471ced0), SN8(0xfe70747c) },
+ { SN8(0x00000000), SN8(0x00000000), SN8(0x00000000), SN8(0x00000000),
+ SN8(0x00000000), SN8(0x00000000), SN8(0x00000000), SN8(0x00000000) },
+ { SN8(0xfe70747c), SN8(0x0471ced0), SN8(0xf9592678), SN8(0x07d8a5f0),
+ SN8(0xf8275a10), SN8(0x06a6d988), SN8(0xfb8e3130), SN8(0x018f8b84) },
+ { SN8(0xfcf043ac), SN8(0x07641af0), SN8(0xf89be510), SN8(0x030fbc54),
+ SN8(0x030fbc54), SN8(0xf89be510), SN8(0x07641af0), SN8(0xfcf043ac) },
+ { SN8(0xfb8e3130), SN8(0x07d8a5f0), SN8(0xfe70747c), SN8(0xf9592678),
+ SN8(0x06a6d988), SN8(0x018f8b84), SN8(0xf8275a10), SN8(0x0471ced0) },
+ { SN8(0xfa57d868), SN8(0x05a82798), SN8(0x05a82798), SN8(0xfa57d868),
+ SN8(0xfa57d868), SN8(0x05a82798), SN8(0x05a82798), SN8(0xfa57d868) },
+ { SN8(0xf9592678), SN8(0x018f8b84), SN8(0x07d8a5f0), SN8(0x0471ced0),
+ SN8(0xfb8e3130), SN8(0xf8275a10), SN8(0xfe70747c), SN8(0x06a6d988) },
+ { SN8(0xf89be510), SN8(0xfcf043ac), SN8(0x030fbc54), SN8(0x07641af0),
+ SN8(0x07641af0), SN8(0x030fbc54), SN8(0xfcf043ac), SN8(0xf89be510) },
+ { SN8(0xf8275a10), SN8(0xf9592678), SN8(0xfb8e3130), SN8(0xfe70747c),
+ SN8(0x018f8b84), SN8(0x0471ced0), SN8(0x06a6d988), SN8(0x07d8a5f0) },
+ { SN8(0xf8000000), SN8(0xf8000000), SN8(0xf8000000), SN8(0xf8000000),
+ SN8(0xf8000000), SN8(0xf8000000), SN8(0xf8000000), SN8(0xf8000000) },
+ { SN8(0xf8275a10), SN8(0xf9592678), SN8(0xfb8e3130), SN8(0xfe70747c),
+ SN8(0x018f8b84), SN8(0x0471ced0), SN8(0x06a6d988), SN8(0x07d8a5f0) },
+ { SN8(0xf89be510), SN8(0xfcf043ac), SN8(0x030fbc54), SN8(0x07641af0),
+ SN8(0x07641af0), SN8(0x030fbc54), SN8(0xfcf043ac), SN8(0xf89be510) },
+ { SN8(0xf9592678), SN8(0x018f8b84), SN8(0x07d8a5f0), SN8(0x0471ced0),
+ SN8(0xfb8e3130), SN8(0xf8275a10), SN8(0xfe70747c), SN8(0x06a6d988) }
+};
+
+/* Uncomment the following line to enable high precision build of SBC encoder */
+
+/* #define SBC_HIGH_PRECISION */
+
+#ifdef SBC_HIGH_PRECISION
+#define FIXED_A int64_t /* data type for fixed point accumulator */
+#define FIXED_T int32_t /* data type for fixed point constants */
+#define SBC_FIXED_EXTRA_BITS 16
+#else
+#define FIXED_A int32_t /* data type for fixed point accumulator */
+#define FIXED_T int16_t /* data type for fixed point constants */
+#define SBC_FIXED_EXTRA_BITS 0
+#endif
+
+/* A2DP specification: Section 12.8 Tables
+ *
+ * Original values are premultiplied by 2 for better precision (that is the
+ * maximum which is possible without overflows)
+ *
+ * Note: in each block of 8 numbers sign was changed for elements 2 and 7
+ * in order to compensate the same change applied to cos_table_fixed_4
+ */
+#define SBC_PROTO_FIXED4_SCALE \
+ ((sizeof(FIXED_T) * CHAR_BIT - 1) - SBC_FIXED_EXTRA_BITS + 1)
+#define F_PROTO4(x) (FIXED_A) ((x * 2) * \
+ ((FIXED_A) 1 << (sizeof(FIXED_T) * CHAR_BIT - 1)) + 0.5)
+#define F(x) F_PROTO4(x)
+static const FIXED_T _sbc_proto_fixed4[40] = {
+ F(0.00000000E+00), F(5.36548976E-04),
+ -F(1.49188357E-03), F(2.73370904E-03),
+ F(3.83720193E-03), F(3.89205149E-03),
+ F(1.86581691E-03), F(3.06012286E-03),
+
+ F(1.09137620E-02), F(2.04385087E-02),
+ -F(2.88757392E-02), F(3.21939290E-02),
+ F(2.58767811E-02), F(6.13245186E-03),
+ -F(2.88217274E-02), F(7.76463494E-02),
+
+ F(1.35593274E-01), F(1.94987841E-01),
+ -F(2.46636662E-01), F(2.81828203E-01),
+ F(2.94315332E-01), F(2.81828203E-01),
+ F(2.46636662E-01), -F(1.94987841E-01),
+
+ -F(1.35593274E-01), -F(7.76463494E-02),
+ F(2.88217274E-02), F(6.13245186E-03),
+ F(2.58767811E-02), F(3.21939290E-02),
+ F(2.88757392E-02), -F(2.04385087E-02),
+
+ -F(1.09137620E-02), -F(3.06012286E-03),
+ -F(1.86581691E-03), F(3.89205149E-03),
+ F(3.83720193E-03), F(2.73370904E-03),
+ F(1.49188357E-03), -F(5.36548976E-04),
+};
+#undef F
+
+/*
+ * To produce this cosine matrix in Octave:
+ *
+ * b = zeros(4, 8);
+ * for i = 0:3
+ * for j = 0:7 b(i+1, j+1) = cos((i + 0.5) * (j - 2) * (pi/4))
+ * endfor
+ * endfor;
+ * printf("%.10f, ", b');
+ *
+ * Note: in each block of 8 numbers sign was changed for elements 2 and 7
+ *
+ * Change of sign for element 2 allows to replace constant 1.0 (not
+ * representable in Q15 format) with -1.0 (fine with Q15).
+ * Changed sign for element 7 allows to have more similar constants
+ * and simplify subband filter function code.
+ */
+#define SBC_COS_TABLE_FIXED4_SCALE \
+ ((sizeof(FIXED_T) * CHAR_BIT - 1) + SBC_FIXED_EXTRA_BITS)
+#define F_COS4(x) (FIXED_A) ((x) * \
+ ((FIXED_A) 1 << (sizeof(FIXED_T) * CHAR_BIT - 1)) + 0.5)
+#define F(x) F_COS4(x)
+static const FIXED_T cos_table_fixed_4[32] = {
+ F(0.7071067812), F(0.9238795325), -F(1.0000000000), F(0.9238795325),
+ F(0.7071067812), F(0.3826834324), F(0.0000000000), F(0.3826834324),
+
+ -F(0.7071067812), F(0.3826834324), -F(1.0000000000), F(0.3826834324),
+ -F(0.7071067812), -F(0.9238795325), -F(0.0000000000), -F(0.9238795325),
+
+ -F(0.7071067812), -F(0.3826834324), -F(1.0000000000), -F(0.3826834324),
+ -F(0.7071067812), F(0.9238795325), F(0.0000000000), F(0.9238795325),
+
+ F(0.7071067812), -F(0.9238795325), -F(1.0000000000), -F(0.9238795325),
+ F(0.7071067812), -F(0.3826834324), -F(0.0000000000), -F(0.3826834324),
+};
+#undef F
+
+/* A2DP specification: Section 12.8 Tables
+ *
+ * Original values are premultiplied by 4 for better precision (that is the
+ * maximum which is possible without overflows)
+ *
+ * Note: in each block of 16 numbers sign was changed for elements 4, 13, 14, 15
+ * in order to compensate the same change applied to cos_table_fixed_8
+ */
+#define SBC_PROTO_FIXED8_SCALE \
+ ((sizeof(FIXED_T) * CHAR_BIT - 1) - SBC_FIXED_EXTRA_BITS + 1)
+#define F_PROTO8(x) (FIXED_A) ((x * 2) * \
+ ((FIXED_A) 1 << (sizeof(FIXED_T) * CHAR_BIT - 1)) + 0.5)
+#define F(x) F_PROTO8(x)
+static const FIXED_T _sbc_proto_fixed8[80] = {
+ F(0.00000000E+00), F(1.56575398E-04),
+ F(3.43256425E-04), F(5.54620202E-04),
+ -F(8.23919506E-04), F(1.13992507E-03),
+ F(1.47640169E-03), F(1.78371725E-03),
+ F(2.01182542E-03), F(2.10371989E-03),
+ F(1.99454554E-03), F(1.61656283E-03),
+ F(9.02154502E-04), F(1.78805361E-04),
+ F(1.64973098E-03), F(3.49717454E-03),
+
+ F(5.65949473E-03), F(8.02941163E-03),
+ F(1.04584443E-02), F(1.27472335E-02),
+ -F(1.46525263E-02), F(1.59045603E-02),
+ F(1.62208471E-02), F(1.53184106E-02),
+ F(1.29371806E-02), F(8.85757540E-03),
+ F(2.92408442E-03), -F(4.91578024E-03),
+ -F(1.46404076E-02), F(2.61098752E-02),
+ F(3.90751381E-02), F(5.31873032E-02),
+
+ F(6.79989431E-02), F(8.29847578E-02),
+ F(9.75753918E-02), F(1.11196689E-01),
+ -F(1.23264548E-01), F(1.33264415E-01),
+ F(1.40753505E-01), F(1.45389847E-01),
+ F(1.46955068E-01), F(1.45389847E-01),
+ F(1.40753505E-01), F(1.33264415E-01),
+ F(1.23264548E-01), -F(1.11196689E-01),
+ -F(9.75753918E-02), -F(8.29847578E-02),
+
+ -F(6.79989431E-02), -F(5.31873032E-02),
+ -F(3.90751381E-02), -F(2.61098752E-02),
+ F(1.46404076E-02), -F(4.91578024E-03),
+ F(2.92408442E-03), F(8.85757540E-03),
+ F(1.29371806E-02), F(1.53184106E-02),
+ F(1.62208471E-02), F(1.59045603E-02),
+ F(1.46525263E-02), -F(1.27472335E-02),
+ -F(1.04584443E-02), -F(8.02941163E-03),
+
+ -F(5.65949473E-03), -F(3.49717454E-03),
+ -F(1.64973098E-03), -F(1.78805361E-04),
+ -F(9.02154502E-04), F(1.61656283E-03),
+ F(1.99454554E-03), F(2.10371989E-03),
+ F(2.01182542E-03), F(1.78371725E-03),
+ F(1.47640169E-03), F(1.13992507E-03),
+ F(8.23919506E-04), -F(5.54620202E-04),
+ -F(3.43256425E-04), -F(1.56575398E-04),
+};
+#undef F
+
+/*
+ * To produce this cosine matrix in Octave:
+ *
+ * b = zeros(8, 16);
+ * for i = 0:7
+ * for j = 0:15 b(i+1, j+1) = cos((i + 0.5) * (j - 4) * (pi/8))
+ * endfor endfor;
+ * printf("%.10f, ", b');
+ *
+ * Note: in each block of 16 numbers sign was changed for elements 4, 13, 14, 15
+ *
+ * Change of sign for element 4 allows to replace constant 1.0 (not
+ * representable in Q15 format) with -1.0 (fine with Q15).
+ * Changed signs for elements 13, 14, 15 allow to have more similar constants
+ * and simplify subband filter function code.
+ */
+#define SBC_COS_TABLE_FIXED8_SCALE \
+ ((sizeof(FIXED_T) * CHAR_BIT - 1) + SBC_FIXED_EXTRA_BITS)
+#define F_COS8(x) (FIXED_A) ((x) * \
+ ((FIXED_A) 1 << (sizeof(FIXED_T) * CHAR_BIT - 1)) + 0.5)
+#define F(x) F_COS8(x)
+static const FIXED_T cos_table_fixed_8[128] = {
+ F(0.7071067812), F(0.8314696123), F(0.9238795325), F(0.9807852804),
+ -F(1.0000000000), F(0.9807852804), F(0.9238795325), F(0.8314696123),
+ F(0.7071067812), F(0.5555702330), F(0.3826834324), F(0.1950903220),
+ F(0.0000000000), F(0.1950903220), F(0.3826834324), F(0.5555702330),
+
+ -F(0.7071067812), -F(0.1950903220), F(0.3826834324), F(0.8314696123),
+ -F(1.0000000000), F(0.8314696123), F(0.3826834324), -F(0.1950903220),
+ -F(0.7071067812), -F(0.9807852804), -F(0.9238795325), -F(0.5555702330),
+ -F(0.0000000000), -F(0.5555702330), -F(0.9238795325), -F(0.9807852804),
+
+ -F(0.7071067812), -F(0.9807852804), -F(0.3826834324), F(0.5555702330),
+ -F(1.0000000000), F(0.5555702330), -F(0.3826834324), -F(0.9807852804),
+ -F(0.7071067812), F(0.1950903220), F(0.9238795325), F(0.8314696123),
+ F(0.0000000000), F(0.8314696123), F(0.9238795325), F(0.1950903220),
+
+ F(0.7071067812), -F(0.5555702330), -F(0.9238795325), F(0.1950903220),
+ -F(1.0000000000), F(0.1950903220), -F(0.9238795325), -F(0.5555702330),
+ F(0.7071067812), F(0.8314696123), -F(0.3826834324), -F(0.9807852804),
+ -F(0.0000000000), -F(0.9807852804), -F(0.3826834324), F(0.8314696123),
+
+ F(0.7071067812), F(0.5555702330), -F(0.9238795325), -F(0.1950903220),
+ -F(1.0000000000), -F(0.1950903220), -F(0.9238795325), F(0.5555702330),
+ F(0.7071067812), -F(0.8314696123), -F(0.3826834324), F(0.9807852804),
+ F(0.0000000000), F(0.9807852804), -F(0.3826834324), -F(0.8314696123),
+
+ -F(0.7071067812), F(0.9807852804), -F(0.3826834324), -F(0.5555702330),
+ -F(1.0000000000), -F(0.5555702330), -F(0.3826834324), F(0.9807852804),
+ -F(0.7071067812), -F(0.1950903220), F(0.9238795325), -F(0.8314696123),
+ -F(0.0000000000), -F(0.8314696123), F(0.9238795325), -F(0.1950903220),
+
+ -F(0.7071067812), F(0.1950903220), F(0.3826834324), -F(0.8314696123),
+ -F(1.0000000000), -F(0.8314696123), F(0.3826834324), F(0.1950903220),
+ -F(0.7071067812), F(0.9807852804), -F(0.9238795325), F(0.5555702330),
+ -F(0.0000000000), F(0.5555702330), -F(0.9238795325), F(0.9807852804),
+
+ F(0.7071067812), -F(0.8314696123), F(0.9238795325), -F(0.9807852804),
+ -F(1.0000000000), -F(0.9807852804), F(0.9238795325), -F(0.8314696123),
+ F(0.7071067812), -F(0.5555702330), F(0.3826834324), -F(0.1950903220),
+ -F(0.0000000000), -F(0.1950903220), F(0.3826834324), -F(0.5555702330),
+};
+#undef F
+
+/*
+ * Enforce 16 byte alignment for the data, which is supposed to be used
+ * with SIMD optimized code.
+ */
+
+#define SBC_ALIGN_BITS 4
+#define SBC_ALIGN_MASK ((1 << (SBC_ALIGN_BITS)) - 1)
+
+#ifdef __GNUC__
+#define SBC_ALIGNED __attribute__((aligned(1 << (SBC_ALIGN_BITS))))
+#else
+#define SBC_ALIGNED
+#endif
+
+/*
+ * Constant tables for the use in SIMD optimized analysis filters
+ * Each table consists of two parts:
+ * 1. reordered "proto" table
+ * 2. reordered "cos" table
+ *
+ * Due to non-symmetrical reordering, separate tables for "even"
+ * and "odd" cases are needed
+ */
+
+static const FIXED_T SBC_ALIGNED analysis_consts_fixed4_simd_even[40 + 16] = {
+#define C0 1.0932568993
+#define C1 1.3056875580
+#define C2 1.3056875580
+#define C3 1.6772280856
+
+#define F(x) F_PROTO4(x)
+ F(0.00000000E+00 * C0), F(3.83720193E-03 * C0),
+ F(5.36548976E-04 * C1), F(2.73370904E-03 * C1),
+ F(3.06012286E-03 * C2), F(3.89205149E-03 * C2),
+ F(0.00000000E+00 * C3), -F(1.49188357E-03 * C3),
+ F(1.09137620E-02 * C0), F(2.58767811E-02 * C0),
+ F(2.04385087E-02 * C1), F(3.21939290E-02 * C1),
+ F(7.76463494E-02 * C2), F(6.13245186E-03 * C2),
+ F(0.00000000E+00 * C3), -F(2.88757392E-02 * C3),
+ F(1.35593274E-01 * C0), F(2.94315332E-01 * C0),
+ F(1.94987841E-01 * C1), F(2.81828203E-01 * C1),
+ -F(1.94987841E-01 * C2), F(2.81828203E-01 * C2),
+ F(0.00000000E+00 * C3), -F(2.46636662E-01 * C3),
+ -F(1.35593274E-01 * C0), F(2.58767811E-02 * C0),
+ -F(7.76463494E-02 * C1), F(6.13245186E-03 * C1),
+ -F(2.04385087E-02 * C2), F(3.21939290E-02 * C2),
+ F(0.00000000E+00 * C3), F(2.88217274E-02 * C3),
+ -F(1.09137620E-02 * C0), F(3.83720193E-03 * C0),
+ -F(3.06012286E-03 * C1), F(3.89205149E-03 * C1),
+ -F(5.36548976E-04 * C2), F(2.73370904E-03 * C2),
+ F(0.00000000E+00 * C3), -F(1.86581691E-03 * C3),
+#undef F
+#define F(x) F_COS4(x)
+ F(0.7071067812 / C0), F(0.9238795325 / C1),
+ -F(0.7071067812 / C0), F(0.3826834324 / C1),
+ -F(0.7071067812 / C0), -F(0.3826834324 / C1),
+ F(0.7071067812 / C0), -F(0.9238795325 / C1),
+ F(0.3826834324 / C2), -F(1.0000000000 / C3),
+ -F(0.9238795325 / C2), -F(1.0000000000 / C3),
+ F(0.9238795325 / C2), -F(1.0000000000 / C3),
+ -F(0.3826834324 / C2), -F(1.0000000000 / C3),
+#undef F
+
+#undef C0
+#undef C1
+#undef C2
+#undef C3
+};
+
+static const FIXED_T SBC_ALIGNED analysis_consts_fixed4_simd_odd[40 + 16] = {
+#define C0 1.3056875580
+#define C1 1.6772280856
+#define C2 1.0932568993
+#define C3 1.3056875580
+
+#define F(x) F_PROTO4(x)
+ F(2.73370904E-03 * C0), F(5.36548976E-04 * C0),
+ -F(1.49188357E-03 * C1), F(0.00000000E+00 * C1),
+ F(3.83720193E-03 * C2), F(1.09137620E-02 * C2),
+ F(3.89205149E-03 * C3), F(3.06012286E-03 * C3),
+ F(3.21939290E-02 * C0), F(2.04385087E-02 * C0),
+ -F(2.88757392E-02 * C1), F(0.00000000E+00 * C1),
+ F(2.58767811E-02 * C2), F(1.35593274E-01 * C2),
+ F(6.13245186E-03 * C3), F(7.76463494E-02 * C3),
+ F(2.81828203E-01 * C0), F(1.94987841E-01 * C0),
+ -F(2.46636662E-01 * C1), F(0.00000000E+00 * C1),
+ F(2.94315332E-01 * C2), -F(1.35593274E-01 * C2),
+ F(2.81828203E-01 * C3), -F(1.94987841E-01 * C3),
+ F(6.13245186E-03 * C0), -F(7.76463494E-02 * C0),
+ F(2.88217274E-02 * C1), F(0.00000000E+00 * C1),
+ F(2.58767811E-02 * C2), -F(1.09137620E-02 * C2),
+ F(3.21939290E-02 * C3), -F(2.04385087E-02 * C3),
+ F(3.89205149E-03 * C0), -F(3.06012286E-03 * C0),
+ -F(1.86581691E-03 * C1), F(0.00000000E+00 * C1),
+ F(3.83720193E-03 * C2), F(0.00000000E+00 * C2),
+ F(2.73370904E-03 * C3), -F(5.36548976E-04 * C3),
+#undef F
+#define F(x) F_COS4(x)
+ F(0.9238795325 / C0), -F(1.0000000000 / C1),
+ F(0.3826834324 / C0), -F(1.0000000000 / C1),
+ -F(0.3826834324 / C0), -F(1.0000000000 / C1),
+ -F(0.9238795325 / C0), -F(1.0000000000 / C1),
+ F(0.7071067812 / C2), F(0.3826834324 / C3),
+ -F(0.7071067812 / C2), -F(0.9238795325 / C3),
+ -F(0.7071067812 / C2), F(0.9238795325 / C3),
+ F(0.7071067812 / C2), -F(0.3826834324 / C3),
+#undef F
+
+#undef C0
+#undef C1
+#undef C2
+#undef C3
+};
+
+static const FIXED_T SBC_ALIGNED analysis_consts_fixed8_simd_even[80 + 64] = {
+#define C0 2.7906148894
+#define C1 2.4270044280
+#define C2 2.8015616024
+#define C3 3.1710363741
+#define C4 2.5377944043
+#define C5 2.4270044280
+#define C6 2.8015616024
+#define C7 3.1710363741
+
+#define F(x) F_PROTO8(x)
+ F(0.00000000E+00 * C0), F(2.01182542E-03 * C0),
+ F(1.56575398E-04 * C1), F(1.78371725E-03 * C1),
+ F(3.43256425E-04 * C2), F(1.47640169E-03 * C2),
+ F(5.54620202E-04 * C3), F(1.13992507E-03 * C3),
+ -F(8.23919506E-04 * C4), F(0.00000000E+00 * C4),
+ F(2.10371989E-03 * C5), F(3.49717454E-03 * C5),
+ F(1.99454554E-03 * C6), F(1.64973098E-03 * C6),
+ F(1.61656283E-03 * C7), F(1.78805361E-04 * C7),
+ F(5.65949473E-03 * C0), F(1.29371806E-02 * C0),
+ F(8.02941163E-03 * C1), F(1.53184106E-02 * C1),
+ F(1.04584443E-02 * C2), F(1.62208471E-02 * C2),
+ F(1.27472335E-02 * C3), F(1.59045603E-02 * C3),
+ -F(1.46525263E-02 * C4), F(0.00000000E+00 * C4),
+ F(8.85757540E-03 * C5), F(5.31873032E-02 * C5),
+ F(2.92408442E-03 * C6), F(3.90751381E-02 * C6),
+ -F(4.91578024E-03 * C7), F(2.61098752E-02 * C7),
+ F(6.79989431E-02 * C0), F(1.46955068E-01 * C0),
+ F(8.29847578E-02 * C1), F(1.45389847E-01 * C1),
+ F(9.75753918E-02 * C2), F(1.40753505E-01 * C2),
+ F(1.11196689E-01 * C3), F(1.33264415E-01 * C3),
+ -F(1.23264548E-01 * C4), F(0.00000000E+00 * C4),
+ F(1.45389847E-01 * C5), -F(8.29847578E-02 * C5),
+ F(1.40753505E-01 * C6), -F(9.75753918E-02 * C6),
+ F(1.33264415E-01 * C7), -F(1.11196689E-01 * C7),
+ -F(6.79989431E-02 * C0), F(1.29371806E-02 * C0),
+ -F(5.31873032E-02 * C1), F(8.85757540E-03 * C1),
+ -F(3.90751381E-02 * C2), F(2.92408442E-03 * C2),
+ -F(2.61098752E-02 * C3), -F(4.91578024E-03 * C3),
+ F(1.46404076E-02 * C4), F(0.00000000E+00 * C4),
+ F(1.53184106E-02 * C5), -F(8.02941163E-03 * C5),
+ F(1.62208471E-02 * C6), -F(1.04584443E-02 * C6),
+ F(1.59045603E-02 * C7), -F(1.27472335E-02 * C7),
+ -F(5.65949473E-03 * C0), F(2.01182542E-03 * C0),
+ -F(3.49717454E-03 * C1), F(2.10371989E-03 * C1),
+ -F(1.64973098E-03 * C2), F(1.99454554E-03 * C2),
+ -F(1.78805361E-04 * C3), F(1.61656283E-03 * C3),
+ -F(9.02154502E-04 * C4), F(0.00000000E+00 * C4),
+ F(1.78371725E-03 * C5), -F(1.56575398E-04 * C5),
+ F(1.47640169E-03 * C6), -F(3.43256425E-04 * C6),
+ F(1.13992507E-03 * C7), -F(5.54620202E-04 * C7),
+#undef F
+#define F(x) F_COS8(x)
+ F(0.7071067812 / C0), F(0.8314696123 / C1),
+ -F(0.7071067812 / C0), -F(0.1950903220 / C1),
+ -F(0.7071067812 / C0), -F(0.9807852804 / C1),
+ F(0.7071067812 / C0), -F(0.5555702330 / C1),
+ F(0.7071067812 / C0), F(0.5555702330 / C1),
+ -F(0.7071067812 / C0), F(0.9807852804 / C1),
+ -F(0.7071067812 / C0), F(0.1950903220 / C1),
+ F(0.7071067812 / C0), -F(0.8314696123 / C1),
+ F(0.9238795325 / C2), F(0.9807852804 / C3),
+ F(0.3826834324 / C2), F(0.8314696123 / C3),
+ -F(0.3826834324 / C2), F(0.5555702330 / C3),
+ -F(0.9238795325 / C2), F(0.1950903220 / C3),
+ -F(0.9238795325 / C2), -F(0.1950903220 / C3),
+ -F(0.3826834324 / C2), -F(0.5555702330 / C3),
+ F(0.3826834324 / C2), -F(0.8314696123 / C3),
+ F(0.9238795325 / C2), -F(0.9807852804 / C3),
+ -F(1.0000000000 / C4), F(0.5555702330 / C5),
+ -F(1.0000000000 / C4), -F(0.9807852804 / C5),
+ -F(1.0000000000 / C4), F(0.1950903220 / C5),
+ -F(1.0000000000 / C4), F(0.8314696123 / C5),
+ -F(1.0000000000 / C4), -F(0.8314696123 / C5),
+ -F(1.0000000000 / C4), -F(0.1950903220 / C5),
+ -F(1.0000000000 / C4), F(0.9807852804 / C5),
+ -F(1.0000000000 / C4), -F(0.5555702330 / C5),
+ F(0.3826834324 / C6), F(0.1950903220 / C7),
+ -F(0.9238795325 / C6), -F(0.5555702330 / C7),
+ F(0.9238795325 / C6), F(0.8314696123 / C7),
+ -F(0.3826834324 / C6), -F(0.9807852804 / C7),
+ -F(0.3826834324 / C6), F(0.9807852804 / C7),
+ F(0.9238795325 / C6), -F(0.8314696123 / C7),
+ -F(0.9238795325 / C6), F(0.5555702330 / C7),
+ F(0.3826834324 / C6), -F(0.1950903220 / C7),
+#undef F
+
+#undef C0
+#undef C1
+#undef C2
+#undef C3
+#undef C4
+#undef C5
+#undef C6
+#undef C7
+};
+
+static const FIXED_T SBC_ALIGNED analysis_consts_fixed8_simd_odd[80 + 64] = {
+#define C0 2.5377944043
+#define C1 2.4270044280
+#define C2 2.8015616024
+#define C3 3.1710363741
+#define C4 2.7906148894
+#define C5 2.4270044280
+#define C6 2.8015616024
+#define C7 3.1710363741
+
+#define F(x) F_PROTO8(x)
+ F(0.00000000E+00 * C0), -F(8.23919506E-04 * C0),
+ F(1.56575398E-04 * C1), F(1.78371725E-03 * C1),
+ F(3.43256425E-04 * C2), F(1.47640169E-03 * C2),
+ F(5.54620202E-04 * C3), F(1.13992507E-03 * C3),
+ F(2.01182542E-03 * C4), F(5.65949473E-03 * C4),
+ F(2.10371989E-03 * C5), F(3.49717454E-03 * C5),
+ F(1.99454554E-03 * C6), F(1.64973098E-03 * C6),
+ F(1.61656283E-03 * C7), F(1.78805361E-04 * C7),
+ F(0.00000000E+00 * C0), -F(1.46525263E-02 * C0),
+ F(8.02941163E-03 * C1), F(1.53184106E-02 * C1),
+ F(1.04584443E-02 * C2), F(1.62208471E-02 * C2),
+ F(1.27472335E-02 * C3), F(1.59045603E-02 * C3),
+ F(1.29371806E-02 * C4), F(6.79989431E-02 * C4),
+ F(8.85757540E-03 * C5), F(5.31873032E-02 * C5),
+ F(2.92408442E-03 * C6), F(3.90751381E-02 * C6),
+ -F(4.91578024E-03 * C7), F(2.61098752E-02 * C7),
+ F(0.00000000E+00 * C0), -F(1.23264548E-01 * C0),
+ F(8.29847578E-02 * C1), F(1.45389847E-01 * C1),
+ F(9.75753918E-02 * C2), F(1.40753505E-01 * C2),
+ F(1.11196689E-01 * C3), F(1.33264415E-01 * C3),
+ F(1.46955068E-01 * C4), -F(6.79989431E-02 * C4),
+ F(1.45389847E-01 * C5), -F(8.29847578E-02 * C5),
+ F(1.40753505E-01 * C6), -F(9.75753918E-02 * C6),
+ F(1.33264415E-01 * C7), -F(1.11196689E-01 * C7),
+ F(0.00000000E+00 * C0), F(1.46404076E-02 * C0),
+ -F(5.31873032E-02 * C1), F(8.85757540E-03 * C1),
+ -F(3.90751381E-02 * C2), F(2.92408442E-03 * C2),
+ -F(2.61098752E-02 * C3), -F(4.91578024E-03 * C3),
+ F(1.29371806E-02 * C4), -F(5.65949473E-03 * C4),
+ F(1.53184106E-02 * C5), -F(8.02941163E-03 * C5),
+ F(1.62208471E-02 * C6), -F(1.04584443E-02 * C6),
+ F(1.59045603E-02 * C7), -F(1.27472335E-02 * C7),
+ F(0.00000000E+00 * C0), -F(9.02154502E-04 * C0),
+ -F(3.49717454E-03 * C1), F(2.10371989E-03 * C1),
+ -F(1.64973098E-03 * C2), F(1.99454554E-03 * C2),
+ -F(1.78805361E-04 * C3), F(1.61656283E-03 * C3),
+ F(2.01182542E-03 * C4), F(0.00000000E+00 * C4),
+ F(1.78371725E-03 * C5), -F(1.56575398E-04 * C5),
+ F(1.47640169E-03 * C6), -F(3.43256425E-04 * C6),
+ F(1.13992507E-03 * C7), -F(5.54620202E-04 * C7),
+#undef F
+#define F(x) F_COS8(x)
+ -F(1.0000000000 / C0), F(0.8314696123 / C1),
+ -F(1.0000000000 / C0), -F(0.1950903220 / C1),
+ -F(1.0000000000 / C0), -F(0.9807852804 / C1),
+ -F(1.0000000000 / C0), -F(0.5555702330 / C1),
+ -F(1.0000000000 / C0), F(0.5555702330 / C1),
+ -F(1.0000000000 / C0), F(0.9807852804 / C1),
+ -F(1.0000000000 / C0), F(0.1950903220 / C1),
+ -F(1.0000000000 / C0), -F(0.8314696123 / C1),
+ F(0.9238795325 / C2), F(0.9807852804 / C3),
+ F(0.3826834324 / C2), F(0.8314696123 / C3),
+ -F(0.3826834324 / C2), F(0.5555702330 / C3),
+ -F(0.9238795325 / C2), F(0.1950903220 / C3),
+ -F(0.9238795325 / C2), -F(0.1950903220 / C3),
+ -F(0.3826834324 / C2), -F(0.5555702330 / C3),
+ F(0.3826834324 / C2), -F(0.8314696123 / C3),
+ F(0.9238795325 / C2), -F(0.9807852804 / C3),
+ F(0.7071067812 / C4), F(0.5555702330 / C5),
+ -F(0.7071067812 / C4), -F(0.9807852804 / C5),
+ -F(0.7071067812 / C4), F(0.1950903220 / C5),
+ F(0.7071067812 / C4), F(0.8314696123 / C5),
+ F(0.7071067812 / C4), -F(0.8314696123 / C5),
+ -F(0.7071067812 / C4), -F(0.1950903220 / C5),
+ -F(0.7071067812 / C4), F(0.9807852804 / C5),
+ F(0.7071067812 / C4), -F(0.5555702330 / C5),
+ F(0.3826834324 / C6), F(0.1950903220 / C7),
+ -F(0.9238795325 / C6), -F(0.5555702330 / C7),
+ F(0.9238795325 / C6), F(0.8314696123 / C7),
+ -F(0.3826834324 / C6), -F(0.9807852804 / C7),
+ -F(0.3826834324 / C6), F(0.9807852804 / C7),
+ F(0.9238795325 / C6), -F(0.8314696123 / C7),
+ -F(0.9238795325 / C6), F(0.5555702330 / C7),
+ F(0.3826834324 / C6), -F(0.1950903220 / C7),
+#undef F
+
+#undef C0
+#undef C1
+#undef C2
+#undef C3
+#undef C4
+#undef C5
+#undef C6
+#undef C7
+};
diff --git a/sbc/sbcdec.c b/sbc/sbcdec.c
new file mode 100644
index 0000000..7008e88
--- /dev/null
+++ b/sbc/sbcdec.c
@@ -0,0 +1,293 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) decoder
+ *
+ * Copyright (C) 2008-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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 <getopt.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/soundcard.h>
+
+#include "sbc.h"
+#include "formats.h"
+
+#define BUF_SIZE 8192
+
+static int verbose = 0;
+
+static void decode(char *filename, char *output, int tofile)
+{
+ unsigned char buf[BUF_SIZE], *stream;
+ struct stat st;
+ sbc_t sbc;
+ int fd, ad, pos, streamlen, framelen, count;
+ size_t len;
+ int format = AFMT_S16_BE, frequency, channels;
+ ssize_t written;
+
+ if (stat(filename, &st) < 0) {
+ fprintf(stderr, "Can't get size of file %s: %s\n",
+ filename, strerror(errno));
+ return;
+ }
+
+ stream = malloc(st.st_size);
+
+ if (!stream) {
+ fprintf(stderr, "Can't allocate memory for %s: %s\n",
+ filename, strerror(errno));
+ return;
+ }
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "Can't open file %s: %s\n",
+ filename, strerror(errno));
+ goto free;
+ }
+
+ if (read(fd, stream, st.st_size) != st.st_size) {
+ fprintf(stderr, "Can't read content of %s: %s\n",
+ filename, strerror(errno));
+ close(fd);
+ goto free;
+ }
+
+ close(fd);
+
+ pos = 0;
+ streamlen = st.st_size;
+
+ if (tofile)
+ ad = open(output, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ else
+ ad = open(output, O_WRONLY, 0);
+
+ if (ad < 0) {
+ fprintf(stderr, "Can't open output %s: %s\n",
+ output, strerror(errno));
+ goto free;
+ }
+
+ sbc_init(&sbc, 0L);
+ sbc.endian = SBC_BE;
+
+ framelen = sbc_decode(&sbc, stream, streamlen, buf, sizeof(buf), &len);
+ channels = sbc.mode == SBC_MODE_MONO ? 1 : 2;
+ switch (sbc.frequency) {
+ case SBC_FREQ_16000:
+ frequency = 16000;
+ break;
+
+ case SBC_FREQ_32000:
+ frequency = 32000;
+ break;
+
+ case SBC_FREQ_44100:
+ frequency = 44100;
+ break;
+
+ case SBC_FREQ_48000:
+ frequency = 48000;
+ break;
+ default:
+ frequency = 0;
+ }
+
+ if (verbose) {
+ fprintf(stderr,"decoding %s with rate %d, %d subbands, "
+ "%d bits, allocation method %s and mode %s\n",
+ filename, frequency, sbc.subbands * 4 + 4, sbc.bitpool,
+ sbc.allocation == SBC_AM_SNR ? "SNR" : "LOUDNESS",
+ sbc.mode == SBC_MODE_MONO ? "MONO" :
+ sbc.mode == SBC_MODE_STEREO ?
+ "STEREO" : "JOINTSTEREO");
+ }
+
+ if (tofile) {
+ struct au_header au_hdr;
+
+ au_hdr.magic = AU_MAGIC;
+ au_hdr.hdr_size = BE_INT(24);
+ au_hdr.data_size = BE_INT(0);
+ au_hdr.encoding = BE_INT(AU_FMT_LIN16);
+ au_hdr.sample_rate = BE_INT(frequency);
+ au_hdr.channels = BE_INT(channels);
+
+ written = write(ad, &au_hdr, sizeof(au_hdr));
+ if (written < (ssize_t) sizeof(au_hdr)) {
+ fprintf(stderr, "Failed to write header\n");
+ goto close;
+ }
+ } else {
+ if (ioctl(ad, SNDCTL_DSP_SETFMT, &format) < 0) {
+ fprintf(stderr, "Can't set audio format on %s: %s\n",
+ output, strerror(errno));
+ goto close;
+ }
+
+ if (ioctl(ad, SNDCTL_DSP_CHANNELS, &channels) < 0) {
+ fprintf(stderr, "Can't set number of channels on %s: %s\n",
+ output, strerror(errno));
+ goto close;
+ }
+
+ if (ioctl(ad, SNDCTL_DSP_SPEED, &frequency) < 0) {
+ fprintf(stderr, "Can't set audio rate on %s: %s\n",
+ output, strerror(errno));
+ goto close;
+ }
+ }
+
+ count = len;
+
+ while (framelen > 0) {
+ /* we have completed an sbc_decode at this point sbc.len is the
+ * length of the frame we just decoded count is the number of
+ * decoded bytes yet to be written */
+
+ if (count + len >= BUF_SIZE) {
+ /* buffer is too full to stuff decoded audio in so it
+ * must be written to the device */
+ written = write(ad, buf, count);
+ if (written > 0)
+ count -= written;
+ }
+
+ /* sanity check */
+ if (count + len >= BUF_SIZE) {
+ fprintf(stderr,
+ "buffer size of %d is too small for decoded"
+ " data (%lu)\n", BUF_SIZE, (unsigned long) (len + count));
+ exit(1);
+ }
+
+ /* push the pointer in the file forward to the next bit to be
+ * decoded tell the decoder to decode up to the remaining
+ * length of the file (!) */
+ pos += framelen;
+ framelen = sbc_decode(&sbc, stream + pos, streamlen - pos,
+ buf + count, sizeof(buf) - count, &len);
+
+ /* increase the count */
+ count += len;
+ }
+
+ if (count > 0) {
+ written = write(ad, buf, count);
+ if (written > 0)
+ count -= written;
+ }
+
+close:
+ sbc_finish(&sbc);
+
+ close(ad);
+
+free:
+ free(stream);
+}
+
+static void usage(void)
+{
+ printf("SBC decoder utility ver %s\n", VERSION);
+ printf("Copyright (c) 2004-2010 Marcel Holtmann\n\n");
+
+ printf("Usage:\n"
+ "\tsbcdec [options] file(s)\n"
+ "\n");
+
+ printf("Options:\n"
+ "\t-h, --help Display help\n"
+ "\t-v, --verbose Verbose mode\n"
+ "\t-d, --device <dsp> Sound device\n"
+ "\t-f, --file <file> Decode to a file\n"
+ "\n");
+}
+
+static struct option main_options[] = {
+ { "help", 0, 0, 'h' },
+ { "device", 1, 0, 'd' },
+ { "verbose", 0, 0, 'v' },
+ { "file", 1, 0, 'f' },
+ { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ char *output = NULL;
+ int i, opt, tofile = 0;
+
+ while ((opt = getopt_long(argc, argv, "+hvd:f:",
+ main_options, NULL)) != -1) {
+ switch(opt) {
+ case 'h':
+ usage();
+ exit(0);
+
+ case 'v':
+ verbose = 1;
+ break;
+
+ case 'd':
+ free(output);
+ output = strdup(optarg);
+ tofile = 0;
+ break;
+
+ case 'f' :
+ free(output);
+ output = strdup(optarg);
+ tofile = 1;
+ break;
+
+ default:
+ exit(1);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ if (argc < 1) {
+ usage();
+ exit(1);
+ }
+
+ for (i = 0; i < argc; i++)
+ decode(argv[i], output ? output : "/dev/dsp", tofile);
+
+ free(output);
+
+ return 0;
+}
diff --git a/sbc/sbcenc.c b/sbc/sbcenc.c
new file mode 100644
index 0000000..3d3a7dc
--- /dev/null
+++ b/sbc/sbcenc.c
@@ -0,0 +1,308 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) encoder
+ *
+ * Copyright (C) 2008-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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 <stdint.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/stat.h>
+
+#include "sbc.h"
+#include "formats.h"
+
+static int verbose = 0;
+
+#define BUF_SIZE 32768
+static unsigned char input[BUF_SIZE], output[BUF_SIZE + BUF_SIZE / 4];
+
+static void encode(char *filename, int subbands, int bitpool, int joint,
+ int dualchannel, int snr, int blocks)
+{
+ struct au_header au_hdr;
+ sbc_t sbc;
+ int fd, size, srate, codesize, nframes;
+ ssize_t encoded;
+ ssize_t len;
+
+ if (sizeof(au_hdr) != 24) {
+ /* Sanity check just in case */
+ fprintf(stderr, "FIXME: sizeof(au_hdr) != 24\n");
+ return;
+ }
+
+ if (strcmp(filename, "-")) {
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "Can't open file %s: %s\n",
+ filename, strerror(errno));
+ return;
+ }
+ } else
+ fd = fileno(stdin);
+
+ len = read(fd, &au_hdr, sizeof(au_hdr));
+ if (len < (ssize_t) sizeof(au_hdr)) {
+ if (fd > fileno(stderr))
+ fprintf(stderr, "Can't read header from file %s: %s\n",
+ filename, strerror(errno));
+ else
+ perror("Can't read audio header");
+ goto done;
+ }
+
+ if (au_hdr.magic != AU_MAGIC ||
+ BE_INT(au_hdr.hdr_size) > 128 ||
+ BE_INT(au_hdr.hdr_size) < sizeof(au_hdr) ||
+ BE_INT(au_hdr.encoding) != AU_FMT_LIN16) {
+ fprintf(stderr, "Not in Sun/NeXT audio S16_BE format\n");
+ goto done;
+ }
+
+ sbc_init(&sbc, 0L);
+
+ switch (BE_INT(au_hdr.sample_rate)) {
+ case 16000:
+ sbc.frequency = SBC_FREQ_16000;
+ break;
+ case 32000:
+ sbc.frequency = SBC_FREQ_32000;
+ break;
+ case 44100:
+ sbc.frequency = SBC_FREQ_44100;
+ break;
+ case 48000:
+ sbc.frequency = SBC_FREQ_48000;
+ break;
+ }
+
+ srate = BE_INT(au_hdr.sample_rate);
+
+ sbc.subbands = subbands == 4 ? SBC_SB_4 : SBC_SB_8;
+
+ if (BE_INT(au_hdr.channels) == 1) {
+ sbc.mode = SBC_MODE_MONO;
+ if (joint || dualchannel) {
+ fprintf(stderr, "Audio is mono but joint or "
+ "dualchannel mode has been specified\n");
+ goto done;
+ }
+ } else if (joint && !dualchannel)
+ sbc.mode = SBC_MODE_JOINT_STEREO;
+ else if (!joint && dualchannel)
+ sbc.mode = SBC_MODE_DUAL_CHANNEL;
+ else if (!joint && !dualchannel)
+ sbc.mode = SBC_MODE_STEREO;
+ else {
+ fprintf(stderr, "Both joint and dualchannel mode have been "
+ "specified\n");
+ goto done;
+ }
+
+ sbc.endian = SBC_BE;
+ /* Skip extra bytes of the header if any */
+ if (read(fd, input, BE_INT(au_hdr.hdr_size) - len) < 0)
+ goto done;
+
+ sbc.bitpool = bitpool;
+ sbc.allocation = snr ? SBC_AM_SNR : SBC_AM_LOUDNESS;
+ sbc.blocks = blocks == 4 ? SBC_BLK_4 :
+ blocks == 8 ? SBC_BLK_8 :
+ blocks == 12 ? SBC_BLK_12 : SBC_BLK_16;
+
+ if (verbose) {
+ fprintf(stderr, "encoding %s with rate %d, %d blocks, "
+ "%d subbands, %d bits, allocation method %s, "
+ "and mode %s\n",
+ filename, srate, blocks, subbands, bitpool,
+ sbc.allocation == SBC_AM_SNR ? "SNR" : "LOUDNESS",
+ sbc.mode == SBC_MODE_MONO ? "MONO" :
+ sbc.mode == SBC_MODE_STEREO ?
+ "STEREO" : "JOINTSTEREO");
+ }
+
+ codesize = sbc_get_codesize(&sbc);
+ nframes = sizeof(input) / codesize;
+ while (1) {
+ unsigned char *inp, *outp;
+ /* read data for up to 'nframes' frames of input data */
+ size = read(fd, input, codesize * nframes);
+ if (size < 0) {
+ /* Something really bad happened */
+ perror("Can't read audio data");
+ break;
+ }
+ if (size < codesize) {
+ /* Not enough data for encoding even a single frame */
+ break;
+ }
+ /* encode all the data from the input buffer in a loop */
+ inp = input;
+ outp = output;
+ while (size >= codesize) {
+ len = sbc_encode(&sbc, inp, codesize,
+ outp, sizeof(output) - (outp - output),
+ &encoded);
+ if (len != codesize || encoded <= 0) {
+ fprintf(stderr,
+ "sbc_encode fail, len=%zd, encoded=%lu\n",
+ len, (unsigned long) encoded);
+ break;
+ }
+ size -= len;
+ inp += len;
+ outp += encoded;
+ }
+ len = write(fileno(stdout), output, outp - output);
+ if (len != outp - output) {
+ perror("Can't write SBC output");
+ break;
+ }
+ if (size != 0) {
+ /*
+ * sbc_encode failure has been detected earlier or end
+ * of file reached (have trailing partial data which is
+ * insufficient to encode SBC frame)
+ */
+ break;
+ }
+ }
+
+ sbc_finish(&sbc);
+
+done:
+ if (fd > fileno(stderr))
+ close(fd);
+}
+
+static void usage(void)
+{
+ printf("SBC encoder utility ver %s\n", VERSION);
+ printf("Copyright (c) 2004-2010 Marcel Holtmann\n\n");
+
+ printf("Usage:\n"
+ "\tsbcenc [options] file(s)\n"
+ "\n");
+
+ printf("Options:\n"
+ "\t-h, --help Display help\n"
+ "\t-v, --verbose Verbose mode\n"
+ "\t-s, --subbands Number of subbands to use (4 or 8)\n"
+ "\t-b, --bitpool Bitpool value (default is 32)\n"
+ "\t-j, --joint Joint stereo\n"
+ "\t-d, --dualchannel Dual channel\n"
+ "\t-S, --snr Use SNR mode (default is loudness)\n"
+ "\t-B, --blocks Number of blocks (4, 8, 12 or 16)\n"
+ "\n");
+}
+
+static struct option main_options[] = {
+ { "help", 0, 0, 'h' },
+ { "verbose", 0, 0, 'v' },
+ { "subbands", 1, 0, 's' },
+ { "bitpool", 1, 0, 'b' },
+ { "joint", 0, 0, 'j' },
+ { "dualchannel",0, 0, 'd' },
+ { "snr", 0, 0, 'S' },
+ { "blocks", 1, 0, 'B' },
+ { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ int i, opt, subbands = 8, bitpool = 32, joint = 0, dualchannel = 0;
+ int snr = 0, blocks = 16;
+
+ while ((opt = getopt_long(argc, argv, "+hvs:b:jdSB:",
+ main_options, NULL)) != -1) {
+ switch(opt) {
+ case 'h':
+ usage();
+ exit(0);
+
+ case 'v':
+ verbose = 1;
+ break;
+
+ case 's':
+ subbands = atoi(optarg);
+ if (subbands != 8 && subbands != 4) {
+ fprintf(stderr, "Invalid subbands\n");
+ exit(1);
+ }
+ break;
+
+ case 'b':
+ bitpool = atoi(optarg);
+ break;
+
+ case 'j':
+ joint = 1;
+ break;
+
+ case 'd':
+ dualchannel = 1;
+ break;
+
+ case 'S':
+ snr = 1;
+ break;
+
+ case 'B':
+ blocks = atoi(optarg);
+ if (blocks != 16 && blocks != 12 &&
+ blocks != 8 && blocks != 4) {
+ fprintf(stderr, "Invalid blocks\n");
+ exit(1);
+ }
+ break;
+
+ default:
+ usage();
+ exit(1);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ if (argc < 1) {
+ usage();
+ exit(1);
+ }
+
+ for (i = 0; i < argc; i++)
+ encode(argv[i], subbands, bitpool, joint, dualchannel,
+ snr, blocks);
+
+ return 0;
+}
diff --git a/sbc/sbcinfo.c b/sbc/sbcinfo.c
new file mode 100644
index 0000000..6d92679
--- /dev/null
+++ b/sbc/sbcinfo.c
@@ -0,0 +1,322 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2008-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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 <stdint.h>
+#include <string.h>
+#include <libgen.h>
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+struct sbc_frame_hdr {
+ uint8_t syncword:8; /* Sync word */
+ uint8_t subbands:1; /* Subbands */
+ uint8_t allocation_method:1; /* Allocation method */
+ uint8_t channel_mode:2; /* Channel mode */
+ uint8_t blocks:2; /* Blocks */
+ uint8_t sampling_frequency:2; /* Sampling frequency */
+ uint8_t bitpool:8; /* Bitpool */
+ uint8_t crc_check:8; /* CRC check */
+} __attribute__ ((packed));
+#elif __BYTE_ORDER == __BIG_ENDIAN
+struct sbc_frame_hdr {
+ uint8_t syncword:8; /* Sync word */
+ uint8_t sampling_frequency:2; /* Sampling frequency */
+ uint8_t blocks:2; /* Blocks */
+ uint8_t channel_mode:2; /* Channel mode */
+ uint8_t allocation_method:1; /* Allocation method */
+ uint8_t subbands:1; /* Subbands */
+ uint8_t bitpool:8; /* Bitpool */
+ uint8_t crc_check:8; /* CRC check */
+} __attribute__ ((packed));
+#else
+#error "Unknown byte order"
+#endif
+
+static int calc_frame_len(struct sbc_frame_hdr *hdr)
+{
+ int tmp, nrof_subbands, nrof_blocks;
+
+ nrof_subbands = (hdr->subbands + 1) * 4;
+ nrof_blocks = (hdr->blocks + 1) * 4;
+
+ switch (hdr->channel_mode) {
+ case 0x00:
+ nrof_subbands /= 2;
+ tmp = nrof_blocks * hdr->bitpool;
+ break;
+ case 0x01:
+ tmp = nrof_blocks * hdr->bitpool * 2;
+ break;
+ case 0x02:
+ tmp = nrof_blocks * hdr->bitpool;
+ break;
+ case 0x03:
+ tmp = nrof_blocks * hdr->bitpool + nrof_subbands;
+ break;
+ default:
+ return 0;
+ }
+
+ return (nrof_subbands + ((tmp + 7) / 8));
+}
+
+static double calc_bit_rate(struct sbc_frame_hdr *hdr)
+{
+ int nrof_subbands, nrof_blocks;
+ double f;
+
+ nrof_subbands = (hdr->subbands + 1) * 4;
+ nrof_blocks = (hdr->blocks + 1) * 4;
+
+ switch (hdr->sampling_frequency) {
+ case 0:
+ f = 16;
+ break;
+ case 1:
+ f = 32;
+ break;
+ case 2:
+ f = 44.1;
+ break;
+ case 3:
+ f = 48;
+ break;
+ default:
+ return 0;
+ }
+
+ return ((8 * (calc_frame_len(hdr) + 4) * f) /
+ (nrof_subbands * nrof_blocks));
+}
+
+static char *freq2str(uint8_t freq)
+{
+ switch (freq) {
+ case 0:
+ return "16 kHz";
+ case 1:
+ return "32 kHz";
+ case 2:
+ return "44.1 kHz";
+ case 3:
+ return "48 kHz";
+ default:
+ return "Unknown";
+ }
+}
+
+static char *mode2str(uint8_t mode)
+{
+ switch (mode) {
+ case 0:
+ return "Mono";
+ case 1:
+ return "Dual Channel";
+ case 2:
+ return "Stereo";
+ case 3:
+ return "Joint Stereo";
+ default:
+ return "Unknown";
+ }
+}
+
+static ssize_t __read(int fd, void *buf, size_t count)
+{
+ ssize_t len, pos = 0;
+
+ while (count > 0) {
+ len = read(fd, buf + pos, count);
+ if (len <= 0)
+ return len;
+
+ count -= len;
+ pos += len;
+ }
+
+ return pos;
+}
+
+#define SIZE 32
+
+static int analyze_file(char *filename)
+{
+ struct sbc_frame_hdr hdr;
+ unsigned char buf[64];
+ double rate;
+ int bitpool[SIZE], frame_len[SIZE];
+ int subbands, blocks, freq, mode, method;
+ int n, p1, p2, fd, size, num;
+ ssize_t len;
+ unsigned int count;
+
+ if (strcmp(filename, "-")) {
+ printf("Filename\t\t%s\n", basename(filename));
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ perror("Can't open file");
+ return -1;
+ }
+ } else
+ fd = fileno(stdin);
+
+ len = __read(fd, &hdr, sizeof(hdr));
+ if (len != sizeof(hdr) || hdr.syncword != 0x9c) {
+ fprintf(stderr, "Not a SBC audio file\n");
+ return -1;
+ }
+
+ subbands = (hdr.subbands + 1) * 4;
+ blocks = (hdr.blocks + 1) * 4;
+ freq = hdr.sampling_frequency;
+ mode = hdr.channel_mode;
+ method = hdr.allocation_method;
+
+ count = calc_frame_len(&hdr);
+
+ bitpool[0] = hdr.bitpool;
+ frame_len[0] = count + 4;
+
+ for (n = 1; n < SIZE; n++) {
+ bitpool[n] = 0;
+ frame_len[n] = 0;
+ }
+
+ if (lseek(fd, 0, SEEK_SET) < 0) {
+ num = 1;
+ rate = calc_bit_rate(&hdr);
+ while (count) {
+ size = count > sizeof(buf) ? sizeof(buf) : count;
+ len = __read(fd, buf, size);
+ if (len < 0)
+ break;
+ count -= len;
+ }
+ } else {
+ num = 0;
+ rate = 0;
+ }
+
+ while (1) {
+ len = __read(fd, &hdr, sizeof(hdr));
+ if (len < 0) {
+ fprintf(stderr, "Unable to read frame header"
+ " (error %d)\n", errno);
+ break;
+ }
+
+ if (len == 0)
+ break;
+
+ if ((size_t) len < sizeof(hdr) || hdr.syncword != 0x9c) {
+ fprintf(stderr, "Corrupted SBC stream "
+ "(len %zd syncword 0x%02x)\n",
+ len, hdr.syncword);
+ break;
+ }
+
+ count = calc_frame_len(&hdr);
+ len = count + 4;
+
+ p1 = -1;
+ p2 = -1;
+ for (n = 0; n < SIZE; n++) {
+ if (p1 < 0 && (bitpool[n] == 0 || bitpool[n] == hdr.bitpool))
+ p1 = n;
+ if (p2 < 0 && (frame_len[n] == 0 || frame_len[n] == len))
+ p2 = n;
+ }
+ if (p1 >= 0)
+ bitpool[p1] = hdr.bitpool;
+ if (p2 >= 0)
+ frame_len[p2] = len;
+
+ while (count) {
+ size = count > sizeof(buf) ? sizeof(buf) : count;
+
+ len = __read(fd, buf, size);
+ if (len != size) {
+ fprintf(stderr, "Unable to read frame data "
+ "(error %d)\n", errno);
+ break;
+ }
+
+ count -= len;
+ }
+
+ rate += calc_bit_rate(&hdr);
+ num++;
+ }
+
+ printf("Subbands\t\t%d\n", subbands);
+ printf("Block length\t\t%d\n", blocks);
+ printf("Sampling frequency\t%s\n", freq2str(freq));
+ printf("Channel mode\t\t%s\n", mode2str(hdr.channel_mode));
+ printf("Allocation method\t%s\n", method ? "SNR" : "Loudness");
+ printf("Bitpool\t\t\t%d", bitpool[0]);
+ for (n = 1; n < SIZE; n++)
+ if (bitpool[n] > 0)
+ printf(", %d", bitpool[n]);
+ printf("\n");
+ printf("Number of frames\t%d\n", num);
+ printf("Frame length\t\t%d", frame_len[0]);
+ for (n = 1; n < SIZE; n++)
+ if (frame_len[n] > 0)
+ printf(", %d", frame_len[n]);
+ printf(" Bytes\n");
+ if (num > 0)
+ printf("Bit rate\t\t%.3f kbps\n", rate / num);
+
+ if (fd > fileno(stderr))
+ close(fd);
+
+ printf("\n");
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int i;
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: sbcinfo <file>\n");
+ exit(1);
+ }
+
+ for (i = 0; i < argc - 1; i++)
+ if (analyze_file(argv[i + 1]) < 0)
+ exit(1);
+
+ return 0;
+}
diff --git a/sbc/sbctester.c b/sbc/sbctester.c
new file mode 100644
index 0000000..b1e3608
--- /dev/null
+++ b/sbc/sbctester.c
@@ -0,0 +1,358 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2008-2010 Nokia Corporation
+ * Copyright (C) 2007-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2007-2008 Frederic Dalleau <fdalleau@free.fr>
+ *
+ *
+ * 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 <stdlib.h>
+#include <sndfile.h>
+#include <math.h>
+#include <string.h>
+
+#define MAXCHANNELS 2
+#define DEFACCURACY 7
+
+static double sampletobits(short sample16, int verbose)
+{
+ double bits = 0;
+ unsigned short bit;
+ int i;
+
+ if (verbose)
+ printf("===> sampletobits(%hd, %04hX)\n", sample16, sample16);
+
+ /* Bit 0 is MSB */
+ if (sample16 < 0)
+ bits = -1;
+
+ if (verbose)
+ printf("%d", (sample16 < 0) ? 1 : 0);
+
+ /* Bit 15 is LSB */
+ for (i = 1; i < 16; i++) {
+ bit = (unsigned short) sample16;
+ bit >>= 15 - i;
+ bit %= 2;
+
+ if (verbose)
+ printf("%d", bit);
+
+ if (bit)
+ bits += (1.0 / pow(2.0, i));
+ }
+
+ if (verbose)
+ printf("\n");
+
+ return bits;
+}
+
+static int calculate_rms_level(SNDFILE * sndref, SF_INFO * infosref,
+ SNDFILE * sndtst, SF_INFO * infostst,
+ int accuracy, char *csvname)
+{
+ short refsample[MAXCHANNELS], tstsample[MAXCHANNELS];
+ double refbits, tstbits;
+ double rms_accu[MAXCHANNELS];
+ double rms_level[MAXCHANNELS];
+ double rms_limit = 1.0 / (pow(2.0, accuracy - 1) * pow(12.0, 0.5));
+ FILE *csv = NULL;
+ int i, j, r1, r2, verdict;
+
+ if (csvname)
+ csv = fopen(csvname, "wt");
+
+ if (csv) {
+ fprintf(csv, "num;");
+ for (j = 0; j < infostst->channels; j++)
+ fprintf(csv, "ref channel %d;tst channel %d;", j, j);
+ fprintf(csv, "\r\n");
+ }
+
+ sf_seek(sndref, 0, SEEK_SET);
+ sf_seek(sndtst, 0, SEEK_SET);
+
+ memset(rms_accu, 0, sizeof(rms_accu));
+ memset(rms_level, 0, sizeof(rms_level));
+
+ for (i = 0; i < infostst->frames; i++) {
+ if (csv)
+ fprintf(csv, "%d;", i);
+
+ r1 = sf_read_short(sndref, refsample, infostst->channels);
+ if (r1 != infostst->channels) {
+ printf("Failed to read reference data: %s "
+ "(r1=%d, channels=%d)",
+ sf_strerror(sndref), r1,
+ infostst->channels);
+ if (csv)
+ fclose(csv);
+ return -1;
+ }
+
+ r2 = sf_read_short(sndtst, tstsample, infostst->channels);
+ if (r2 != infostst->channels) {
+ printf("Failed to read test data: %s "
+ "(r2=%d, channels=%d)\n",
+ sf_strerror(sndtst), r2,
+ infostst->channels);
+ if (csv)
+ fclose(csv);
+ return -1;
+ }
+
+ for (j = 0; j < infostst->channels; j++) {
+ if (csv)
+ fprintf(csv, "%d;%d;", refsample[j],
+ tstsample[j]);
+
+ refbits = sampletobits(refsample[j], 0);
+ tstbits = sampletobits(tstsample[j], 0);
+
+ rms_accu[j] += pow(tstbits - refbits, 2.0);
+ }
+
+ if (csv)
+ fprintf(csv, "\r\n");
+ }
+
+ printf("Limit: %f\n", rms_limit);
+
+ for (j = 0; j < infostst->channels; j++) {
+ printf("Channel %d\n", j);
+ printf("Accumulated %f\n", rms_accu[j]);
+ rms_accu[j] /= (double) infostst->frames;
+ printf("Accumulated / %f = %f\n", (double) infostst->frames,
+ rms_accu[j]);
+ rms_level[j] = sqrt(rms_accu[j]);
+ printf("Level = %f (%f x %f = %f)\n",
+ rms_level[j], rms_level[j], rms_level[j],
+ rms_level[j] * rms_level[j]);
+ }
+
+ verdict = 1;
+
+ for (j = 0; j < infostst->channels; j++) {
+ printf("Channel %d: %f\n", j, rms_level[j]);
+
+ if (rms_level[j] > rms_limit)
+ verdict = 0;
+ }
+
+ printf("%s return %d\n", __FUNCTION__, verdict);
+
+ return verdict;
+}
+
+static int check_absolute_diff(SNDFILE * sndref, SF_INFO * infosref,
+ SNDFILE * sndtst, SF_INFO * infostst,
+ int accuracy)
+{
+ short refsample[MAXCHANNELS], tstsample[MAXCHANNELS];
+ short refmax[MAXCHANNELS], tstmax[MAXCHANNELS];
+ double refbits, tstbits;
+ double rms_absolute = 1.0 / (pow(2, accuracy - 2));
+ double calc_max[MAXCHANNELS];
+ int calc_count = 0;
+ short r1, r2;
+ double cur_diff;
+ int i, j, verdict;
+
+ memset(&refmax, 0, sizeof(refmax));
+ memset(&tstmax, 0, sizeof(tstmax));
+ memset(&calc_max, 0, sizeof(calc_max));
+ memset(&refsample, 0, sizeof(refsample));
+ memset(&tstsample, 0, sizeof(tstsample));
+
+ sf_seek(sndref, 0, SEEK_SET);
+ sf_seek(sndtst, 0, SEEK_SET);
+
+ verdict = 1;
+
+ printf("Absolute max: %f\n", rms_absolute);
+ for (i = 0; i < infostst->frames; i++) {
+ r1 = sf_read_short(sndref, refsample, infostst->channels);
+
+ if (r1 != infostst->channels) {
+ printf("Failed to read reference data: %s "
+ "(r1=%d, channels=%d)",
+ sf_strerror(sndref), r1,
+ infostst->channels);
+ return -1;
+ }
+
+ r2 = sf_read_short(sndtst, tstsample, infostst->channels);
+ if (r2 != infostst->channels) {
+ printf("Failed to read test data: %s "
+ "(r2=%d, channels=%d)\n",
+ sf_strerror(sndtst), r2,
+ infostst->channels);
+ return -1;
+ }
+
+ for (j = 0; j < infostst->channels; j++) {
+ refbits = sampletobits(refsample[j], 0);
+ tstbits = sampletobits(tstsample[j], 0);
+
+ cur_diff = fabs(tstbits - refbits);
+
+ if (cur_diff > rms_absolute) {
+ calc_count++;
+ /* printf("Channel %d exceeded : fabs(%f - %f) = %f > %f\n", j, tstbits, refbits, cur_diff, rms_absolute); */
+ verdict = 0;
+ }
+
+ if (cur_diff > calc_max[j]) {
+ calc_max[j] = cur_diff;
+ refmax[j] = refsample[j];
+ tstmax[j] = tstsample[j];
+ }
+ }
+ }
+
+ for (j = 0; j < infostst->channels; j++) {
+ printf("Calculated max: %f (%hd-%hd=%hd)\n",
+ calc_max[j], tstmax[j], refmax[j],
+ tstmax[j] - refmax[j]);
+ }
+
+ printf("%s return %d\n", __FUNCTION__, verdict);
+
+ return verdict;
+}
+
+static void usage()
+{
+ printf("SBC conformance test ver %s\n", VERSION);
+ printf("Copyright (c) 2007-2010 Marcel Holtmann\n");
+ printf("Copyright (c) 2007-2008 Frederic Dalleau\n\n");
+
+ printf("Usage:\n"
+ "\tsbctester reference.wav checkfile.wav\n"
+ "\tsbctester integer\n"
+ "\n");
+
+ printf("To test the encoder:\n");
+ printf("\tUse a reference codec to encode original.wav to reference.sbc\n");
+ printf("\tUse sbcenc to encode original.wav to checkfile.sbc\n");
+ printf("\tDecode both file using the reference decoder\n");
+ printf("\tRun sbctester with these two wav files to get the result\n\n");
+
+ printf("\tA file called out.csv is generated to use the data in a\n");
+ printf("\tspreadsheet application or database.\n\n");
+}
+
+int main(int argc, char *argv[])
+{
+ SNDFILE *sndref = NULL;
+ SNDFILE *sndtst = NULL;
+ SF_INFO infosref;
+ SF_INFO infostst;
+ char *ref;
+ char *tst;
+ int pass_rms, pass_absolute, pass, accuracy;
+
+ if (argc == 2) {
+ double db;
+
+ printf("Test sampletobits\n");
+ db = sampletobits((short) atoi(argv[1]), 1);
+ printf("db = %f\n", db);
+ exit(0);
+ }
+
+ if (argc < 3) {
+ usage();
+ exit(1);
+ }
+
+ ref = argv[1];
+ tst = argv[2];
+
+ printf("opening reference %s\n", ref);
+
+ sndref = sf_open(ref, SFM_READ, &infosref);
+ if (!sndref) {
+ printf("Failed to open reference file\n");
+ exit(1);
+ }
+
+ printf("opening testfile %s\n", tst);
+ sndtst = sf_open(tst, SFM_READ, &infostst);
+ if (!sndtst) {
+ printf("Failed to open test file\n");
+ sf_close(sndref);
+ exit(1);
+ }
+
+ printf("reference:\n\t%d frames,\n\t%d hz,\n\t%d channels\n",
+ (int) infosref.frames, (int) infosref.samplerate,
+ (int) infosref.channels);
+ printf("testfile:\n\t%d frames,\n\t%d hz,\n\t%d channels\n",
+ (int) infostst.frames, (int) infostst.samplerate,
+ (int) infostst.channels);
+
+ /* check number of channels */
+ if (infosref.channels > 2 || infostst.channels > 2) {
+ printf("Too many channels\n");
+ goto error;
+ }
+
+ /* compare number of samples */
+ if (infosref.samplerate != infostst.samplerate ||
+ infosref.channels != infostst.channels) {
+ printf("Cannot compare files with different charasteristics\n");
+ goto error;
+ }
+
+ accuracy = DEFACCURACY;
+ printf("Accuracy: %d\n", accuracy);
+
+ /* Condition 1 rms level */
+ pass_rms = calculate_rms_level(sndref, &infosref, sndtst, &infostst,
+ accuracy, "out.csv");
+ if (pass_rms < 0)
+ goto error;
+
+ /* Condition 2 absolute difference */
+ pass_absolute = check_absolute_diff(sndref, &infosref, sndtst,
+ &infostst, accuracy);
+ if (pass_absolute < 0)
+ goto error;
+
+ /* Verdict */
+ pass = pass_rms && pass_absolute;
+ printf("Verdict: %s\n", pass ? "pass" : "fail");
+
+ return 0;
+
+error:
+ sf_close(sndref);
+ sf_close(sndtst);
+
+ exit(1);
+}
diff --git a/scripts/bluetooth-hid2hci.rules b/scripts/bluetooth-hid2hci.rules
new file mode 100644
index 0000000..1b231d1
--- /dev/null
+++ b/scripts/bluetooth-hid2hci.rules
@@ -0,0 +1,36 @@
+# Variety of Dell Bluetooth devices
+#
+# it looks like a bit of an odd rule, because it is matching
+# on a mouse device that is self powered, but that is where
+# a HID report needs to be sent to switch modes.
+#
+# Known supported devices:
+# 413c:8154
+# 413c:8158
+# 413c:8162
+ACTION=="add", ENV{ID_VENDOR}=="413c", ENV{ID_CLASS}=="mouse", ATTRS{bmAttributes}=="e0", KERNEL=="mouse*", RUN+="/usr/sbin/hid2hci --method dell -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+
+# Logitech devices
+ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c703" RUN+="/usr/sbin/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c704" RUN+="/usr/sbin/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c705" RUN+="/usr/sbin/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c70a" RUN+="/usr/sbin/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c70b" RUN+="/usr/sbin/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c70c" RUN+="/usr/sbin/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c70e" RUN+="/usr/sbin/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c713" RUN+="/usr/sbin/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c714" RUN+="/usr/sbin/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c71b" RUN+="/usr/sbin/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c71c" RUN+="/usr/sbin/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+
+# CSR devices (in HID mode)
+ACTION=="add", ENV{ID_VENDOR}=="0a12", ENV{ID_MODEL}=="1000" RUN+="/usr/sbin/hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ACTION=="add", ENV{ID_VENDOR}=="0458", ENV{ID_MODEL}=="1000" RUN+="/usr/sbin/hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ACTION=="add", ENV{ID_VENDOR}=="05ac", ENV{ID_MODEL}=="1000" RUN+="/usr/sbin/hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+
+# CSR devices (in HCI mode)
+#ACTION=="add", ENV{ID_VENDOR}=="0a12", ENV{ID_MODEL}=="0001" RUN+="/usr/sbin/hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hid"
+#ACTION=="add", ENV{ID_VENDOR}=="0458", ENV{ID_MODEL}=="003f" RUN+="/usr/sbin/hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hid"
+#ACTION=="add", ENV{ID_VENDOR}=="05ac", ENV{ID_MODEL}=="8203" RUN+="/usr/sbin/hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hid"
+#ACTION=="add", ENV{ID_VENDOR}=="05ac", ENV{ID_MODEL}=="8204" RUN+="/usr/sbin/hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hid"
+#ACTION=="add", ENV{ID_VENDOR}=="05ac", ENV{ID_MODEL}=="8207" RUN+="/usr/sbin/hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hid"
diff --git a/scripts/bluetooth-serial.rules b/scripts/bluetooth-serial.rules
new file mode 100644
index 0000000..072335f
--- /dev/null
+++ b/scripts/bluetooth-serial.rules
@@ -0,0 +1,35 @@
+# Brain Boxes BL-620 Bluetooth Adapter
+SUBSYSTEM=="tty", SUBSYSTEMS=="pcmcia", ATTRS{prod_id1}=="Brain Boxes", ATTRS{prod_id2}=="Bluetooth PC Card", ENV{HCIOPTS}="bboxes", RUN+="bluetooth_serial"
+
+# Xircom CreditCard Bluetooth Adapter
+SUBSYSTEM=="tty", SUBSYSTEMS=="pcmcia", ATTRS{prod_id1}=="Xircom", ATTRS{prod_id3}=="CBT", ENV{HCIOPTS}="xircom", RUN+="bluetooth_serial"
+
+# Xircom RealPort2 Bluetooth Adapter
+SUBSYSTEM=="tty", SUBSYSTEMS=="pcmcia", ATTRS{prod_id1}=="Xircom", ATTRS{prod_id3}=="CBT", ENV{HCIOPTS}="xircom", RUN+="bluetooth_serial"
+
+# IBM Bluetooth PC Card II
+SUBSYSTEM=="tty", SUBSYSTEMS=="pcmcia", ATTRS{prod_id1}=="IBM", ATTRS{prod_id2}=="Bluetooth PC Card II", ENV{HCIOPTS}="tdk", RUN+="bluetooth_serial"
+
+# TDK Bluetooth PC Card
+SUBSYSTEM=="tty", SUBSYSTEMS=="pcmcia", ATTRS{prod_id1}=="TDK", ATTRS{prod_id2}=="Bluetooth PC Card II", ENV{HCIOPTS}="tdk", RUN+="bluetooth_serial"
+
+# AmbiCom BT2000C Bluetooth PC/CF Card
+SUBSYSTEM=="tty", SUBSYSTEMS=="pcmcia", ATTRS{prod_id1}=="AmbiCom BT2000C", ATTRS{prod_id2}=="Bluetooth PC/CF Card", ENV{HCIOPTS}="bt2000c", RUN+="bluetooth_serial"
+
+# COM One Platinium Bluetooth PC Card
+SUBSYSTEM=="tty", SUBSYSTEMS=="pcmcia", ATTRS{prod_id1}=="COM1 SA", ATTRS{prod_id2}=="MC310 CARD", ENV{HCIOPTS}="comone", RUN+="bluetooth_serial"
+
+# Sphinx PICO Card
+SUBSYSTEM=="tty", SUBSYSTEMS=="pcmcia", ATTRS{prod_id1}=="SPHINX", ATTRS{prod_id2}=="BT-CARD", ENV{HCIOPTS}="picocard", RUN+="bluetooth_serial"
+
+# H-Soft blue+Card
+SUBSYSTEM=="tty", SUBSYSTEMS=="pcmcia", ATTRS{prod_id1}=="H-Soft", ATTRS{prod_id2}=="Blue+CARD", ENV{HCIOPTS}="$sysfs{manf_id},$sysfs{card_id}", RUN+="bluetooth_serial"
+
+# Compaq iPAQ Bluetooth Sleeve, Belkin F8T020, any other muppet who used an OXCF950 and didn't bother to program it appropriately.
+SUBSYSTEM=="tty", SUBSYSTEMS=="pcmcia", ATTRS{prod_id1}=="CF CARD", ATTRS{prod_id2}=="GENERIC", ENV{HCIOPTS}="$sysfs{manf_id},$sysfs{card_id}", RUN+="bluetooth_serial"
+
+# Zoom Bluetooth Card and Sitecom CN-504 Card
+SUBSYSTEM=="tty", SUBSYSTEMS=="pcmcia", ATTRS{prod_id1}=="PCMCIA", ATTRS{prod_id2}=="Bluetooth Card", ENV{HCIOPTS}="zoom", RUN+="bluetooth_serial"
+
+# CC&C BT0100M
+SUBSYSTEM=="tty", SUBSYSTEMS=="pcmcia", ATTRS{prod_id1}=="Bluetooth BT0100M", ENV{HCIOPTS}="bcsp 115200", RUN+="bluetooth_serial"
diff --git a/scripts/bluetooth.rules b/scripts/bluetooth.rules
new file mode 100644
index 0000000..a67c1ad
--- /dev/null
+++ b/scripts/bluetooth.rules
@@ -0,0 +1,4 @@
+# Run helper every time a Bluetooth device appears
+# On remove actions, bluetoothd should go away by itself
+ACTION=="add", SUBSYSTEM=="bluetooth", RUN+="/usr/local/sbin/bluetoothd --udev"
+ACTION=="change", SUBSYSTEM=="bluetooth", RUN+="/usr/local/sbin/bluetoothd --udev"
diff --git a/scripts/bluetooth.rules.in b/scripts/bluetooth.rules.in
new file mode 100644
index 0000000..64df69d
--- /dev/null
+++ b/scripts/bluetooth.rules.in
@@ -0,0 +1,4 @@
+# Run helper every time a Bluetooth device appears
+# On remove actions, bluetoothd should go away by itself
+ACTION=="add", SUBSYSTEM=="bluetooth", RUN+="@prefix@/sbin/bluetoothd --udev"
+ACTION=="change", SUBSYSTEM=="bluetooth", RUN+="@prefix@/sbin/bluetoothd --udev"
diff --git a/scripts/bluetooth_serial b/scripts/bluetooth_serial
new file mode 100644
index 0000000..e5be6c2
--- /dev/null
+++ b/scripts/bluetooth_serial
@@ -0,0 +1,39 @@
+#!/bin/sh
+#
+# bluetooth_serial
+#
+# Bluetooth serial PCMCIA card initialization
+#
+
+start_serial()
+{
+ if [ ! -x /bin/setserial -o ! -x /usr/sbin/hciattach ]; then
+ logger "$0: setserial or hciattach not executable, cannot start $DEVNAME"
+ return 1
+ fi
+
+ if [ "$BAUDBASE" != "" ]; then
+ /bin/setserial $DEVNAME baud_base $BAUDBASE
+ fi
+
+ /usr/sbin/hciattach $DEVNAME $HCIOPTS 2>&1 | logger -t hciattach
+}
+
+stop_serial()
+{
+ [ -x /bin/fuser ] || return 1
+
+ /bin/fuser -k -HUP $DEVNAME > /dev/null
+}
+
+case "$ACTION" in
+ add)
+ start_serial
+ ;;
+ remove)
+ stop_serial
+ ;;
+ *)
+ logger "Unknown action received $0: $ACTION"
+ ;;
+esac
diff --git a/serial/main.c b/serial/main.c
new file mode 100644
index 0000000..38ded03
--- /dev/null
+++ b/serial/main.c
@@ -0,0 +1,59 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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 <errno.h>
+
+#include <gdbus.h>
+
+#include "plugin.h"
+#include "manager.h"
+
+static DBusConnection *connection;
+
+static int serial_init(void)
+{
+ connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+ if (connection == NULL)
+ return -EIO;
+
+ if (serial_manager_init(connection) < 0) {
+ dbus_connection_unref(connection);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void serial_exit(void)
+{
+ serial_manager_exit();
+
+ dbus_connection_unref(connection);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(serial, VERSION,
+ BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, serial_init, serial_exit)
diff --git a/serial/manager.c b/serial/manager.c
new file mode 100644
index 0000000..eba8a91
--- /dev/null
+++ b/serial/manager.c
@@ -0,0 +1,176 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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 <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+#include <bluetooth/rfcomm.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "../src/dbus-common.h"
+#include "adapter.h"
+#include "device.h"
+
+#include "log.h"
+#include "textfile.h"
+
+#include "error.h"
+#include "port.h"
+#include "proxy.h"
+#include "storage.h"
+#include "manager.h"
+#include "sdpd.h"
+#include "glib-helper.h"
+
+#define RFCOMM_UUID_STR "00000003-0000-1000-8000-00805F9B34FB"
+
+static DBusConnection *connection = NULL;
+
+static int serial_probe(struct btd_device *device, const char *uuid)
+{
+ struct btd_adapter *adapter = device_get_adapter(device);
+ const gchar *path = device_get_path(device);
+ sdp_list_t *protos;
+ int ch;
+ bdaddr_t src, dst;
+ const sdp_record_t *rec;
+
+ DBG("path %s: %s", path, uuid);
+
+ rec = btd_device_get_record(device, uuid);
+ if (!rec)
+ return -EINVAL;
+
+ if (sdp_get_access_protos(rec, &protos) < 0)
+ return -EINVAL;
+
+ ch = sdp_get_proto_port(protos, RFCOMM_UUID);
+ sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL);
+ sdp_list_free(protos, NULL);
+
+ if (ch < 1 || ch > 30) {
+ error("Channel out of range: %d", ch);
+ return -EINVAL;
+ }
+
+ adapter_get_address(adapter, &src);
+ device_get_address(device, &dst);
+
+ return port_register(connection, path, &src, &dst, uuid, ch);
+}
+
+static void serial_remove(struct btd_device *device)
+{
+ const gchar *path = device_get_path(device);
+
+ DBG("path %s", path);
+
+ port_unregister(path);
+}
+
+
+static int port_probe(struct btd_device *device, GSList *uuids)
+{
+ while (uuids) {
+ serial_probe(device, uuids->data);
+ uuids = uuids->next;
+ }
+
+ return 0;
+}
+
+static void port_remove(struct btd_device *device)
+{
+ return serial_remove(device);
+}
+
+static struct btd_device_driver serial_port_driver = {
+ .name = "serial-port",
+ .uuids = BTD_UUIDS(RFCOMM_UUID_STR),
+ .probe = port_probe,
+ .remove = port_remove,
+};
+
+static int proxy_probe(struct btd_adapter *adapter)
+{
+ const char *path = adapter_get_path(adapter);
+
+ DBG("path %s", path);
+
+ return proxy_register(connection, adapter);
+}
+
+static void proxy_remove(struct btd_adapter *adapter)
+{
+ const char *path = adapter_get_path(adapter);
+
+ DBG("path %s", path);
+
+ proxy_unregister(adapter);
+}
+
+static struct btd_adapter_driver serial_proxy_driver = {
+ .name = "serial-proxy",
+ .probe = proxy_probe,
+ .remove = proxy_remove,
+};
+
+int serial_manager_init(DBusConnection *conn)
+{
+ connection = dbus_connection_ref(conn);
+
+ btd_register_adapter_driver(&serial_proxy_driver);
+ btd_register_device_driver(&serial_port_driver);
+
+ return 0;
+}
+
+void serial_manager_exit(void)
+{
+ btd_unregister_device_driver(&serial_port_driver);
+
+ dbus_connection_unref(connection);
+ connection = NULL;
+}
diff --git a/serial/manager.h b/serial/manager.h
new file mode 100644
index 0000000..c8b96e8
--- /dev/null
+++ b/serial/manager.h
@@ -0,0 +1,25 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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
+ *
+ */
+
+int serial_manager_init(DBusConnection *conn);
+void serial_manager_exit(void);
diff --git a/serial/port.c b/serial/port.c
new file mode 100644
index 0000000..7d56faa
--- /dev/null
+++ b/serial/port.c
@@ -0,0 +1,622 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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 <errno.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "../src/dbus-common.h"
+
+#include "log.h"
+#include "glib-helper.h"
+#include "btio.h"
+
+#include "error.h"
+#include "manager.h"
+#include "adapter.h"
+#include "device.h"
+#include "storage.h"
+#include "port.h"
+
+#define SERIAL_PORT_INTERFACE "org.bluez.Serial"
+
+#define MAX_OPEN_TRIES 5
+#define OPEN_WAIT 300 /* ms. udev node creation retry wait */
+
+struct serial_device {
+ DBusConnection *conn; /* for name listener handling */
+ bdaddr_t src; /* Source (local) address */
+ bdaddr_t dst; /* Destination address */
+ char *path; /* Device path */
+ GSList *ports; /* Available ports */
+};
+
+struct serial_port {
+ DBusMessage *msg; /* for name listener handling */
+ int16_t id; /* RFCOMM device id */
+ uint8_t channel; /* RFCOMM channel */
+ char *uuid; /* service identification */
+ char *dev; /* RFCOMM device name */
+ int fd; /* Opened file descriptor */
+ GIOChannel *io; /* BtIO channel */
+ guint listener_id;
+ struct serial_device *device;
+};
+
+static GSList *devices = NULL;
+
+static struct serial_device *find_device(GSList *devices, const char *path)
+{
+ GSList *l;
+
+ for (l = devices; l != NULL; l = l->next) {
+ struct serial_device *device = l->data;
+
+ if (!strcmp(device->path, path))
+ return device;
+ }
+
+ return NULL;
+}
+
+static struct serial_port *find_port(GSList *ports, const char *pattern)
+{
+ GSList *l;
+ int channel;
+ char *endptr = NULL;
+
+ channel = strtol(pattern, &endptr, 10);
+
+ for (l = ports; l != NULL; l = l->next) {
+ struct serial_port *port = l->data;
+ char *uuid_str;
+ int ret;
+
+ if (port->uuid && !strcasecmp(port->uuid, pattern))
+ return port;
+
+ if (endptr && *endptr == '\0' && port->channel == channel)
+ return port;
+
+ if (port->dev && !strcmp(port->dev, pattern))
+ return port;
+
+ if (!port->uuid)
+ continue;
+
+ uuid_str = bt_name2string(pattern);
+ if (!uuid_str)
+ continue;
+
+ ret = strcasecmp(port->uuid, uuid_str);
+ g_free(uuid_str);
+ if (ret == 0)
+ return port;
+ }
+
+ return NULL;
+}
+
+static int port_release(struct serial_port *port)
+{
+ struct rfcomm_dev_req req;
+ int rfcomm_ctl;
+ int err = 0;
+
+ if (port->id < 0) {
+ if (port->io) {
+ g_io_channel_shutdown(port->io, TRUE, NULL);
+ g_io_channel_unref(port->io);
+ port->io = NULL;
+ } else
+ bt_cancel_discovery(&port->device->src,
+ &port->device->dst);
+
+ return 0;
+ }
+
+ DBG("Serial port %s released", port->dev);
+
+ rfcomm_ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_RFCOMM);
+ if (rfcomm_ctl < 0)
+ return -errno;
+
+ if (port->fd >= 0) {
+ close(port->fd);
+ port->fd = -1;
+ }
+
+ memset(&req, 0, sizeof(req));
+ req.dev_id = port->id;
+
+ /*
+ * We are hitting a kernel bug inside RFCOMM code when
+ * RFCOMM_HANGUP_NOW bit is set on request's flags passed to
+ * ioctl(RFCOMMRELEASEDEV)!
+ */
+ req.flags = (1 << RFCOMM_HANGUP_NOW);
+
+ if (ioctl(rfcomm_ctl, RFCOMMRELEASEDEV, &req) < 0) {
+ err = errno;
+ error("Can't release device %s: %s (%d)",
+ port->dev, strerror(err), err);
+ }
+
+ g_free(port->dev);
+ port->dev = NULL;
+ port->id = -1;
+ close(rfcomm_ctl);
+ return -err;
+}
+
+static void serial_port_free(struct serial_port *port)
+{
+ struct serial_device *device = port->device;
+
+ if (device && port->listener_id > 0)
+ g_dbus_remove_watch(device->conn, port->listener_id);
+
+ port_release(port);
+
+ g_free(port->uuid);
+ g_free(port);
+}
+
+static void serial_device_free(struct serial_device *device)
+{
+ g_free(device->path);
+ if (device->conn)
+ dbus_connection_unref(device->conn);
+ g_free(device);
+}
+
+static void port_owner_exited(DBusConnection *conn, void *user_data)
+{
+ struct serial_port *port = user_data;
+
+ port_release(port);
+
+ port->listener_id = 0;
+}
+
+static void path_unregister(void *data)
+{
+ struct serial_device *device = data;
+
+ DBG("Unregistered interface %s on path %s", SERIAL_PORT_INTERFACE,
+ device->path);
+
+ devices = g_slist_remove(devices, device);
+ serial_device_free(device);
+}
+
+void port_release_all(void)
+{
+ g_slist_foreach(devices, (GFunc) serial_device_free, NULL);
+ g_slist_free(devices);
+}
+
+static void open_notify(int fd, int err, struct serial_port *port)
+{
+ struct serial_device *device = port->device;
+ DBusMessage *reply;
+
+ if (err < 0) {
+ /* Max tries exceeded */
+ port_release(port);
+ reply = btd_error_failed(port->msg, strerror(-err));
+ } else {
+ port->fd = fd;
+ reply = g_dbus_create_reply(port->msg,
+ DBUS_TYPE_STRING, &port->dev,
+ DBUS_TYPE_INVALID);
+ }
+
+ /* Reply to the requestor */
+ g_dbus_send_message(device->conn, reply);
+}
+
+static gboolean open_continue(gpointer user_data)
+{
+ struct serial_port *port = user_data;
+ int fd;
+ static int ntries = MAX_OPEN_TRIES;
+
+ if (!port->listener_id)
+ return FALSE; /* Owner exited */
+
+ fd = open(port->dev, O_RDONLY | O_NOCTTY);
+ if (fd < 0) {
+ int err = -errno;
+ error("Could not open %s: %s (%d)",
+ port->dev, strerror(-err), -err);
+ if (!--ntries) {
+ /* Reporting error */
+ open_notify(fd, err, port);
+ ntries = MAX_OPEN_TRIES;
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+ /* Connection succeeded */
+ open_notify(fd, 0, port);
+ return FALSE;
+}
+
+static int port_open(struct serial_port *port)
+{
+ int fd;
+
+ fd = open(port->dev, O_RDONLY | O_NOCTTY);
+ if (fd < 0) {
+ g_timeout_add(OPEN_WAIT, open_continue, port);
+ return -EINPROGRESS;
+ }
+
+ return fd;
+}
+
+static void rfcomm_connect_cb(GIOChannel *chan, GError *conn_err,
+ gpointer user_data)
+{
+ struct serial_port *port = user_data;
+ struct serial_device *device = port->device;
+ struct rfcomm_dev_req req;
+ int sk, fd;
+ DBusMessage *reply;
+
+ /* Owner exited? */
+ if (!port->listener_id)
+ return;
+
+ if (conn_err) {
+ error("%s", conn_err->message);
+ reply = btd_error_failed(port->msg, conn_err->message);
+ goto fail;
+ }
+
+ memset(&req, 0, sizeof(req));
+ req.dev_id = -1;
+ req.flags = (1 << RFCOMM_REUSE_DLC);
+ bacpy(&req.src, &device->src);
+ bacpy(&req.dst, &device->dst);
+ req.channel = port->channel;
+
+ g_io_channel_unref(port->io);
+ port->io = NULL;
+
+ sk = g_io_channel_unix_get_fd(chan);
+ port->id = ioctl(sk, RFCOMMCREATEDEV, &req);
+ if (port->id < 0) {
+ int err = -errno;
+ error("ioctl(RFCOMMCREATEDEV): %s (%d)", strerror(-err), -err);
+ reply = btd_error_failed(port->msg, strerror(-err));
+ g_io_channel_shutdown(chan, TRUE, NULL);
+ goto fail;
+ }
+
+ port->dev = g_strdup_printf("/dev/rfcomm%d", port->id);
+
+ DBG("Serial port %s created", port->dev);
+
+ g_io_channel_shutdown(chan, TRUE, NULL);
+
+ /* Addressing connect port */
+ fd = port_open(port);
+ if (fd < 0)
+ /* Open in progress: Wait the callback */
+ return;
+
+ open_notify(fd, 0, port);
+ return;
+
+fail:
+ g_dbus_send_message(device->conn, reply);
+ g_dbus_remove_watch(device->conn, port->listener_id);
+ port->listener_id = 0;
+}
+
+static void get_record_cb(sdp_list_t *recs, int err, gpointer user_data)
+{
+ struct serial_port *port = user_data;
+ struct serial_device *device = port->device;
+ sdp_record_t *record = NULL;
+ sdp_list_t *protos;
+ DBusMessage *reply;
+ GError *gerr = NULL;
+
+ if (!port->listener_id) {
+ reply = NULL;
+ goto failed;
+ }
+
+ if (err < 0) {
+ error("Unable to get service record: %s (%d)", strerror(-err),
+ -err);
+ reply = btd_error_failed(port->msg, strerror(-err));
+ goto failed;
+ }
+
+ if (!recs || !recs->data) {
+ error("No record found");
+ reply = btd_error_failed(port->msg, "No record found");
+ goto failed;
+ }
+
+ record = recs->data;
+
+ if (sdp_get_access_protos(record, &protos) < 0) {
+ error("Unable to get access protos from port record");
+ reply = btd_error_failed(port->msg, "Invalid channel");
+ goto failed;
+ }
+
+ port->channel = sdp_get_proto_port(protos, RFCOMM_UUID);
+
+ sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL);
+ sdp_list_free(protos, NULL);
+
+ port->io = bt_io_connect(BT_IO_RFCOMM, rfcomm_connect_cb, port,
+ NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, &device->src,
+ BT_IO_OPT_DEST_BDADDR, &device->dst,
+ BT_IO_OPT_CHANNEL, port->channel,
+ BT_IO_OPT_INVALID);
+ if (!port->io) {
+ error("%s", gerr->message);
+ reply = btd_error_failed(port->msg, gerr->message);
+ g_error_free(gerr);
+ goto failed;
+ }
+
+ return;
+
+failed:
+ g_dbus_remove_watch(device->conn, port->listener_id);
+ port->listener_id = 0;
+ g_dbus_send_message(device->conn, reply);
+}
+
+static int connect_port(struct serial_port *port)
+{
+ struct serial_device *device = port->device;
+ uuid_t uuid;
+ int err;
+
+ if (!port->uuid)
+ goto connect;
+
+ err = bt_string2uuid(&uuid, port->uuid);
+ if (err < 0)
+ return err;
+
+ sdp_uuid128_to_uuid(&uuid);
+
+ return bt_search_service(&device->src, &device->dst, &uuid,
+ get_record_cb, port, NULL);
+
+connect:
+ port->io = bt_io_connect(BT_IO_RFCOMM, rfcomm_connect_cb, port,
+ NULL, NULL,
+ BT_IO_OPT_SOURCE_BDADDR, &device->src,
+ BT_IO_OPT_DEST_BDADDR, &device->dst,
+ BT_IO_OPT_CHANNEL, port->channel,
+ BT_IO_OPT_INVALID);
+ if (port->io)
+ return 0;
+
+ return -errno;
+}
+
+static struct serial_port *create_port(struct serial_device *device,
+ const char *uuid, uint8_t channel)
+{
+ struct serial_port *port;
+
+ port = g_new0(struct serial_port, 1);
+ port->uuid = g_strdup(uuid);
+ port->channel = channel;
+ port->device = device;
+ port->id = -1;
+ port->fd = -1;
+
+ device->ports = g_slist_append(device->ports, port);
+
+ return port;
+}
+
+static DBusMessage *port_connect(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct serial_device *device = user_data;
+ struct serial_port *port;
+ const char *pattern;
+ int err;
+
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &pattern,
+ DBUS_TYPE_INVALID) == FALSE)
+ return NULL;
+
+ port = find_port(device->ports, pattern);
+ if (!port) {
+ char *endptr = NULL;
+ int channel;
+
+ channel = strtol(pattern, &endptr, 10);
+ if ((endptr && *endptr != '\0') || channel < 1 || channel > 30)
+ return btd_error_does_not_exist(msg);
+
+ port = create_port(device, NULL, channel);
+ }
+
+ if (port->listener_id)
+ return btd_error_failed(msg, "Port already in use");
+
+ port->listener_id = g_dbus_add_disconnect_watch(conn,
+ dbus_message_get_sender(msg),
+ port_owner_exited, port,
+ NULL);
+ port->msg = dbus_message_ref(msg);
+
+ err = connect_port(port);
+ if (err < 0) {
+ error("%s", strerror(-err));
+ g_dbus_remove_watch(conn, port->listener_id);
+ port->listener_id = 0;
+
+ return btd_error_failed(msg, strerror(-err));
+ }
+
+ return NULL;
+}
+
+static DBusMessage *port_disconnect(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct serial_device *device = user_data;
+ struct serial_port *port;
+ const char *dev, *owner, *caller;
+
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &dev,
+ DBUS_TYPE_INVALID) == FALSE)
+ return NULL;
+
+ port = find_port(device->ports, dev);
+ if (!port)
+ return btd_error_does_not_exist(msg);
+
+ if (!port->listener_id)
+ return btd_error_not_connected(msg);
+
+ owner = dbus_message_get_sender(port->msg);
+ caller = dbus_message_get_sender(msg);
+ if (!g_str_equal(owner, caller))
+ return btd_error_not_authorized(msg);
+
+ port_release(port);
+
+ g_dbus_remove_watch(conn, port->listener_id);
+ port->listener_id = 0;
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static GDBusMethodTable port_methods[] = {
+ { "Connect", "s", "s", port_connect, G_DBUS_METHOD_FLAG_ASYNC },
+ { "Disconnect", "s", "", port_disconnect },
+ { }
+};
+
+static struct serial_device *create_serial_device(DBusConnection *conn,
+ const char *path, bdaddr_t *src,
+ bdaddr_t *dst)
+{
+ struct serial_device *device;
+
+ device = g_new0(struct serial_device, 1);
+ device->conn = dbus_connection_ref(conn);
+ bacpy(&device->dst, dst);
+ bacpy(&device->src, src);
+ device->path = g_strdup(path);
+
+ if (!g_dbus_register_interface(conn, path,
+ SERIAL_PORT_INTERFACE,
+ port_methods, NULL, NULL,
+ device, path_unregister)) {
+ error("D-Bus failed to register %s interface",
+ SERIAL_PORT_INTERFACE);
+ serial_device_free(device);
+ return NULL;
+ }
+
+ DBG("Registered interface %s on path %s",
+ SERIAL_PORT_INTERFACE, path);
+
+ return device;
+}
+
+int port_register(DBusConnection *conn, const char *path, bdaddr_t *src,
+ bdaddr_t *dst, const char *uuid, uint8_t channel)
+{
+ struct serial_device *device;
+ struct serial_port *port;
+
+ device = find_device(devices, path);
+ if (!device) {
+ device = create_serial_device(conn, path, src, dst);
+ if (!device)
+ return -1;
+ devices = g_slist_append(devices, device);
+ }
+
+ if (find_port(device->ports, uuid))
+ return 0;
+
+ port = g_new0(struct serial_port, 1);
+ port->uuid = g_strdup(uuid);
+ port->channel = channel;
+ port->device = device;
+ port->id = -1;
+ port->fd = -1;
+
+ device->ports = g_slist_append(device->ports, port);
+
+ return 0;
+}
+
+int port_unregister(const char *path)
+{
+ struct serial_device *device;
+
+ device = find_device(devices, path);
+ if (!device)
+ return -ENOENT;
+
+ g_slist_foreach(device->ports, (GFunc) serial_port_free, NULL);
+ g_slist_free(device->ports);
+
+ g_dbus_unregister_interface(device->conn, path, SERIAL_PORT_INTERFACE);
+
+ return 0;
+}
diff --git a/serial/port.h b/serial/port.h
new file mode 100644
index 0000000..74ac9f0
--- /dev/null
+++ b/serial/port.h
@@ -0,0 +1,29 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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
+ *
+ */
+
+void port_release_all(void);
+
+int port_register(DBusConnection *conn, const char *path, bdaddr_t *src,
+ bdaddr_t *dst, const char *name, uint8_t channel);
+
+int port_unregister(const char *path);
diff --git a/serial/proxy.c b/serial/proxy.c
new file mode 100644
index 0000000..46561d0
--- /dev/null
+++ b/serial/proxy.c
@@ -0,0 +1,1278 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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 <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+#include <bluetooth/rfcomm.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "../src/dbus-common.h"
+#include "../src/adapter.h"
+
+#include "log.h"
+#include "textfile.h"
+
+#include "error.h"
+#include "sdpd.h"
+#include "glib-helper.h"
+#include "btio.h"
+#include "proxy.h"
+
+#define SERIAL_PROXY_INTERFACE "org.bluez.SerialProxy"
+#define SERIAL_MANAGER_INTERFACE "org.bluez.SerialProxyManager"
+#define BUF_SIZE 1024
+
+typedef enum {
+ TTY_PROXY,
+ UNIX_SOCKET_PROXY,
+ TCP_SOCKET_PROXY,
+ UNKNOWN_PROXY_TYPE = 0xFF
+} proxy_type_t;
+
+struct serial_adapter {
+ struct btd_adapter *btd_adapter; /* Adapter pointer */
+ DBusConnection *conn; /* Adapter connection */
+ GSList *proxies; /* Proxies list */
+};
+
+struct serial_proxy {
+ bdaddr_t src; /* Local address */
+ bdaddr_t dst; /* Remote address */
+ char *path; /* Proxy path */
+ char *uuid128; /* UUID 128 */
+ char *address; /* TTY or Unix socket name */
+ char *owner; /* Application bus name */
+ guint watch; /* Application watch */
+ short int port; /* TCP port */
+ proxy_type_t type; /* TTY or Unix socket */
+ struct termios sys_ti; /* Default TTY setting */
+ struct termios proxy_ti; /* Proxy TTY settings */
+ uint8_t channel; /* RFCOMM channel */
+ uint32_t record_id; /* Service record id */
+ GIOChannel *io; /* Server listen */
+ GIOChannel *rfcomm; /* Remote RFCOMM channel*/
+ GIOChannel *local; /* Local channel: TTY or Unix socket */
+ struct serial_adapter *adapter; /* Adapter pointer */
+};
+
+static GSList *adapters = NULL;
+static int sk_counter = 0;
+
+static void disable_proxy(struct serial_proxy *prx)
+{
+ if (prx->rfcomm) {
+ g_io_channel_shutdown(prx->rfcomm, TRUE, NULL);
+ g_io_channel_unref(prx->rfcomm);
+ prx->rfcomm = NULL;
+ }
+
+ if (prx->local) {
+ g_io_channel_shutdown(prx->local, TRUE, NULL);
+ g_io_channel_unref(prx->local);
+ prx->local = NULL;
+ }
+
+ remove_record_from_server(prx->record_id);
+ prx->record_id = 0;
+
+ g_io_channel_unref(prx->io);
+ prx->io = NULL;
+}
+
+static void proxy_free(struct serial_proxy *prx)
+{
+ g_free(prx->owner);
+ g_free(prx->path);
+ g_free(prx->address);
+ g_free(prx->uuid128);
+ g_free(prx);
+}
+
+static void add_lang_attr(sdp_record_t *r)
+{
+ sdp_lang_attr_t base_lang;
+ sdp_list_t *langs = 0;
+
+ /* UTF-8 MIBenum (http://www.iana.org/assignments/character-sets) */
+ base_lang.code_ISO639 = (0x65 << 8) | 0x6e;
+ base_lang.encoding = 106;
+ base_lang.base_offset = SDP_PRIMARY_LANG_BASE;
+ langs = sdp_list_append(0, &base_lang);
+ sdp_set_lang_attr(r, langs);
+ sdp_list_free(langs, 0);
+}
+
+static sdp_record_t *proxy_record_new(const char *uuid128, uint8_t channel)
+{
+ sdp_list_t *apseq, *aproto, *profiles, *proto[2], *root, *svclass_id;
+ uuid_t uuid, root_uuid, l2cap, rfcomm;
+ sdp_profile_desc_t profile;
+ sdp_record_t *record;
+ sdp_data_t *ch;
+
+ record = sdp_record_alloc();
+ if (!record)
+ return NULL;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(record, root);
+ sdp_list_free(root, NULL);
+
+ bt_string2uuid(&uuid, uuid128);
+ sdp_uuid128_to_uuid(&uuid);
+ svclass_id = sdp_list_append(NULL, &uuid);
+ sdp_set_service_classes(record, svclass_id);
+ sdp_list_free(svclass_id, NULL);
+
+ sdp_uuid16_create(&profile.uuid, SERIAL_PORT_PROFILE_ID);
+ profile.version = 0x0100;
+ profiles = sdp_list_append(NULL, &profile);
+ sdp_set_profile_descs(record, profiles);
+ sdp_list_free(profiles, NULL);
+
+ sdp_uuid16_create(&l2cap, L2CAP_UUID);
+ proto[0] = sdp_list_append(NULL, &l2cap);
+ apseq = sdp_list_append(NULL, proto[0]);
+
+ sdp_uuid16_create(&rfcomm, RFCOMM_UUID);
+ proto[1] = sdp_list_append(NULL, &rfcomm);
+ ch = sdp_data_alloc(SDP_UINT8, &channel);
+ proto[1] = sdp_list_append(proto[1], ch);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(NULL, apseq);
+ sdp_set_access_protos(record, aproto);
+
+ add_lang_attr(record);
+
+ sdp_set_info_attr(record, "Serial Proxy", NULL, "Serial Proxy");
+
+ sdp_data_free(ch);
+ sdp_list_free(proto[0], NULL);
+ sdp_list_free(proto[1], NULL);
+ sdp_list_free(apseq, NULL);
+ sdp_list_free(aproto, NULL);
+
+ return record;
+}
+
+static int channel_write(GIOChannel *chan, char *buf, size_t size)
+{
+ size_t wbytes;
+ ssize_t written;
+ int fd;
+
+ fd = g_io_channel_unix_get_fd(chan);
+
+ wbytes = written = 0;
+ while (wbytes < size) {
+ written = write(fd, buf + wbytes, size - wbytes);
+
+ if (written)
+ return -errno;
+
+ wbytes += written;
+ }
+
+ return 0;
+}
+
+static gboolean forward_data(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+ char buf[BUF_SIZE];
+ struct serial_proxy *prx = data;
+ GIOChannel *dest;
+ ssize_t rbytes;
+ int fd, err;
+
+ if (cond & G_IO_NVAL)
+ return FALSE;
+
+ dest = (chan == prx->rfcomm) ? prx->local : prx->rfcomm;
+
+ fd = g_io_channel_unix_get_fd(chan);
+
+ if (cond & (G_IO_HUP | G_IO_ERR)) {
+ /* Try forward remaining data */
+ do {
+ rbytes = read(fd, buf, sizeof(buf));
+ if (rbytes <= 0)
+ break;
+
+ err = channel_write(dest, buf, rbytes);
+ } while (err == 0);
+
+ g_io_channel_shutdown(prx->local, TRUE, NULL);
+ g_io_channel_unref(prx->local);
+ prx->local = NULL;
+
+ g_io_channel_shutdown(prx->rfcomm, TRUE, NULL);
+ g_io_channel_unref(prx->rfcomm);
+ prx->rfcomm = NULL;
+
+ return FALSE;
+ }
+
+ rbytes = read(fd, buf, sizeof(buf));
+ if (rbytes < 0)
+ return FALSE;
+
+ err = channel_write(dest, buf, rbytes);
+ if (err != 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static inline int unix_socket_connect(const char *address)
+{
+ struct sockaddr_un addr;
+ int err, sk;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = PF_UNIX;
+
+ if (strncmp("x00", address, 3) == 0) {
+ /*
+ * Abstract namespace: first byte NULL, x00
+ * must be removed from the original address.
+ */
+ strncpy(addr.sun_path + 1, address + 3,
+ sizeof(addr.sun_path) - 2);
+ } else {
+ /* Filesystem address */
+ strncpy(addr.sun_path, address, sizeof(addr.sun_path) - 1);
+ }
+
+ /* Unix socket */
+ sk = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sk < 0) {
+ err = errno;
+ error("Unix socket(%s) create failed: %s(%d)",
+ address, strerror(err), err);
+ return -err;
+ }
+
+ if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ err = errno;
+ error("Unix socket(%s) connect failed: %s(%d)",
+ address, strerror(err), err);
+ close(sk);
+ errno = err;
+ return -err;
+ }
+
+ return sk;
+}
+
+static int tcp_socket_connect(const char *address)
+{
+ struct sockaddr_in addr;
+ int err, sk;
+ unsigned short int port;
+
+ memset(&addr, 0, sizeof(addr));
+
+ if (strncmp(address, "localhost", 9) != 0) {
+ error("Address should have the form localhost:port.");
+ return -1;
+ }
+ port = atoi(strchr(address, ':') + 1);
+ if (port <= 0) {
+ error("Invalid port '%d'.", port);
+ return -1;
+ }
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = inet_addr("127.0.0.1");
+ addr.sin_port = htons(port);
+
+ sk = socket(PF_INET, SOCK_STREAM, 0);
+ if (sk < 0) {
+ err = errno;
+ error("TCP socket(%s) create failed %s(%d)", address,
+ strerror(err), err);
+ return -err;
+ }
+ if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ err = errno;
+ error("TCP socket(%s) connect failed: %s(%d)",
+ address, strerror(err), err);
+ close(sk);
+ errno = err;
+ return -err;
+ }
+ return sk;
+}
+
+static inline int tty_open(const char *tty, struct termios *ti)
+{
+ int err, sk;
+
+ sk = open(tty, O_RDWR | O_NOCTTY);
+ if (sk < 0) {
+ err = errno;
+ error("Can't open TTY %s: %s(%d)", tty, strerror(err), err);
+ return -err;
+ }
+
+ if (ti && tcsetattr(sk, TCSANOW, ti) < 0) {
+ err = errno;
+ error("Can't change serial settings: %s(%d)",
+ strerror(err), err);
+ close(sk);
+ errno = err;
+ return -err;
+ }
+
+ return sk;
+}
+
+static void connect_event_cb(GIOChannel *chan, GError *conn_err, gpointer data)
+{
+ struct serial_proxy *prx = data;
+ int sk;
+
+ if (conn_err) {
+ error("%s", conn_err->message);
+ goto drop;
+ }
+
+ /* Connect local */
+ switch (prx->type) {
+ case UNIX_SOCKET_PROXY:
+ sk = unix_socket_connect(prx->address);
+ break;
+ case TTY_PROXY:
+ sk = tty_open(prx->address, &prx->proxy_ti);
+ break;
+ case TCP_SOCKET_PROXY:
+ sk = tcp_socket_connect(prx->address);
+ break;
+ default:
+ sk = -1;
+ }
+
+ if (sk < 0)
+ goto drop;
+
+ prx->local = g_io_channel_unix_new(sk);
+
+ g_io_add_watch(prx->rfcomm,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ forward_data, prx);
+
+ g_io_add_watch(prx->local,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ forward_data, prx);
+
+ return;
+
+drop:
+ g_io_channel_shutdown(prx->rfcomm, TRUE, NULL);
+ g_io_channel_unref(prx->rfcomm);
+ prx->rfcomm = NULL;
+}
+
+static void auth_cb(DBusError *derr, void *user_data)
+{
+ struct serial_proxy *prx = user_data;
+ GError *err = NULL;
+
+ if (derr) {
+ error("Access denied: %s", derr->message);
+ goto reject;
+ }
+
+ if (!bt_io_accept(prx->rfcomm, connect_event_cb, prx, NULL,
+ &err)) {
+ error("bt_io_accept: %s", err->message);
+ g_error_free(err);
+ goto reject;
+ }
+
+ return;
+
+reject:
+ g_io_channel_shutdown(prx->rfcomm, TRUE, NULL);
+ g_io_channel_unref(prx->rfcomm);
+ prx->rfcomm = NULL;
+}
+
+static void confirm_event_cb(GIOChannel *chan, gpointer user_data)
+{
+ struct serial_proxy *prx = user_data;
+ int perr;
+ char address[18];
+ GError *err = NULL;
+
+ bt_io_get(chan, BT_IO_RFCOMM, &err,
+ BT_IO_OPT_DEST_BDADDR, &prx->dst,
+ BT_IO_OPT_DEST, address,
+ BT_IO_OPT_INVALID);
+ if (err) {
+ error("%s", err->message);
+ g_error_free(err);
+ goto drop;
+ }
+
+ if (prx->rfcomm) {
+ error("Refusing connect from %s: Proxy already in use",
+ address);
+ goto drop;
+ }
+
+ DBG("Serial Proxy: incoming connect from %s", address);
+
+ prx->rfcomm = g_io_channel_ref(chan);
+
+ perr = btd_request_authorization(&prx->src, &prx->dst,
+ prx->uuid128, auth_cb, prx);
+ if (perr < 0) {
+ error("Refusing connect from %s: %s (%d)", address,
+ strerror(-perr), -perr);
+ g_io_channel_unref(prx->rfcomm);
+ prx->rfcomm = NULL;
+ goto drop;
+ }
+
+ return;
+
+drop:
+ g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+static int enable_proxy(struct serial_proxy *prx)
+{
+ sdp_record_t *record;
+ GError *gerr = NULL;
+ int err;
+
+ if (prx->io)
+ return -EALREADY;
+
+ /* Listen */
+ prx->io = bt_io_listen(BT_IO_RFCOMM, NULL, confirm_event_cb, prx,
+ NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, &prx->src,
+ BT_IO_OPT_INVALID);
+ if (!prx->io)
+ goto failed;
+
+ bt_io_get(prx->io, BT_IO_RFCOMM, &gerr,
+ BT_IO_OPT_CHANNEL, &prx->channel,
+ BT_IO_OPT_INVALID);
+ if (gerr) {
+ g_io_channel_unref(prx->io);
+ prx->io = NULL;
+ goto failed;
+ }
+
+ DBG("Allocated channel %d", prx->channel);
+
+ g_io_channel_set_close_on_unref(prx->io, TRUE);
+
+ record = proxy_record_new(prx->uuid128, prx->channel);
+ if (!record) {
+ g_io_channel_unref(prx->io);
+ return -ENOMEM;
+ }
+
+ err = add_record_to_server(&prx->src, record);
+ if (err < 0) {
+ sdp_record_free(record);
+ g_io_channel_unref(prx->io);
+ return err;
+ }
+
+ prx->record_id = record->handle;
+
+ return 0;
+
+failed:
+ error("%s", gerr->message);
+ g_error_free(gerr);
+ return -EIO;
+
+}
+static DBusMessage *proxy_enable(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct serial_proxy *prx = data;
+ int err;
+
+ err = enable_proxy(prx);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *proxy_disable(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct serial_proxy *prx = data;
+
+ if (!prx->io)
+ return btd_error_failed(msg, "Not enabled");
+
+ /* Remove the watches and unregister the record */
+ disable_proxy(prx);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *proxy_get_info(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct serial_proxy *prx = data;
+ DBusMessage *reply;
+ DBusMessageIter iter, dict;
+ dbus_bool_t boolean;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ dict_append_entry(&dict, "uuid", DBUS_TYPE_STRING, &prx->uuid128);
+
+ dict_append_entry(&dict, "address", DBUS_TYPE_STRING, &prx->address);
+
+ if (prx->channel)
+ dict_append_entry(&dict, "channel",
+ DBUS_TYPE_BYTE, &prx->channel);
+
+ boolean = (prx->io ? TRUE : FALSE);
+ dict_append_entry(&dict, "enabled", DBUS_TYPE_BOOLEAN, &boolean);
+
+ boolean = (prx->rfcomm ? TRUE : FALSE);
+ dict_append_entry(&dict, "connected", DBUS_TYPE_BOOLEAN, &boolean);
+
+ /* If connected: append the remote address */
+ if (boolean) {
+ char bda[18];
+ const char *pstr = bda;
+
+ ba2str(&prx->dst, bda);
+ dict_append_entry(&dict, "address", DBUS_TYPE_STRING, &pstr);
+ }
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static struct {
+ const char *str;
+ speed_t speed;
+} supported_speed[] = {
+ {"50", B50 },
+ {"300", B300 },
+ {"600", B600 },
+ {"1200", B1200 },
+ {"1800", B1800 },
+ {"2400", B2400 },
+ {"4800", B4800 },
+ {"9600", B9600 },
+ {"19200", B19200 },
+ {"38400", B38400 },
+ {"57600", B57600 },
+ {"115200", B115200 },
+ { NULL, B0 }
+};
+
+static speed_t str2speed(const char *str, speed_t *speed)
+{
+ int i;
+
+ for (i = 0; supported_speed[i].str; i++) {
+ if (strcmp(supported_speed[i].str, str) != 0)
+ continue;
+
+ if (speed)
+ *speed = supported_speed[i].speed;
+
+ return supported_speed[i].speed;
+ }
+
+ return B0;
+}
+
+static int set_parity(const char *str, tcflag_t *ctrl)
+{
+ if (strcasecmp("even", str) == 0) {
+ *ctrl |= PARENB;
+ *ctrl &= ~PARODD;
+ } else if (strcasecmp("odd", str) == 0) {
+ *ctrl |= PARENB;
+ *ctrl |= PARODD;
+ } else if (strcasecmp("mark", str) == 0)
+ *ctrl |= PARENB;
+ else if ((strcasecmp("none", str) == 0) ||
+ (strcasecmp("space", str) == 0))
+ *ctrl &= ~PARENB;
+ else
+ return -1;
+
+ return 0;
+}
+
+static int set_databits(uint8_t databits, tcflag_t *ctrl)
+{
+ if (databits < 5 || databits > 8)
+ return -EINVAL;
+
+ *ctrl &= ~CSIZE;
+ switch (databits) {
+ case 5:
+ *ctrl |= CS5;
+ break;
+ case 6:
+ *ctrl |= CS6;
+ break;
+ case 7:
+ *ctrl |= CS7;
+ break;
+ case 8:
+ *ctrl |= CS8;
+ break;
+ }
+
+ return 0;
+}
+
+static int set_stopbits(uint8_t stopbits, tcflag_t *ctrl)
+{
+ /* 1.5 will not be allowed */
+ switch (stopbits) {
+ case 1:
+ *ctrl &= ~CSTOPB;
+ return 0;
+ case 2:
+ *ctrl |= CSTOPB;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static DBusMessage *proxy_set_serial_params(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct serial_proxy *prx = data;
+ const char *ratestr, *paritystr;
+ uint8_t databits, stopbits;
+ tcflag_t ctrl; /* Control mode flags */
+ speed_t speed = B0; /* In/Out speed */
+
+ /* Don't allow change TTY settings if it is open */
+ if (prx->local)
+ return btd_error_not_authorized(msg);
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &ratestr,
+ DBUS_TYPE_BYTE, &databits,
+ DBUS_TYPE_BYTE, &stopbits,
+ DBUS_TYPE_STRING, &paritystr,
+ DBUS_TYPE_INVALID))
+ return NULL;
+
+ if (str2speed(ratestr, &speed) == B0)
+ return btd_error_invalid_args(msg);
+
+ ctrl = prx->proxy_ti.c_cflag;
+ if (set_databits(databits, &ctrl) < 0)
+ return btd_error_invalid_args(msg);
+
+ if (set_stopbits(stopbits, &ctrl) < 0)
+ return btd_error_invalid_args(msg);
+
+ if (set_parity(paritystr, &ctrl) < 0)
+ return btd_error_invalid_args(msg);
+
+ prx->proxy_ti.c_cflag = ctrl;
+ prx->proxy_ti.c_cflag |= (CLOCAL | CREAD);
+ cfsetispeed(&prx->proxy_ti, speed);
+ cfsetospeed(&prx->proxy_ti, speed);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static GDBusMethodTable proxy_methods[] = {
+ { "Enable", "", "", proxy_enable },
+ { "Disable", "", "", proxy_disable },
+ { "GetInfo", "", "a{sv}",proxy_get_info },
+ { "SetSerialParameters", "syys", "", proxy_set_serial_params },
+ { },
+};
+
+static void proxy_path_unregister(gpointer data)
+{
+ struct serial_proxy *prx = data;
+ int sk;
+
+ DBG("Unregistered proxy: %s", prx->address);
+
+ if (prx->type != TTY_PROXY)
+ goto done;
+
+ /* Restore the initial TTY configuration */
+ sk = open(prx->address, O_RDWR | O_NOCTTY);
+ if (sk >= 0) {
+ tcsetattr(sk, TCSAFLUSH, &prx->sys_ti);
+ close(sk);
+ }
+done:
+
+ proxy_free(prx);
+}
+
+static int register_proxy_object(struct serial_proxy *prx)
+{
+ struct serial_adapter *adapter = prx->adapter;
+ char path[MAX_PATH_LENGTH + 1];
+
+ snprintf(path, MAX_PATH_LENGTH, "%s/proxy%d",
+ adapter_get_path(adapter->btd_adapter), sk_counter++);
+
+ if (!g_dbus_register_interface(adapter->conn, path,
+ SERIAL_PROXY_INTERFACE,
+ proxy_methods, NULL, NULL,
+ prx, proxy_path_unregister)) {
+ error("D-Bus failed to register %s path", path);
+ return -1;
+ }
+
+ prx->path = g_strdup(path);
+ adapter->proxies = g_slist_append(adapter->proxies, prx);
+
+ DBG("Registered proxy: %s", path);
+
+ return 0;
+}
+
+static int proxy_tty_register(struct serial_adapter *adapter,
+ const char *uuid128, const char *address,
+ struct termios *ti,
+ struct serial_proxy **proxy)
+{
+ struct termios sys_ti;
+ struct serial_proxy *prx;
+ int sk, ret;
+
+ sk = open(address, O_RDONLY | O_NOCTTY);
+ if (sk < 0) {
+ error("Cant open TTY: %s(%d)", strerror(errno), errno);
+ return -EINVAL;
+ }
+
+ prx = g_new0(struct serial_proxy, 1);
+ prx->address = g_strdup(address);
+ prx->uuid128 = g_strdup(uuid128);
+ prx->type = TTY_PROXY;
+ adapter_get_address(adapter->btd_adapter, &prx->src);
+ prx->adapter = adapter;
+
+ /* Current TTY settings */
+ memset(&sys_ti, 0, sizeof(sys_ti));
+ tcgetattr(sk, &sys_ti);
+ memcpy(&prx->sys_ti, &sys_ti, sizeof(sys_ti));
+ close(sk);
+
+ if (!ti) {
+ /* Use current settings */
+ memcpy(&prx->proxy_ti, &sys_ti, sizeof(sys_ti));
+ } else {
+ /* New TTY settings: user provided */
+ memcpy(&prx->proxy_ti, ti, sizeof(*ti));
+ }
+
+ ret = register_proxy_object(prx);
+ if (ret < 0) {
+ proxy_free(prx);
+ return ret;
+ }
+
+ *proxy = prx;
+
+ return ret;
+}
+
+static int proxy_socket_register(struct serial_adapter *adapter,
+ const char *uuid128, const char *address,
+ struct serial_proxy **proxy)
+{
+ struct serial_proxy *prx;
+ int ret;
+
+ prx = g_new0(struct serial_proxy, 1);
+ prx->address = g_strdup(address);
+ prx->uuid128 = g_strdup(uuid128);
+ prx->type = UNIX_SOCKET_PROXY;
+ adapter_get_address(adapter->btd_adapter, &prx->src);
+ prx->adapter = adapter;
+
+ ret = register_proxy_object(prx);
+ if (ret < 0) {
+ proxy_free(prx);
+ return ret;
+ }
+
+ *proxy = prx;
+
+ return ret;
+}
+
+static int proxy_tcp_register(struct serial_adapter *adapter,
+ const char *uuid128, const char *address,
+ struct serial_proxy **proxy)
+{
+ struct serial_proxy *prx;
+ int ret;
+
+ prx = g_new0(struct serial_proxy, 1);
+ prx->address = g_strdup(address);
+ prx->uuid128 = g_strdup(uuid128);
+ prx->type = TCP_SOCKET_PROXY;
+ adapter_get_address(adapter->btd_adapter, &prx->src);
+ prx->adapter = adapter;
+
+ ret = register_proxy_object(prx);
+ if (ret < 0) {
+ proxy_free(prx);
+ return ret;
+ }
+
+ *proxy = prx;
+
+ return ret;
+}
+
+static proxy_type_t addr2type(const char *address)
+{
+ struct stat st;
+
+ if (stat(address, &st) < 0) {
+ /*
+ * Unix socket: if the sun_path starts with null byte
+ * it refers to abstract namespace. 'x00' will be used
+ * to represent the null byte.
+ */
+ if (strncmp("localhost:", address, 10) == 0)
+ return TCP_SOCKET_PROXY;
+ if (strncmp("x00", address, 3) != 0)
+ return UNKNOWN_PROXY_TYPE;
+ else
+ return UNIX_SOCKET_PROXY;
+ } else {
+ /* Filesystem: char device or unix socket */
+ if (S_ISCHR(st.st_mode) && strncmp("/dev/", address, 4) == 0)
+ return TTY_PROXY;
+ else if (S_ISSOCK(st.st_mode))
+ return UNIX_SOCKET_PROXY;
+ else
+ return UNKNOWN_PROXY_TYPE;
+ }
+}
+
+static int proxy_addrcmp(gconstpointer proxy, gconstpointer addr)
+{
+ const struct serial_proxy *prx = proxy;
+ const char *address = addr;
+
+ return strcmp(prx->address, address);
+}
+
+static int proxy_pathcmp(gconstpointer proxy, gconstpointer p)
+{
+ const struct serial_proxy *prx = proxy;
+ const char *path = p;
+
+ return strcmp(prx->path, path);
+}
+
+static int register_proxy(struct serial_adapter *adapter,
+ const char *uuid_str, const char *address,
+ struct serial_proxy **proxy)
+{
+ proxy_type_t type;
+ int err;
+
+ type = addr2type(address);
+ if (type == UNKNOWN_PROXY_TYPE)
+ return -EINVAL;
+
+ /* Only one proxy per address(TTY or unix socket) is allowed */
+ if (g_slist_find_custom(adapter->proxies, address, proxy_addrcmp))
+ return -EALREADY;
+
+ switch (type) {
+ case UNIX_SOCKET_PROXY:
+ err = proxy_socket_register(adapter, uuid_str, address, proxy);
+ break;
+ case TTY_PROXY:
+ err = proxy_tty_register(adapter, uuid_str, address, NULL,
+ proxy);
+ break;
+ case TCP_SOCKET_PROXY:
+ err = proxy_tcp_register(adapter, uuid_str, address, proxy);
+ break;
+ default:
+ err = -EINVAL;
+ }
+
+ if (err < 0)
+ return err;
+
+ g_dbus_emit_signal(adapter->conn,
+ adapter_get_path(adapter->btd_adapter),
+ SERIAL_MANAGER_INTERFACE, "ProxyCreated",
+ DBUS_TYPE_STRING, &(*proxy)->path,
+ DBUS_TYPE_INVALID);
+
+ return 0;
+}
+
+static void unregister_proxy(struct serial_proxy *proxy)
+{
+ struct serial_adapter *adapter = proxy->adapter;
+ char *path = g_strdup(proxy->path);
+
+ if (proxy->watch > 0)
+ g_dbus_remove_watch(adapter->conn, proxy->watch);
+
+ g_dbus_emit_signal(adapter->conn,
+ adapter_get_path(adapter->btd_adapter),
+ SERIAL_MANAGER_INTERFACE, "ProxyRemoved",
+ DBUS_TYPE_STRING, &path,
+ DBUS_TYPE_INVALID);
+
+ adapter->proxies = g_slist_remove(adapter->proxies, proxy);
+
+ g_dbus_unregister_interface(adapter->conn, path,
+ SERIAL_PROXY_INTERFACE);
+
+ g_free(path);
+}
+
+static void watch_proxy(DBusConnection *connection, void *user_data)
+{
+ struct serial_proxy *proxy = user_data;
+
+ proxy->watch = 0;
+ unregister_proxy(proxy);
+}
+
+static DBusMessage *create_proxy(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct serial_adapter *adapter = data;
+ struct serial_proxy *proxy;
+ const char *pattern, *address;
+ char *uuid_str;
+ int err;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &pattern,
+ DBUS_TYPE_STRING, &address,
+ DBUS_TYPE_INVALID))
+ return NULL;
+
+ uuid_str = bt_name2string(pattern);
+ if (!uuid_str)
+ return btd_error_invalid_args(msg);
+
+ err = register_proxy(adapter, uuid_str, address, &proxy);
+ g_free(uuid_str);
+
+ if (err == -EINVAL)
+ return btd_error_invalid_args(msg);
+ else if (err == -EALREADY)
+ return btd_error_already_exists(msg);
+ else if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+ proxy->owner = g_strdup(dbus_message_get_sender(msg));
+ proxy->watch = g_dbus_add_disconnect_watch(conn, proxy->owner,
+ watch_proxy,
+ proxy, NULL);
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_STRING, &proxy->path,
+ DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *list_proxies(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct serial_adapter *adapter = data;
+ const GSList *l;
+ DBusMessage *reply;
+ DBusMessageIter iter, iter_array;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING, &iter_array);
+
+ for (l = adapter->proxies; l; l = l->next) {
+ struct serial_proxy *prx = l->data;
+
+ dbus_message_iter_append_basic(&iter_array,
+ DBUS_TYPE_STRING, &prx->path);
+ }
+
+ dbus_message_iter_close_container(&iter, &iter_array);
+
+ return reply;
+}
+
+static DBusMessage *remove_proxy(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct serial_adapter *adapter = data;
+ struct serial_proxy *prx;
+ const char *path, *sender;
+ GSList *l;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &path,
+ DBUS_TYPE_INVALID))
+ return NULL;
+
+ l = g_slist_find_custom(adapter->proxies, path, proxy_pathcmp);
+ if (!l)
+ return btd_error_does_not_exist(msg);
+
+ prx = l->data;
+
+ sender = dbus_message_get_sender(msg);
+ if (g_strcmp0(prx->owner, sender) != 0)
+ return btd_error_not_authorized(msg);
+
+ unregister_proxy(prx);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static void manager_path_unregister(void *data)
+{
+ struct serial_adapter *adapter = data;
+ GSList *l;
+
+ /* Remove proxy objects */
+ for (l = adapter->proxies; l; l = l->next) {
+ struct serial_proxy *prx = l->data;
+ char *path = g_strdup(prx->path);
+
+ g_dbus_unregister_interface(adapter->conn, path,
+ SERIAL_PROXY_INTERFACE);
+ g_free(path);
+ }
+
+ if (adapter->conn)
+ dbus_connection_unref(adapter->conn);
+
+ adapters = g_slist_remove(adapters, adapter);
+ g_slist_free(adapter->proxies);
+ btd_adapter_unref(adapter->btd_adapter);
+ g_free(adapter);
+}
+
+static GDBusMethodTable manager_methods[] = {
+ { "CreateProxy", "ss", "s", create_proxy },
+ { "ListProxies", "", "as", list_proxies },
+ { "RemoveProxy", "s", "", remove_proxy },
+ { },
+};
+
+static GDBusSignalTable manager_signals[] = {
+ { "ProxyCreated", "s" },
+ { "ProxyRemoved", "s" },
+ { }
+};
+
+static struct serial_adapter *find_adapter(GSList *list,
+ struct btd_adapter *btd_adapter)
+{
+ GSList *l;
+
+ for (l = list; l; l = l->next) {
+ struct serial_adapter *adapter = l->data;
+
+ if (adapter->btd_adapter == btd_adapter)
+ return adapter;
+ }
+
+ return NULL;
+}
+
+static void serial_proxy_init(struct serial_adapter *adapter)
+{
+ GKeyFile *config;
+ GError *gerr = NULL;
+ const char *file = CONFIGDIR "/serial.conf";
+ char **group_list;
+ int i;
+
+ config = g_key_file_new();
+
+ if (!g_key_file_load_from_file(config, file, 0, &gerr)) {
+ error("Parsing %s failed: %s", file, gerr->message);
+ g_error_free(gerr);
+ g_key_file_free(config);
+ return;
+ }
+
+ group_list = g_key_file_get_groups(config, NULL);
+
+ for (i = 0; group_list[i] != NULL; i++) {
+ char *group_str = group_list[i], *uuid_str, *address;
+ int err;
+ struct serial_proxy *prx;
+
+ /* string length of "Proxy" is 5 */
+ if (strlen(group_str) < 5 || strncmp(group_str, "Proxy", 5))
+ continue;
+
+ uuid_str = g_key_file_get_string(config, group_str, "UUID",
+ &gerr);
+ if (gerr) {
+ DBG("%s: %s", file, gerr->message);
+ g_error_free(gerr);
+ g_key_file_free(config);
+ g_strfreev(group_list);
+ return;
+ }
+
+ address = g_key_file_get_string(config, group_str, "Address",
+ &gerr);
+ if (gerr) {
+ DBG("%s: %s", file, gerr->message);
+ g_error_free(gerr);
+ g_key_file_free(config);
+ g_free(uuid_str);
+ g_strfreev(group_list);
+ return;
+ }
+
+ err = register_proxy(adapter, uuid_str, address, &prx);
+ if (err == -EINVAL)
+ error("Invalid address.");
+ else if (err == -EALREADY)
+ DBG("Proxy already exists.");
+ else if (err < 0)
+ error("Proxy creation failed (%s)", strerror(-err));
+ else {
+ err = enable_proxy(prx);
+ if (err < 0)
+ error("Proxy enable failed (%s)",
+ strerror(-err));
+ }
+
+ g_free(uuid_str);
+ g_free(address);
+ }
+
+ g_strfreev(group_list);
+ g_key_file_free(config);
+}
+
+int proxy_register(DBusConnection *conn, struct btd_adapter *btd_adapter)
+{
+ struct serial_adapter *adapter;
+ const char *path;
+
+ adapter = find_adapter(adapters, btd_adapter);
+ if (adapter)
+ return -EINVAL;
+
+ adapter = g_new0(struct serial_adapter, 1);
+ adapter->conn = dbus_connection_ref(conn);
+ adapter->btd_adapter = btd_adapter_ref(btd_adapter);
+
+ path = adapter_get_path(btd_adapter);
+
+ if (!g_dbus_register_interface(conn, path,
+ SERIAL_MANAGER_INTERFACE,
+ manager_methods, manager_signals, NULL,
+ adapter, manager_path_unregister)) {
+ error("Failed to register %s interface to %s",
+ SERIAL_MANAGER_INTERFACE, path);
+ return -1;
+ }
+
+ adapters = g_slist_append(adapters, adapter);
+
+ DBG("Registered interface %s on path %s",
+ SERIAL_MANAGER_INTERFACE, path);
+
+ serial_proxy_init(adapter);
+
+ return 0;
+}
+
+void proxy_unregister(struct btd_adapter *btd_adapter)
+{
+ struct serial_adapter *adapter;
+
+ adapter = find_adapter(adapters, btd_adapter);
+ if (!adapter)
+ return;
+
+ g_dbus_unregister_interface(adapter->conn,
+ adapter_get_path(btd_adapter),
+ SERIAL_MANAGER_INTERFACE);
+}
diff --git a/serial/proxy.h b/serial/proxy.h
new file mode 100644
index 0000000..7871665
--- /dev/null
+++ b/serial/proxy.h
@@ -0,0 +1,25 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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
+ *
+ */
+
+int proxy_register(DBusConnection *conn, struct btd_adapter *btd_adapter);
+void proxy_unregister(struct btd_adapter *btd_adapter);
diff --git a/serial/serial.conf b/serial/serial.conf
new file mode 100644
index 0000000..43ee6af
--- /dev/null
+++ b/serial/serial.conf
@@ -0,0 +1,10 @@
+# Configuration file for serial
+
+# There could be multiple proxy sections, the format is [Proxy <user chosen name>]
+#[Proxy DUN]
+
+# UUID for DUN proxy service
+#UUID=00001103-0000-1000-8000-00805F9B34FB
+
+# Address for device node
+#Address=/dev/ttyx
diff --git a/src/adapter.c b/src/adapter.c
new file mode 100644
index 0000000..031e141
--- /dev/null
+++ b/src/adapter.c
@@ -0,0 +1,3752 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/uuid.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+#include "textfile.h"
+
+#include "hcid.h"
+#include "sdpd.h"
+#include "adapter.h"
+#include "manager.h"
+#include "device.h"
+#include "dbus-common.h"
+#include "event.h"
+#include "error.h"
+#include "glib-helper.h"
+#include "agent.h"
+#include "storage.h"
+#include "attrib-server.h"
+#include "att.h"
+
+/* Flags Descriptions */
+#define EIR_LIM_DISC 0x01 /* LE Limited Discoverable Mode */
+#define EIR_GEN_DISC 0x02 /* LE General Discoverable Mode */
+#define EIR_BREDR_UNSUP 0x04 /* BR/EDR Not Supported */
+#define EIR_SIM_CONTROLLER 0x08 /* Simultaneous LE and BR/EDR to Same
+ Device Capable (Controller) */
+#define EIR_SIM_HOST 0x10 /* Simultaneous LE and BR/EDR to Same
+ Device Capable (Host) */
+
+#define ADV_TYPE_IND 0x00
+#define ADV_TYPE_DIRECT_IND 0x01
+
+#define IO_CAPABILITY_DISPLAYONLY 0x00
+#define IO_CAPABILITY_DISPLAYYESNO 0x01
+#define IO_CAPABILITY_KEYBOARDONLY 0x02
+#define IO_CAPABILITY_NOINPUTNOOUTPUT 0x03
+#define IO_CAPABILITY_INVALID 0xFF
+
+#define check_address(address) bachk(address)
+
+static DBusConnection *connection = NULL;
+static GSList *adapter_drivers = NULL;
+
+static GSList *ops_candidates = NULL;
+
+const struct btd_adapter_ops *adapter_ops = NULL;
+
+struct session_req {
+ struct btd_adapter *adapter;
+ DBusConnection *conn; /* Connection reference */
+ DBusMessage *msg; /* Unreplied message ref */
+ char *owner; /* Bus name of the owner */
+ guint id; /* Listener id */
+ uint8_t mode; /* Requested mode */
+ int refcount; /* Session refcount */
+ gboolean got_reply; /* Agent reply received */
+};
+
+struct service_auth {
+ service_auth_cb cb;
+ void *user_data;
+ struct btd_device *device;
+ struct btd_adapter *adapter;
+};
+
+struct btd_adapter {
+ uint16_t dev_id;
+ int up;
+ char *path; /* adapter object path */
+ bdaddr_t bdaddr; /* adapter Bluetooth Address */
+ uint32_t dev_class; /* Class of Device */
+ guint discov_timeout_id; /* discoverable timeout id */
+ guint stop_discov_id; /* stop inquiry/scanning id */
+ uint32_t discov_timeout; /* discoverable time(sec) */
+ guint pairable_timeout_id; /* pairable timeout id */
+ uint32_t pairable_timeout; /* pairable time(sec) */
+ uint8_t scan_mode; /* scan mode: SCAN_DISABLED, SCAN_PAGE,
+ * SCAN_INQUIRY */
+ uint8_t mode; /* off, connectable, discoverable,
+ * limited */
+ uint8_t global_mode; /* last valid global mode */
+ struct session_req *pending_mode;
+ int state; /* standard inq, periodic inq, name
+ * resolving, suspended discovery */
+ GSList *found_devices;
+ GSList *oor_devices; /* out of range device list */
+ struct agent *agent; /* For the new API */
+ guint auth_idle_id; /* Ongoing authorization */
+ GSList *connections; /* Connected devices */
+ GSList *devices; /* Devices structure pointers */
+ GSList *mode_sessions; /* Request Mode sessions */
+ GSList *disc_sessions; /* Discovery sessions */
+ guint scheduler_id; /* Scheduler handle */
+ sdp_list_t *services; /* Services associated to adapter */
+
+ struct hci_dev dev; /* hci info */
+ gboolean pairable; /* pairable state */
+ gboolean initialized;
+
+ gboolean off_requested; /* DEVDOWN ioctl was called */
+
+ gint ref;
+
+ GSList *powered_callbacks;
+
+ gboolean name_stored;
+
+ GSList *loaded_drivers;
+};
+
+static void adapter_set_pairable_timeout(struct btd_adapter *adapter,
+ guint interval);
+
+static int found_device_cmp(const struct remote_dev_info *d1,
+ const struct remote_dev_info *d2)
+{
+ int ret;
+
+ if (bacmp(&d2->bdaddr, BDADDR_ANY)) {
+ ret = bacmp(&d1->bdaddr, &d2->bdaddr);
+ if (ret)
+ return ret;
+ }
+
+ if (d2->name_status != NAME_ANY) {
+ ret = (d1->name_status - d2->name_status);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void dev_info_free(struct remote_dev_info *dev)
+{
+ g_free(dev->name);
+ g_free(dev->alias);
+ g_slist_foreach(dev->services, (GFunc) g_free, NULL);
+ g_slist_free(dev->services);
+ g_strfreev(dev->uuids);
+ g_free(dev);
+}
+
+/*
+ * Device name expansion
+ * %d - device id
+ */
+static char *expand_name(char *dst, int size, char *str, int dev_id)
+{
+ register int sp, np, olen;
+ char *opt, buf[10];
+
+ if (!str || !dst)
+ return NULL;
+
+ sp = np = 0;
+ while (np < size - 1 && str[sp]) {
+ switch (str[sp]) {
+ case '%':
+ opt = NULL;
+
+ switch (str[sp+1]) {
+ case 'd':
+ sprintf(buf, "%d", dev_id);
+ opt = buf;
+ break;
+
+ case 'h':
+ opt = main_opts.host_name;
+ break;
+
+ case '%':
+ dst[np++] = str[sp++];
+ /* fall through */
+ default:
+ sp++;
+ continue;
+ }
+
+ if (opt) {
+ /* substitute */
+ olen = strlen(opt);
+ if (np + olen < size - 1)
+ memcpy(dst + np, opt, olen);
+ np += olen;
+ }
+ sp += 2;
+ continue;
+
+ case '\\':
+ sp++;
+ /* fall through */
+ default:
+ dst[np++] = str[sp++];
+ break;
+ }
+ }
+ dst[np] = '\0';
+ return dst;
+}
+
+int btd_adapter_set_class(struct btd_adapter *adapter, uint8_t major,
+ uint8_t minor)
+{
+ return adapter_ops->set_dev_class(adapter->dev_id, major, minor);
+}
+
+static int pending_remote_name_cancel(struct btd_adapter *adapter)
+{
+ struct remote_dev_info *dev, match;
+ int err;
+
+ /* find the pending remote name request */
+ memset(&match, 0, sizeof(struct remote_dev_info));
+ bacpy(&match.bdaddr, BDADDR_ANY);
+ match.name_status = NAME_REQUESTED;
+
+ dev = adapter_search_found_devices(adapter, &match);
+ if (!dev) /* no pending request */
+ return -ENODATA;
+
+ adapter->state &= ~STATE_RESOLVNAME;
+ err = adapter_ops->cancel_resolve_name(adapter->dev_id, &dev->bdaddr);
+ if (err < 0)
+ error("Remote name cancel failed: %s(%d)",
+ strerror(errno), errno);
+ return err;
+}
+
+int adapter_resolve_names(struct btd_adapter *adapter)
+{
+ struct remote_dev_info *dev, match;
+ int err;
+
+ /* Do not attempt to resolve more names if on suspended state */
+ if (adapter->state & STATE_SUSPENDED)
+ return 0;
+
+ memset(&match, 0, sizeof(struct remote_dev_info));
+ bacpy(&match.bdaddr, BDADDR_ANY);
+ match.name_status = NAME_REQUIRED;
+
+ dev = adapter_search_found_devices(adapter, &match);
+ if (!dev)
+ return -ENODATA;
+
+ /* send at least one request or return failed if the list is empty */
+ do {
+ /* flag to indicate the current remote name requested */
+ dev->name_status = NAME_REQUESTED;
+
+ err = adapter_ops->resolve_name(adapter->dev_id, &dev->bdaddr);
+
+ if (!err)
+ break;
+
+ error("Unable to send HCI remote name req: %s (%d)",
+ strerror(errno), errno);
+
+ /* if failed, request the next element */
+ /* remove the element from the list */
+ adapter_remove_found_device(adapter, &dev->bdaddr);
+
+ /* get the next element */
+ dev = adapter_search_found_devices(adapter, &match);
+ } while (dev);
+
+ return err;
+}
+
+static const char *mode2str(uint8_t mode)
+{
+ switch(mode) {
+ case MODE_OFF:
+ return "off";
+ case MODE_CONNECTABLE:
+ return "connectable";
+ case MODE_DISCOVERABLE:
+ return "discoverable";
+ default:
+ return "unknown";
+ }
+}
+
+static uint8_t get_mode(const bdaddr_t *bdaddr, const char *mode)
+{
+ if (strcasecmp("off", mode) == 0)
+ return MODE_OFF;
+ else if (strcasecmp("connectable", mode) == 0)
+ return MODE_CONNECTABLE;
+ else if (strcasecmp("discoverable", mode) == 0)
+ return MODE_DISCOVERABLE;
+ else if (strcasecmp("on", mode) == 0) {
+ char onmode[14], srcaddr[18];
+
+ ba2str(bdaddr, srcaddr);
+ if (read_on_mode(srcaddr, onmode, sizeof(onmode)) < 0)
+ return MODE_CONNECTABLE;
+
+ return get_mode(bdaddr, onmode);
+ } else
+ return MODE_UNKNOWN;
+}
+
+static void adapter_set_limited_discoverable(struct btd_adapter *adapter,
+ gboolean limited)
+{
+ DBG("%s", limited ? "TRUE" : "FALSE");
+
+ adapter_ops->set_limited_discoverable(adapter->dev_id, limited);
+}
+
+static void adapter_remove_discov_timeout(struct btd_adapter *adapter)
+{
+ if (!adapter)
+ return;
+
+ if (adapter->discov_timeout_id == 0)
+ return;
+
+ g_source_remove(adapter->discov_timeout_id);
+ adapter->discov_timeout_id = 0;
+}
+
+static gboolean discov_timeout_handler(gpointer user_data)
+{
+ struct btd_adapter *adapter = user_data;
+
+ adapter->discov_timeout_id = 0;
+
+ adapter_ops->set_discoverable(adapter->dev_id, FALSE);
+
+ return FALSE;
+}
+
+static void adapter_set_discov_timeout(struct btd_adapter *adapter,
+ guint interval)
+{
+ if (adapter->discov_timeout_id) {
+ g_source_remove(adapter->discov_timeout_id);
+ adapter->discov_timeout_id = 0;
+ }
+
+ if (interval == 0) {
+ adapter_set_limited_discoverable(adapter, FALSE);
+ return;
+ }
+
+ /* Set limited discoverable if pairable and interval between 0 to 60
+ sec */
+ if (adapter->pairable && interval <= 60)
+ adapter_set_limited_discoverable(adapter, TRUE);
+ else
+ adapter_set_limited_discoverable(adapter, FALSE);
+
+ adapter->discov_timeout_id = g_timeout_add_seconds(interval,
+ discov_timeout_handler,
+ adapter);
+}
+
+static struct session_req *session_ref(struct session_req *req)
+{
+ req->refcount++;
+
+ DBG("%p: ref=%d", req, req->refcount);
+
+ return req;
+}
+
+static struct session_req *create_session(struct btd_adapter *adapter,
+ DBusConnection *conn, DBusMessage *msg,
+ uint8_t mode, GDBusWatchFunction cb)
+{
+ const char *sender = dbus_message_get_sender(msg);
+ struct session_req *req;
+
+ req = g_new0(struct session_req, 1);
+ req->adapter = adapter;
+ req->conn = dbus_connection_ref(conn);
+ req->msg = dbus_message_ref(msg);
+ req->mode = mode;
+
+ if (cb == NULL)
+ return session_ref(req);
+
+ req->owner = g_strdup(sender);
+ req->id = g_dbus_add_disconnect_watch(conn, sender, cb, req, NULL);
+
+ info("%s session %p with %s activated",
+ req->mode ? "Mode" : "Discovery", req, sender);
+
+ return session_ref(req);
+}
+
+static int adapter_set_mode(struct btd_adapter *adapter, uint8_t mode)
+{
+ int err;
+
+ if (mode == MODE_CONNECTABLE)
+ err = adapter_ops->set_discoverable(adapter->dev_id, FALSE);
+ else
+ err = adapter_ops->set_discoverable(adapter->dev_id, TRUE);
+
+ if (err < 0)
+ return err;
+
+ if (mode == MODE_CONNECTABLE)
+ return 0;
+
+ adapter_remove_discov_timeout(adapter);
+
+ if (adapter->discov_timeout)
+ adapter_set_discov_timeout(adapter, adapter->discov_timeout);
+
+ return 0;
+}
+
+static struct session_req *find_session_by_msg(GSList *list, const DBusMessage *msg)
+{
+ GSList *l;
+
+ for (l = list; l; l = l->next) {
+ struct session_req *req = l->data;
+
+ if (req->msg == msg)
+ return req;
+ }
+
+ return NULL;
+}
+
+static int set_mode(struct btd_adapter *adapter, uint8_t new_mode,
+ DBusMessage *msg)
+{
+ int err;
+ const char *modestr;
+ gboolean discoverable;
+
+ if (adapter->pending_mode != NULL)
+ return -EALREADY;
+
+ discoverable = new_mode == MODE_DISCOVERABLE;
+
+ if (!adapter->up && new_mode != MODE_OFF) {
+ err = adapter_ops->set_powered(adapter->dev_id, TRUE);
+ if (err < 0)
+ return err;
+
+ goto done;
+ }
+
+ if (adapter->up && new_mode == MODE_OFF) {
+ err = adapter_ops->set_powered(adapter->dev_id, FALSE);
+ if (err < 0)
+ return err;
+
+ adapter->off_requested = TRUE;
+
+ goto done;
+ }
+
+ if (new_mode == adapter->mode)
+ return 0;
+
+ err = adapter_set_mode(adapter, new_mode);
+
+ if (err < 0)
+ return err;
+
+done:
+ modestr = mode2str(new_mode);
+ write_device_mode(&adapter->bdaddr, modestr);
+
+ DBG("%s", modestr);
+
+ if (msg != NULL) {
+ struct session_req *req;
+
+ req = find_session_by_msg(adapter->mode_sessions, msg);
+ if (req) {
+ adapter->pending_mode = req;
+ session_ref(req);
+ } else
+ /* Wait for mode change to reply */
+ adapter->pending_mode = create_session(adapter,
+ connection, msg, new_mode, NULL);
+ } else
+ /* Nothing to reply just write the new mode */
+ adapter->mode = new_mode;
+
+ return 0;
+}
+
+static DBusMessage *set_discoverable(DBusConnection *conn, DBusMessage *msg,
+ gboolean discoverable, void *data)
+{
+ struct btd_adapter *adapter = data;
+ uint8_t mode;
+ int err;
+
+ mode = discoverable ? MODE_DISCOVERABLE : MODE_CONNECTABLE;
+
+ if (mode == adapter->mode) {
+ adapter->global_mode = mode;
+ return dbus_message_new_method_return(msg);
+ }
+
+ err = set_mode(adapter, mode, msg);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+ return NULL;
+}
+
+static DBusMessage *set_powered(DBusConnection *conn, DBusMessage *msg,
+ gboolean powered, void *data)
+{
+ struct btd_adapter *adapter = data;
+ uint8_t mode;
+ int err;
+
+ if (powered) {
+ mode = get_mode(&adapter->bdaddr, "on");
+ return set_discoverable(conn, msg, mode == MODE_DISCOVERABLE,
+ data);
+ }
+
+ mode = MODE_OFF;
+
+ if (mode == adapter->mode) {
+ adapter->global_mode = mode;
+ return dbus_message_new_method_return(msg);
+ }
+
+ err = set_mode(adapter, mode, msg);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+ return NULL;
+}
+
+void btd_adapter_pairable_changed(struct btd_adapter *adapter,
+ gboolean pairable)
+{
+ adapter->pairable = pairable;
+
+ write_device_pairable(&adapter->bdaddr, pairable);
+
+ emit_property_changed(connection, adapter->path,
+ ADAPTER_INTERFACE, "Pairable",
+ DBUS_TYPE_BOOLEAN, &pairable);
+
+ if (pairable && adapter->pairable_timeout)
+ adapter_set_pairable_timeout(adapter,
+ adapter->pairable_timeout);
+}
+
+static DBusMessage *set_pairable(DBusConnection *conn, DBusMessage *msg,
+ gboolean pairable, void *data)
+{
+ struct btd_adapter *adapter = data;
+ int err;
+
+ if (adapter->scan_mode == SCAN_DISABLED)
+ return btd_error_not_ready(msg);
+
+ if (pairable == adapter->pairable)
+ goto done;
+
+ if (!(adapter->scan_mode & SCAN_INQUIRY))
+ goto store;
+
+ err = set_mode(adapter, MODE_DISCOVERABLE, NULL);
+ if (err < 0 && msg)
+ return btd_error_failed(msg, strerror(-err));
+
+store:
+ adapter_ops->set_pairable(adapter->dev_id, pairable);
+
+done:
+ return msg ? dbus_message_new_method_return(msg) : NULL;
+}
+
+static gboolean pairable_timeout_handler(void *data)
+{
+ set_pairable(NULL, NULL, FALSE, data);
+
+ return FALSE;
+}
+
+static void adapter_set_pairable_timeout(struct btd_adapter *adapter,
+ guint interval)
+{
+ if (adapter->pairable_timeout_id) {
+ g_source_remove(adapter->pairable_timeout_id);
+ adapter->pairable_timeout_id = 0;
+ }
+
+ if (interval == 0)
+ return;
+
+ adapter->pairable_timeout_id = g_timeout_add_seconds(interval,
+ pairable_timeout_handler,
+ adapter);
+}
+
+static struct session_req *find_session(GSList *list, const char *sender)
+{
+ GSList *l;
+
+ for (l = list; l; l = l->next) {
+ struct session_req *req = l->data;
+
+ if (g_str_equal(req->owner, sender))
+ return req;
+ }
+
+ return NULL;
+}
+
+static uint8_t get_needed_mode(struct btd_adapter *adapter, uint8_t mode)
+{
+ GSList *l;
+
+ if (adapter->global_mode > mode)
+ mode = adapter->global_mode;
+
+ for (l = adapter->mode_sessions; l; l = l->next) {
+ struct session_req *req = l->data;
+
+ if (req->mode > mode)
+ mode = req->mode;
+ }
+
+ return mode;
+}
+
+static GSList *remove_bredr(GSList *all)
+{
+ GSList *l, *le;
+
+ for (l = all, le = NULL; l; l = l->next) {
+ struct remote_dev_info *dev = l->data;
+ if (dev->le == FALSE) {
+ dev_info_free(dev);
+ continue;
+ }
+
+ le = g_slist_append(le, dev);
+ }
+
+ g_slist_free(all);
+
+ return le;
+}
+
+static void stop_discovery(struct btd_adapter *adapter, gboolean suspend)
+{
+ pending_remote_name_cancel(adapter);
+
+ if (suspend == FALSE)
+ adapter->found_devices = remove_bredr(adapter->found_devices);
+
+ if (adapter->oor_devices) {
+ g_slist_free(adapter->oor_devices);
+ adapter->oor_devices = NULL;
+ }
+
+ /* Reset if suspended, otherwise remove timer (software scheduler)
+ or request inquiry to stop */
+ if (adapter->state & STATE_SUSPENDED) {
+ adapter->state &= ~STATE_SUSPENDED;
+ return;
+ }
+
+ if (adapter->scheduler_id) {
+ g_source_remove(adapter->scheduler_id);
+ adapter->scheduler_id = 0;
+ return;
+ }
+
+ if (adapter->state & STATE_LE_SCAN)
+ adapter_ops->stop_scanning(adapter->dev_id);
+ else
+ adapter_ops->stop_inquiry(adapter->dev_id);
+}
+
+static void session_remove(struct session_req *req)
+{
+ struct btd_adapter *adapter = req->adapter;
+
+ /* Ignore set_mode session */
+ if (req->owner == NULL)
+ return;
+
+ DBG("%s session %p with %s deactivated",
+ req->mode ? "Mode" : "Discovery", req, req->owner);
+
+ if (req->mode) {
+ uint8_t mode;
+
+ adapter->mode_sessions = g_slist_remove(adapter->mode_sessions,
+ req);
+
+ mode = get_needed_mode(adapter, adapter->global_mode);
+
+ if (mode == adapter->mode)
+ return;
+
+ DBG("Switching to '%s' mode", mode2str(mode));
+
+ set_mode(adapter, mode, NULL);
+ } else {
+ adapter->disc_sessions = g_slist_remove(adapter->disc_sessions,
+ req);
+
+ if (adapter->disc_sessions)
+ return;
+
+ DBG("Stopping discovery");
+
+ stop_discovery(adapter, FALSE);
+ }
+}
+
+static void session_free(struct session_req *req)
+{
+ if (req->id)
+ g_dbus_remove_watch(req->conn, req->id);
+
+ session_remove(req);
+
+ if (req->msg) {
+ dbus_message_unref(req->msg);
+ if (!req->got_reply && req->mode && req->adapter->agent)
+ agent_cancel(req->adapter->agent);
+ }
+
+ if (req->conn)
+ dbus_connection_unref(req->conn);
+ g_free(req->owner);
+ g_free(req);
+}
+
+static void session_owner_exit(DBusConnection *conn, void *user_data)
+{
+ struct session_req *req = user_data;
+
+ req->id = 0;
+
+ session_free(req);
+}
+
+static void session_unref(struct session_req *req)
+{
+ req->refcount--;
+
+ DBG("%p: ref=%d", req, req->refcount);
+
+ if (req->refcount)
+ return;
+
+ session_free(req);
+}
+
+static void confirm_mode_cb(struct agent *agent, DBusError *derr, void *data)
+{
+ struct session_req *req = data;
+ int err;
+ DBusMessage *reply;
+
+ req->got_reply = TRUE;
+
+ if (derr && dbus_error_is_set(derr)) {
+ reply = dbus_message_new_error(req->msg, derr->name,
+ derr->message);
+ g_dbus_send_message(req->conn, reply);
+ session_unref(req);
+ return;
+ }
+
+ err = set_mode(req->adapter, req->mode, req->msg);
+ if (err < 0)
+ reply = btd_error_failed(req->msg, strerror(-err));
+ else if (!req->adapter->pending_mode)
+ reply = dbus_message_new_method_return(req->msg);
+ else
+ reply = NULL;
+
+ if (reply) {
+ /*
+ * Send reply immediately only if there was an error changing
+ * mode, or change is not needed. Otherwise, reply is sent in
+ * set_mode_complete.
+ */
+ g_dbus_send_message(req->conn, reply);
+
+ dbus_message_unref(req->msg);
+ req->msg = NULL;
+ }
+
+ if (!find_session(req->adapter->mode_sessions, req->owner))
+ session_unref(req);
+}
+
+static DBusMessage *set_discoverable_timeout(DBusConnection *conn,
+ DBusMessage *msg,
+ uint32_t timeout,
+ void *data)
+{
+ struct btd_adapter *adapter = data;
+ const char *path;
+
+ if (adapter->discov_timeout == timeout && timeout == 0)
+ return dbus_message_new_method_return(msg);
+
+ if (adapter->scan_mode & SCAN_INQUIRY)
+ adapter_set_discov_timeout(adapter, timeout);
+
+ adapter->discov_timeout = timeout;
+
+ write_discoverable_timeout(&adapter->bdaddr, timeout);
+
+ path = dbus_message_get_path(msg);
+
+ emit_property_changed(conn, path,
+ ADAPTER_INTERFACE, "DiscoverableTimeout",
+ DBUS_TYPE_UINT32, &timeout);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *set_pairable_timeout(DBusConnection *conn,
+ DBusMessage *msg,
+ uint32_t timeout,
+ void *data)
+{
+ struct btd_adapter *adapter = data;
+ const char *path;
+
+ if (adapter->pairable_timeout == timeout && timeout == 0)
+ return dbus_message_new_method_return(msg);
+
+ if (adapter->pairable)
+ adapter_set_pairable_timeout(adapter, timeout);
+
+ adapter->pairable_timeout = timeout;
+
+ write_pairable_timeout(&adapter->bdaddr, timeout);
+
+ path = dbus_message_get_path(msg);
+
+ emit_property_changed(conn, path,
+ ADAPTER_INTERFACE, "PairableTimeout",
+ DBUS_TYPE_UINT32, &timeout);
+
+ return dbus_message_new_method_return(msg);
+}
+
+void btd_adapter_class_changed(struct btd_adapter *adapter, uint32_t new_class)
+{
+ uint8_t class[3];
+
+ class[2] = (new_class >> 16) & 0xff;
+ class[1] = (new_class >> 8) & 0xff;
+ class[0] = new_class & 0xff;
+
+ write_local_class(&adapter->bdaddr, class);
+
+ adapter->dev_class = new_class;
+
+ if (main_opts.attrib_server) {
+ /* Removes service class */
+ class[1] = class[1] & 0x1f;
+ attrib_gap_set(GATT_CHARAC_APPEARANCE, class, 2);
+ }
+
+ emit_property_changed(connection, adapter->path,
+ ADAPTER_INTERFACE, "Class",
+ DBUS_TYPE_UINT32, &new_class);
+}
+
+void adapter_update_local_name(struct btd_adapter *adapter, const char *name)
+{
+ struct hci_dev *dev = &adapter->dev;
+
+ if (strncmp(name, dev->name, MAX_NAME_LENGTH) == 0)
+ return;
+
+ strncpy(dev->name, name, MAX_NAME_LENGTH);
+
+ if (main_opts.attrib_server)
+ attrib_gap_set(GATT_CHARAC_DEVICE_NAME,
+ (const uint8_t *) dev->name, strlen(dev->name));
+
+ if (!adapter->name_stored) {
+ char *name_ptr = dev->name;
+
+ write_local_name(&adapter->bdaddr, dev->name);
+
+ if (connection)
+ emit_property_changed(connection, adapter->path,
+ ADAPTER_INTERFACE, "Name",
+ DBUS_TYPE_STRING, &name_ptr);
+ }
+
+ adapter->name_stored = FALSE;
+}
+
+static DBusMessage *set_name(DBusConnection *conn, DBusMessage *msg,
+ const char *name, void *data)
+{
+ struct btd_adapter *adapter = data;
+ struct hci_dev *dev = &adapter->dev;
+ char *name_ptr = dev->name;
+
+ if (!g_utf8_validate(name, -1, NULL)) {
+ error("Name change failed: supplied name isn't valid UTF-8");
+ return btd_error_invalid_args(msg);
+ }
+
+ if (strncmp(name, dev->name, MAX_NAME_LENGTH) == 0)
+ goto done;
+
+ strncpy(dev->name, name, MAX_NAME_LENGTH);
+ write_local_name(&adapter->bdaddr, name);
+ emit_property_changed(connection, adapter->path,
+ ADAPTER_INTERFACE, "Name",
+ DBUS_TYPE_STRING, &name_ptr);
+
+ if (adapter->up) {
+ int err = adapter_ops->set_name(adapter->dev_id, name);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+ adapter->name_stored = TRUE;
+ }
+
+done:
+ return dbus_message_new_method_return(msg);
+}
+
+struct btd_device *adapter_find_device(struct btd_adapter *adapter,
+ const char *dest)
+{
+ struct btd_device *device;
+ GSList *l;
+
+ if (!adapter)
+ return NULL;
+
+ l = g_slist_find_custom(adapter->devices, dest,
+ (GCompareFunc) device_address_cmp);
+ if (!l)
+ return NULL;
+
+ device = l->data;
+
+ return device;
+}
+
+static void adapter_update_devices(struct btd_adapter *adapter)
+{
+ char **devices;
+ int i;
+ GSList *l;
+
+ /* Devices */
+ devices = g_new0(char *, g_slist_length(adapter->devices) + 1);
+ for (i = 0, l = adapter->devices; l; l = l->next, i++) {
+ struct btd_device *dev = l->data;
+ devices[i] = (char *) device_get_path(dev);
+ }
+
+ emit_array_property_changed(connection, adapter->path,
+ ADAPTER_INTERFACE, "Devices",
+ DBUS_TYPE_OBJECT_PATH, &devices, i);
+ g_free(devices);
+}
+
+static void adapter_emit_uuids_updated(struct btd_adapter *adapter)
+{
+ char **uuids;
+ int i;
+ sdp_list_t *list;
+
+ if (!adapter->initialized)
+ return;
+
+ uuids = g_new0(char *, sdp_list_len(adapter->services) + 1);
+
+ for (i = 0, list = adapter->services; list; list = list->next) {
+ char *uuid;
+ sdp_record_t *rec = list->data;
+
+ uuid = bt_uuid2string(&rec->svclass);
+ if (uuid)
+ uuids[i++] = uuid;
+ }
+
+ emit_array_property_changed(connection, adapter->path,
+ ADAPTER_INTERFACE, "UUIDs", DBUS_TYPE_STRING, &uuids, i);
+
+ g_strfreev(uuids);
+}
+
+static uint8_t get_uuid_mask(uuid_t *uuid)
+{
+ if (uuid->type != SDP_UUID16)
+ return 0;
+
+ switch (uuid->value.uuid16) {
+ case DIALUP_NET_SVCLASS_ID:
+ case CIP_SVCLASS_ID:
+ return 0x42; /* Telephony & Networking */
+ case IRMC_SYNC_SVCLASS_ID:
+ case OBEX_OBJPUSH_SVCLASS_ID:
+ case OBEX_FILETRANS_SVCLASS_ID:
+ case IRMC_SYNC_CMD_SVCLASS_ID:
+ case PBAP_PSE_SVCLASS_ID:
+ return 0x10; /* Object Transfer */
+ case HEADSET_SVCLASS_ID:
+ case HANDSFREE_SVCLASS_ID:
+ return 0x20; /* Audio */
+ case CORDLESS_TELEPHONY_SVCLASS_ID:
+ case INTERCOM_SVCLASS_ID:
+ case FAX_SVCLASS_ID:
+ case SAP_SVCLASS_ID:
+ /*
+ * Setting the telephony bit for the handsfree audio gateway
+ * role is not required by the HFP specification, but the
+ * Nokia 616 carkit is just plain broken! It will refuse
+ * pairing without this bit set.
+ */
+ case HANDSFREE_AGW_SVCLASS_ID:
+ return 0x40; /* Telephony */
+ case AUDIO_SOURCE_SVCLASS_ID:
+ case VIDEO_SOURCE_SVCLASS_ID:
+ return 0x08; /* Capturing */
+ case AUDIO_SINK_SVCLASS_ID:
+ case VIDEO_SINK_SVCLASS_ID:
+ return 0x04; /* Rendering */
+ case PANU_SVCLASS_ID:
+ case NAP_SVCLASS_ID:
+ case GN_SVCLASS_ID:
+ return 0x02; /* Networking */
+ default:
+ return 0;
+ }
+}
+
+static int uuid_cmp(const void *a, const void *b)
+{
+ const sdp_record_t *rec = a;
+ const uuid_t *uuid = b;
+
+ return sdp_uuid_cmp(&rec->svclass, uuid);
+}
+
+void adapter_service_insert(struct btd_adapter *adapter, void *r)
+{
+ sdp_record_t *rec = r;
+ gboolean new_uuid;
+
+ if (sdp_list_find(adapter->services, &rec->svclass, uuid_cmp) == NULL)
+ new_uuid = TRUE;
+ else
+ new_uuid = FALSE;
+
+ adapter->services = sdp_list_insert_sorted(adapter->services, rec,
+ record_sort);
+
+ if (new_uuid) {
+ uint8_t svc_hint = get_uuid_mask(&rec->svclass);
+ adapter_ops->add_uuid(adapter->dev_id, &rec->svclass, svc_hint);
+ }
+
+ adapter_emit_uuids_updated(adapter);
+}
+
+void adapter_service_remove(struct btd_adapter *adapter, void *r)
+{
+ sdp_record_t *rec = r;
+
+ adapter->services = sdp_list_remove(adapter->services, rec);
+
+ if (sdp_list_find(adapter->services, &rec->svclass, uuid_cmp) == NULL)
+ adapter_ops->remove_uuid(adapter->dev_id, &rec->svclass);
+
+ adapter_emit_uuids_updated(adapter);
+}
+
+static struct btd_device *adapter_create_device(DBusConnection *conn,
+ struct btd_adapter *adapter,
+ const char *address,
+ device_type_t type)
+{
+ struct btd_device *device;
+ const char *path;
+
+ DBG("%s", address);
+
+ device = device_create(conn, adapter, address, type);
+ if (!device)
+ return NULL;
+
+ device_set_temporary(device, TRUE);
+
+ adapter->devices = g_slist_append(adapter->devices, device);
+
+ path = device_get_path(device);
+ g_dbus_emit_signal(conn, adapter->path,
+ ADAPTER_INTERFACE, "DeviceCreated",
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID);
+
+ adapter_update_devices(adapter);
+
+ return device;
+}
+
+void adapter_remove_device(DBusConnection *conn, struct btd_adapter *adapter,
+ struct btd_device *device,
+ gboolean remove_storage)
+{
+ const gchar *dev_path = device_get_path(device);
+ struct agent *agent;
+
+ adapter->devices = g_slist_remove(adapter->devices, device);
+ adapter->connections = g_slist_remove(adapter->connections, device);
+
+ adapter_update_devices(adapter);
+
+ g_dbus_emit_signal(conn, adapter->path,
+ ADAPTER_INTERFACE, "DeviceRemoved",
+ DBUS_TYPE_OBJECT_PATH, &dev_path,
+ DBUS_TYPE_INVALID);
+
+ agent = device_get_agent(device);
+
+ if (agent && device_is_authorizing(device))
+ agent_cancel(agent);
+
+ device_remove(device, remove_storage);
+}
+
+struct btd_device *adapter_get_device(DBusConnection *conn,
+ struct btd_adapter *adapter,
+ const gchar *address)
+{
+ struct btd_device *device;
+
+ DBG("%s", address);
+
+ if (!adapter)
+ return NULL;
+
+ device = adapter_find_device(adapter, address);
+ if (device)
+ return device;
+
+ return adapter_create_device(conn, adapter, address,
+ DEVICE_TYPE_BREDR);
+}
+
+static gboolean stop_scanning(gpointer user_data)
+{
+ struct btd_adapter *adapter = user_data;
+
+ adapter_ops->stop_scanning(adapter->dev_id);
+
+ return FALSE;
+}
+
+static int start_discovery(struct btd_adapter *adapter)
+{
+ int err, type;
+
+ /* Do not start if suspended */
+ if (adapter->state & STATE_SUSPENDED)
+ return 0;
+
+ /* Postpone discovery if still resolving names */
+ if (adapter->state & STATE_RESOLVNAME)
+ return 1;
+
+ pending_remote_name_cancel(adapter);
+
+ type = adapter_get_discover_type(adapter) & ~DISC_RESOLVNAME;
+
+ switch (type) {
+ case DISC_STDINQ:
+ case DISC_INTERLEAVE:
+ err = adapter_ops->start_inquiry(adapter->dev_id,
+ 0x08, FALSE);
+ break;
+ case DISC_PINQ:
+ err = adapter_ops->start_inquiry(adapter->dev_id,
+ 0x08, TRUE);
+ break;
+ case DISC_LE:
+ err = adapter_ops->start_scanning(adapter->dev_id);
+ break;
+ default:
+ err = -EINVAL;
+ }
+
+ return err;
+}
+
+static DBusMessage *adapter_start_discovery(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct session_req *req;
+ struct btd_adapter *adapter = data;
+ const char *sender = dbus_message_get_sender(msg);
+ int err;
+
+ if (!adapter->up)
+ return btd_error_not_ready(msg);
+
+ req = find_session(adapter->disc_sessions, sender);
+ if (req) {
+ session_ref(req);
+ return dbus_message_new_method_return(msg);
+ }
+
+ if (adapter->disc_sessions)
+ goto done;
+
+ g_slist_foreach(adapter->found_devices, (GFunc) dev_info_free, NULL);
+ g_slist_free(adapter->found_devices);
+ adapter->found_devices = NULL;
+
+ g_slist_free(adapter->oor_devices);
+ adapter->oor_devices = NULL;
+
+ err = start_discovery(adapter);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+done:
+ req = create_session(adapter, conn, msg, 0,
+ session_owner_exit);
+
+ adapter->disc_sessions = g_slist_append(adapter->disc_sessions, req);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *adapter_stop_discovery(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_adapter *adapter = data;
+ struct session_req *req;
+ const char *sender = dbus_message_get_sender(msg);
+
+ if (!adapter->up)
+ return btd_error_not_ready(msg);
+
+ req = find_session(adapter->disc_sessions, sender);
+ if (!req)
+ return btd_error_failed(msg, "Invalid discovery session");
+
+ session_unref(req);
+ info("Stopping discovery");
+ return dbus_message_new_method_return(msg);
+}
+
+struct remote_device_list_t {
+ GSList *list;
+ time_t time;
+};
+
+static DBusMessage *get_properties(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_adapter *adapter = data;
+ const char *property;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ char str[MAX_NAME_LENGTH + 1], srcaddr[18];
+ gboolean value;
+ char **devices, **uuids;
+ int i;
+ GSList *l;
+ sdp_list_t *list;
+
+ ba2str(&adapter->bdaddr, srcaddr);
+
+ if (check_address(srcaddr) < 0)
+ return btd_error_invalid_args(msg);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ /* Address */
+ property = srcaddr;
+ dict_append_entry(&dict, "Address", DBUS_TYPE_STRING, &property);
+
+ /* Name */
+ memset(str, 0, sizeof(str));
+ strncpy(str, (char *) adapter->dev.name, MAX_NAME_LENGTH);
+ property = str;
+
+ dict_append_entry(&dict, "Name", DBUS_TYPE_STRING, &property);
+
+ /* Class */
+ dict_append_entry(&dict, "Class",
+ DBUS_TYPE_UINT32, &adapter->dev_class);
+
+ /* Powered */
+ value = (adapter->up && !adapter->off_requested) ? TRUE : FALSE;
+ dict_append_entry(&dict, "Powered", DBUS_TYPE_BOOLEAN, &value);
+
+ /* Discoverable */
+ value = adapter->scan_mode & SCAN_INQUIRY ? TRUE : FALSE;
+ dict_append_entry(&dict, "Discoverable", DBUS_TYPE_BOOLEAN, &value);
+
+ /* Pairable */
+ dict_append_entry(&dict, "Pairable", DBUS_TYPE_BOOLEAN,
+ &adapter->pairable);
+
+ /* DiscoverableTimeout */
+ dict_append_entry(&dict, "DiscoverableTimeout",
+ DBUS_TYPE_UINT32, &adapter->discov_timeout);
+
+ /* PairableTimeout */
+ dict_append_entry(&dict, "PairableTimeout",
+ DBUS_TYPE_UINT32, &adapter->pairable_timeout);
+
+
+ if (adapter->state & (STATE_PINQ | STATE_STDINQ | STATE_LE_SCAN))
+ value = TRUE;
+ else
+ value = FALSE;
+
+ /* Discovering */
+ dict_append_entry(&dict, "Discovering", DBUS_TYPE_BOOLEAN, &value);
+
+ /* Devices */
+ devices = g_new0(char *, g_slist_length(adapter->devices) + 1);
+ for (i = 0, l = adapter->devices; l; l = l->next, i++) {
+ struct btd_device *dev = l->data;
+ devices[i] = (char *) device_get_path(dev);
+ }
+ dict_append_array(&dict, "Devices", DBUS_TYPE_OBJECT_PATH,
+ &devices, i);
+ g_free(devices);
+
+ /* UUIDs */
+ uuids = g_new0(char *, sdp_list_len(adapter->services) + 1);
+
+ for (i = 0, list = adapter->services; list; list = list->next) {
+ sdp_record_t *rec = list->data;
+ char *uuid;
+
+ uuid = bt_uuid2string(&rec->svclass);
+ if (uuid)
+ uuids[i++] = uuid;
+ }
+
+ dict_append_array(&dict, "UUIDs", DBUS_TYPE_STRING, &uuids, i);
+
+ g_strfreev(uuids);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static DBusMessage *set_property(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_adapter *adapter = data;
+ DBusMessageIter iter;
+ DBusMessageIter sub;
+ const char *property;
+ char srcaddr[18];
+
+ ba2str(&adapter->bdaddr, srcaddr);
+
+ if (!dbus_message_iter_init(msg, &iter))
+ return btd_error_invalid_args(msg);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&iter, &property);
+ dbus_message_iter_next(&iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+ return btd_error_invalid_args(msg);
+ dbus_message_iter_recurse(&iter, &sub);
+
+ if (g_str_equal("Name", property)) {
+ const char *name;
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING)
+ return btd_error_invalid_args(msg);
+ dbus_message_iter_get_basic(&sub, &name);
+
+ return set_name(conn, msg, name, data);
+ } else if (g_str_equal("Powered", property)) {
+ gboolean powered;
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&sub, &powered);
+
+ return set_powered(conn, msg, powered, data);
+ } else if (g_str_equal("Discoverable", property)) {
+ gboolean discoverable;
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&sub, &discoverable);
+
+ return set_discoverable(conn, msg, discoverable, data);
+ } else if (g_str_equal("DiscoverableTimeout", property)) {
+ uint32_t timeout;
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT32)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&sub, &timeout);
+
+ return set_discoverable_timeout(conn, msg, timeout, data);
+ } else if (g_str_equal("Pairable", property)) {
+ gboolean pairable;
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&sub, &pairable);
+
+ return set_pairable(conn, msg, pairable, data);
+ } else if (g_str_equal("PairableTimeout", property)) {
+ uint32_t timeout;
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT32)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&sub, &timeout);
+
+ return set_pairable_timeout(conn, msg, timeout, data);
+ }
+
+ return btd_error_invalid_args(msg);
+}
+
+static DBusMessage *request_session(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_adapter *adapter = data;
+ struct session_req *req;
+ const char *sender = dbus_message_get_sender(msg);
+ uint8_t new_mode;
+ int err;
+
+ if (!adapter->agent)
+ return btd_error_agent_not_available(msg);
+
+ if (!adapter->mode_sessions)
+ adapter->global_mode = adapter->mode;
+
+ new_mode = get_mode(&adapter->bdaddr, "on");
+
+ req = find_session(adapter->mode_sessions, sender);
+ if (req) {
+ session_ref(req);
+ return dbus_message_new_method_return(msg);
+ } else {
+ req = create_session(adapter, conn, msg, new_mode,
+ session_owner_exit);
+ adapter->mode_sessions = g_slist_append(adapter->mode_sessions,
+ req);
+ }
+
+ /* No need to change mode */
+ if (adapter->mode >= new_mode)
+ return dbus_message_new_method_return(msg);
+
+ err = agent_confirm_mode_change(adapter->agent, mode2str(new_mode),
+ confirm_mode_cb, req, NULL);
+ if (err < 0) {
+ session_unref(req);
+ return btd_error_failed(msg, strerror(-err));
+ }
+
+ return NULL;
+}
+
+static DBusMessage *release_session(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_adapter *adapter = data;
+ struct session_req *req;
+ const char *sender = dbus_message_get_sender(msg);
+
+ req = find_session(adapter->mode_sessions, sender);
+ if (!req)
+ return btd_error_failed(msg, "Invalid Session");
+
+ session_unref(req);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *list_devices(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_adapter *adapter = data;
+ DBusMessage *reply;
+ GSList *l;
+ DBusMessageIter iter;
+ DBusMessageIter array_iter;
+ const gchar *dev_path;
+
+ if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
+ return btd_error_invalid_args(msg);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_OBJECT_PATH_AS_STRING, &array_iter);
+
+ for (l = adapter->devices; l; l = l->next) {
+ struct btd_device *device = l->data;
+
+ dev_path = device_get_path(device);
+
+ dbus_message_iter_append_basic(&array_iter,
+ DBUS_TYPE_OBJECT_PATH, &dev_path);
+ }
+
+ dbus_message_iter_close_container(&iter, &array_iter);
+
+ return reply;
+}
+
+static DBusMessage *cancel_device_creation(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_adapter *adapter = data;
+ const gchar *address, *sender = dbus_message_get_sender(msg);
+ struct btd_device *device;
+
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &address,
+ DBUS_TYPE_INVALID) == FALSE)
+ return btd_error_invalid_args(msg);
+
+ if (check_address(address) < 0)
+ return btd_error_invalid_args(msg);
+
+ device = adapter_find_device(adapter, address);
+ if (!device || !device_is_creating(device, NULL))
+ return btd_error_does_not_exist(msg);
+
+ if (!device_is_creating(device, sender))
+ return btd_error_not_authorized(msg);
+
+ device_set_temporary(device, TRUE);
+
+ if (device_is_connected(device)) {
+ device_request_disconnect(device, msg);
+ return NULL;
+ }
+
+ adapter_remove_device(conn, adapter, device, TRUE);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static device_type_t flags2type(uint8_t flags)
+{
+ /* Inferring the remote type based on the EIR Flags field */
+
+ /* For LE only and dual mode the following flags must be zero */
+ if (flags & (EIR_SIM_CONTROLLER | EIR_SIM_HOST))
+ return DEVICE_TYPE_UNKNOWN;
+
+ /* Limited or General discoverable mode bit must be enabled */
+ if (!(flags & (EIR_LIM_DISC | EIR_GEN_DISC)))
+ return DEVICE_TYPE_UNKNOWN;
+
+ if (flags & EIR_BREDR_UNSUP)
+ return DEVICE_TYPE_LE;
+ else
+ return DEVICE_TYPE_DUALMODE;
+}
+
+static gboolean event_is_connectable(uint8_t type)
+{
+ switch (type) {
+ case ADV_TYPE_IND:
+ case ADV_TYPE_DIRECT_IND:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+static struct btd_device *create_device_internal(DBusConnection *conn,
+ struct btd_adapter *adapter,
+ const gchar *address,
+ gboolean force, int *err)
+{
+ struct remote_dev_info *dev, match;
+ struct btd_device *device;
+ device_type_t type;
+
+ memset(&match, 0, sizeof(struct remote_dev_info));
+ str2ba(address, &match.bdaddr);
+ match.name_status = NAME_ANY;
+
+ dev = adapter_search_found_devices(adapter, &match);
+ if (dev && dev->flags)
+ type = flags2type(dev->flags);
+ else
+ type = DEVICE_TYPE_BREDR;
+
+ if (!force && type == DEVICE_TYPE_LE &&
+ !event_is_connectable(dev->evt_type)) {
+ if (err)
+ *err = -ENOTCONN;
+
+ return NULL;
+ }
+
+ device = adapter_create_device(conn, adapter, address, type);
+ if (!device && err)
+ *err = -ENOMEM;
+
+ return device;
+}
+
+static DBusMessage *create_device(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_adapter *adapter = data;
+ struct btd_device *device;
+ const gchar *address;
+ DBusMessage *reply;
+ int err;
+
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &address,
+ DBUS_TYPE_INVALID) == FALSE)
+ return btd_error_invalid_args(msg);
+
+ if (check_address(address) < 0)
+ return btd_error_invalid_args(msg);
+
+ if (!adapter->up)
+ return btd_error_not_ready(msg);
+
+ if (adapter_find_device(adapter, address))
+ return btd_error_already_exists(msg);
+
+ DBG("%s", address);
+
+ device = create_device_internal(conn, adapter, address, TRUE, &err);
+ if (!device)
+ goto failed;
+
+ if (device_get_type(device) != DEVICE_TYPE_LE)
+ err = device_browse_sdp(device, conn, msg, NULL, FALSE);
+ else
+ err = device_browse_primary(device, conn, msg, FALSE);
+
+ if (err < 0) {
+ adapter_remove_device(conn, adapter, device, TRUE);
+ return btd_error_failed(msg, strerror(-err));
+ }
+
+ return NULL;
+
+failed:
+ if (err == -ENOTCONN) {
+ /* Device is not connectable */
+ const char *path = device_get_path(device);
+
+ reply = dbus_message_new_method_return(msg);
+
+ dbus_message_append_args(reply,
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID);
+ } else
+ reply = btd_error_failed(msg, strerror(-err));
+
+ return reply;
+}
+
+static uint8_t parse_io_capability(const char *capability)
+{
+ if (g_str_equal(capability, ""))
+ return IO_CAPABILITY_DISPLAYYESNO;
+ if (g_str_equal(capability, "DisplayOnly"))
+ return IO_CAPABILITY_DISPLAYONLY;
+ if (g_str_equal(capability, "DisplayYesNo"))
+ return IO_CAPABILITY_DISPLAYYESNO;
+ if (g_str_equal(capability, "KeyboardOnly"))
+ return IO_CAPABILITY_KEYBOARDONLY;
+ if (g_str_equal(capability, "NoInputNoOutput"))
+ return IO_CAPABILITY_NOINPUTNOOUTPUT;
+ return IO_CAPABILITY_INVALID;
+}
+
+static DBusMessage *create_paired_device(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_adapter *adapter = data;
+ struct btd_device *device;
+ const gchar *address, *agent_path, *capability, *sender;
+ uint8_t cap;
+ int err;
+
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &address,
+ DBUS_TYPE_OBJECT_PATH, &agent_path,
+ DBUS_TYPE_STRING, &capability,
+ DBUS_TYPE_INVALID) == FALSE)
+ return btd_error_invalid_args(msg);
+
+ if (check_address(address) < 0)
+ return btd_error_invalid_args(msg);
+
+ if (!adapter->up)
+ return btd_error_not_ready(msg);
+
+ sender = dbus_message_get_sender(msg);
+ if (adapter->agent &&
+ agent_matches(adapter->agent, sender, agent_path)) {
+ error("Refusing adapter agent usage as device specific one");
+ return btd_error_invalid_args(msg);
+ }
+
+ cap = parse_io_capability(capability);
+ if (cap == IO_CAPABILITY_INVALID)
+ return btd_error_invalid_args(msg);
+
+ device = adapter_find_device(adapter, address);
+ if (!device) {
+ device = create_device_internal(conn, adapter, address,
+ FALSE, &err);
+ if (!device)
+ return btd_error_failed(msg, strerror(-err));
+ }
+
+ if (device_get_type(device) != DEVICE_TYPE_LE)
+ return device_create_bonding(device, conn, msg,
+ agent_path, cap);
+
+ err = device_browse_primary(device, conn, msg, TRUE);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+ return NULL;
+}
+
+static gint device_path_cmp(struct btd_device *device, const gchar *path)
+{
+ const gchar *dev_path = device_get_path(device);
+
+ return strcasecmp(dev_path, path);
+}
+
+static DBusMessage *remove_device(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct btd_adapter *adapter = data;
+ struct btd_device *device;
+ const char *path;
+ GSList *l;
+
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID) == FALSE)
+ return btd_error_invalid_args(msg);
+
+ l = g_slist_find_custom(adapter->devices,
+ path, (GCompareFunc) device_path_cmp);
+ if (!l)
+ return btd_error_does_not_exist(msg);
+
+ device = l->data;
+
+ if (device_is_temporary(device) || device_is_busy(device))
+ return g_dbus_create_error(msg,
+ ERROR_INTERFACE ".DoesNotExist",
+ "Device creation in progress");
+
+ device_set_temporary(device, TRUE);
+
+ if (!device_is_connected(device)) {
+ adapter_remove_device(conn, adapter, device, TRUE);
+ return dbus_message_new_method_return(msg);
+ }
+
+ device_request_disconnect(device, msg);
+ return NULL;
+}
+
+static DBusMessage *find_device(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct btd_adapter *adapter = data;
+ struct btd_device *device;
+ DBusMessage *reply;
+ const gchar *address;
+ GSList *l;
+ const gchar *dev_path;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &address,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ l = g_slist_find_custom(adapter->devices,
+ address, (GCompareFunc) device_address_cmp);
+ if (!l)
+ return btd_error_does_not_exist(msg);
+
+ device = l->data;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dev_path = device_get_path(device);
+
+ dbus_message_append_args(reply,
+ DBUS_TYPE_OBJECT_PATH, &dev_path,
+ DBUS_TYPE_INVALID);
+
+ return reply;
+}
+
+static void agent_removed(struct agent *agent, struct btd_adapter *adapter)
+{
+ adapter_ops->set_io_capability(adapter->dev_id,
+ IO_CAPABILITY_NOINPUTNOOUTPUT);
+
+ adapter->agent = NULL;
+}
+
+static DBusMessage *register_agent(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ const char *path, *name, *capability;
+ struct agent *agent;
+ struct btd_adapter *adapter = data;
+ uint8_t cap;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_STRING, &capability, DBUS_TYPE_INVALID))
+ return NULL;
+
+ if (adapter->agent)
+ return btd_error_already_exists(msg);
+
+ cap = parse_io_capability(capability);
+ if (cap == IO_CAPABILITY_INVALID)
+ return btd_error_invalid_args(msg);
+
+ name = dbus_message_get_sender(msg);
+
+ agent = agent_create(adapter, name, path, cap,
+ (agent_remove_cb) agent_removed, adapter);
+ if (!agent)
+ return btd_error_failed(msg, "Failed to create a new agent");
+
+ adapter->agent = agent;
+
+ DBG("Agent registered for hci%d at %s:%s", adapter->dev_id, name,
+ path);
+
+ adapter_ops->set_io_capability(adapter->dev_id, cap);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_agent(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ const char *path, *name;
+ struct btd_adapter *adapter = data;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ return NULL;
+
+ name = dbus_message_get_sender(msg);
+
+ if (!adapter->agent || !agent_matches(adapter->agent, name, path))
+ return btd_error_does_not_exist(msg);
+
+ agent_free(adapter->agent);
+ adapter->agent = NULL;
+
+ return dbus_message_new_method_return(msg);
+}
+
+static GDBusMethodTable adapter_methods[] = {
+ { "GetProperties", "", "a{sv}",get_properties },
+ { "SetProperty", "sv", "", set_property,
+ G_DBUS_METHOD_FLAG_ASYNC},
+ { "RequestSession", "", "", request_session,
+ G_DBUS_METHOD_FLAG_ASYNC},
+ { "ReleaseSession", "", "", release_session },
+ { "StartDiscovery", "", "", adapter_start_discovery },
+ { "StopDiscovery", "", "", adapter_stop_discovery,
+ G_DBUS_METHOD_FLAG_ASYNC},
+ { "ListDevices", "", "ao", list_devices,
+ G_DBUS_METHOD_FLAG_DEPRECATED},
+ { "CreateDevice", "s", "o", create_device,
+ G_DBUS_METHOD_FLAG_ASYNC},
+ { "CreatePairedDevice", "sos", "o", create_paired_device,
+ G_DBUS_METHOD_FLAG_ASYNC},
+ { "CancelDeviceCreation","s", "", cancel_device_creation,
+ G_DBUS_METHOD_FLAG_ASYNC},
+ { "RemoveDevice", "o", "", remove_device,
+ G_DBUS_METHOD_FLAG_ASYNC},
+ { "FindDevice", "s", "o", find_device },
+ { "RegisterAgent", "os", "", register_agent },
+ { "UnregisterAgent", "o", "", unregister_agent },
+ { }
+};
+
+static GDBusSignalTable adapter_signals[] = {
+ { "PropertyChanged", "sv" },
+ { "DeviceCreated", "o" },
+ { "DeviceRemoved", "o" },
+ { "DeviceFound", "sa{sv}" },
+ { "DeviceDisappeared", "s" },
+ { }
+};
+
+static void create_stored_device_from_profiles(char *key, char *value,
+ void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ GSList *uuids = bt_string2list(value);
+ struct btd_device *device;
+
+ if (g_slist_find_custom(adapter->devices,
+ key, (GCompareFunc) device_address_cmp))
+ return;
+
+ device = device_create(connection, adapter, key, DEVICE_TYPE_BREDR);
+ if (!device)
+ return;
+
+ device_set_temporary(device, FALSE);
+ adapter->devices = g_slist_append(adapter->devices, device);
+
+ device_probe_drivers(device, uuids);
+
+ g_slist_foreach(uuids, (GFunc) g_free, NULL);
+ g_slist_free(uuids);
+}
+
+struct adapter_keys {
+ struct btd_adapter *adapter;
+ GSList *keys;
+};
+
+static struct link_key_info *get_key_info(const char *addr, const char *value)
+{
+ struct link_key_info *info;
+ char tmp[3];
+ long int l;
+ int i;
+
+ if (strlen(value) < 36) {
+ error("Unexpectedly short (%zu) link key line", strlen(value));
+ return NULL;
+ }
+
+ info = g_new0(struct link_key_info, 1);
+
+ str2ba(addr, &info->bdaddr);
+
+ memset(tmp, 0, sizeof(tmp));
+
+ for (i = 0; i < 16; i++) {
+ memcpy(tmp, value + (i * 2), 2);
+ info->key[i] = (uint8_t) strtol(tmp, NULL, 16);
+ }
+
+ memcpy(tmp, value + 33, 2);
+ info->type = (uint8_t) strtol(tmp, NULL, 10);
+
+ memcpy(tmp, value + 35, 2);
+ l = strtol(tmp, NULL, 10);
+ if (l < 0)
+ l = 0;
+ info->pin_len = l;
+
+ return info;
+}
+
+static void create_stored_device_from_linkkeys(char *key, char *value,
+ void *user_data)
+{
+ struct adapter_keys *keys = user_data;
+ struct btd_adapter *adapter = keys->adapter;
+ struct btd_device *device;
+ struct link_key_info *info;
+
+ info = get_key_info(key, value);
+ if (info)
+ keys->keys = g_slist_append(keys->keys, info);
+
+ if (g_slist_find_custom(adapter->devices, key,
+ (GCompareFunc) device_address_cmp))
+ return;
+
+ device = device_create(connection, adapter, key, DEVICE_TYPE_BREDR);
+ if (device) {
+ device_set_temporary(device, FALSE);
+ adapter->devices = g_slist_append(adapter->devices, device);
+ }
+}
+
+static void create_stored_device_from_blocked(char *key, char *value,
+ void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ struct btd_device *device;
+
+ if (g_slist_find_custom(adapter->devices,
+ key, (GCompareFunc) device_address_cmp))
+ return;
+
+ device = device_create(connection, adapter, key, DEVICE_TYPE_BREDR);
+ if (device) {
+ device_set_temporary(device, FALSE);
+ adapter->devices = g_slist_append(adapter->devices, device);
+ }
+}
+
+static void create_stored_device_from_types(char *key, char *value,
+ void *user_data)
+{
+ GSList *l;
+ struct btd_adapter *adapter = user_data;
+ struct btd_device *device;
+ uint8_t type;
+
+ type = strtol(value, NULL, 16);
+
+ l = g_slist_find_custom(adapter->devices,
+ key, (GCompareFunc) device_address_cmp);
+ if (l) {
+ device = l->data;
+ device_set_type(device, type);
+ return;
+ }
+
+ device = device_create(connection, adapter, key, type);
+ if (device) {
+ device_set_temporary(device, FALSE);
+ adapter->devices = g_slist_append(adapter->devices, device);
+ }
+}
+
+static GSList *string_to_primary_list(char *str)
+{
+ GSList *l = NULL;
+ char **services;
+ int i;
+
+ if (str == NULL)
+ return NULL;
+
+ services = g_strsplit(str, " ", 0);
+ if (services == NULL)
+ return NULL;
+
+ for (i = 0; services[i]; i++) {
+ struct att_primary *prim;
+ int ret;
+
+ prim = g_new0(struct att_primary, 1);
+
+ ret = sscanf(services[i], "%04hX#%04hX#%s", &prim->start,
+ &prim->end, prim->uuid);
+
+ if (ret < 3) {
+ g_free(prim);
+ continue;
+ }
+
+ l = g_slist_append(l, prim);
+ }
+
+ g_strfreev(services);
+
+ return l;
+}
+
+static void create_stored_device_from_primary(char *key, char *value,
+ void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ struct btd_device *device;
+ GSList *services, *uuids, *l;
+
+ l = g_slist_find_custom(adapter->devices,
+ key, (GCompareFunc) device_address_cmp);
+ if (l)
+ device = l->data;
+ else {
+ device = device_create(connection, adapter, key, DEVICE_TYPE_BREDR);
+ if (!device)
+ return;
+
+ device_set_temporary(device, FALSE);
+ adapter->devices = g_slist_append(adapter->devices, device);
+ }
+
+ services = string_to_primary_list(value);
+ if (services == NULL)
+ return;
+
+ for (l = services, uuids = NULL; l; l = l->next) {
+ struct att_primary *prim = l->data;
+ uuids = g_slist_append(uuids, prim->uuid);
+
+ device_add_primary(device, prim);
+ }
+
+ device_probe_drivers(device, uuids);
+
+ g_slist_free(services);
+ g_slist_free(uuids);
+}
+
+static void load_devices(struct btd_adapter *adapter)
+{
+ char filename[PATH_MAX + 1];
+ char srcaddr[18];
+ struct adapter_keys keys = { adapter, NULL };
+ int err;
+
+ ba2str(&adapter->bdaddr, srcaddr);
+
+ create_name(filename, PATH_MAX, STORAGEDIR, srcaddr, "profiles");
+ textfile_foreach(filename, create_stored_device_from_profiles,
+ adapter);
+
+ create_name(filename, PATH_MAX, STORAGEDIR, srcaddr, "primary");
+ textfile_foreach(filename, create_stored_device_from_primary,
+ adapter);
+
+ create_name(filename, PATH_MAX, STORAGEDIR, srcaddr, "linkkeys");
+ textfile_foreach(filename, create_stored_device_from_linkkeys, &keys);
+
+ err = adapter_ops->load_keys(adapter->dev_id, keys.keys,
+ main_opts.debug_keys);
+ if (err < 0) {
+ error("Unable to load keys to adapter_ops: %s (%d)",
+ strerror(-err), -err);
+ g_slist_foreach(keys.keys, (GFunc) g_free, NULL);
+ g_slist_free(keys.keys);
+ }
+
+ create_name(filename, PATH_MAX, STORAGEDIR, srcaddr, "blocked");
+ textfile_foreach(filename, create_stored_device_from_blocked, adapter);
+
+ create_name(filename, PATH_MAX, STORAGEDIR, srcaddr, "types");
+ textfile_foreach(filename, create_stored_device_from_types, adapter);
+}
+
+int btd_adapter_block_address(struct btd_adapter *adapter, bdaddr_t *bdaddr)
+{
+ return adapter_ops->block_device(adapter->dev_id, bdaddr);
+}
+
+int btd_adapter_unblock_address(struct btd_adapter *adapter, bdaddr_t *bdaddr)
+{
+ return adapter_ops->unblock_device(adapter->dev_id, bdaddr);
+}
+
+static void clear_blocked(struct btd_adapter *adapter)
+{
+ int err;
+
+ err = adapter_ops->unblock_device(adapter->dev_id, BDADDR_ANY);
+ if (err < 0)
+ error("Clearing blocked list failed: %s (%d)",
+ strerror(-err), -err);
+}
+
+static void probe_driver(struct btd_adapter *adapter, gpointer user_data)
+{
+ struct btd_adapter_driver *driver = user_data;
+ int err;
+
+ if (!adapter->up)
+ return;
+
+ if (driver->probe == NULL)
+ return;
+
+ err = driver->probe(adapter);
+ if (err < 0) {
+ error("%s: %s (%d)", driver->name, strerror(-err), -err);
+ return;
+ }
+
+ adapter->loaded_drivers = g_slist_prepend(adapter->loaded_drivers,
+ driver);
+}
+
+static void load_drivers(struct btd_adapter *adapter)
+{
+ GSList *l;
+
+ for (l = adapter_drivers; l; l = l->next)
+ probe_driver(adapter, l->data);
+}
+
+static void load_connections(struct btd_adapter *adapter)
+{
+ GSList *l, *conns;
+ int err;
+
+ err = adapter_ops->get_conn_list(adapter->dev_id, &conns);
+ if (err < 0) {
+ error("Unable to fetch existing connections: %s (%d)",
+ strerror(-err), -err);
+ return;
+ }
+
+ for (l = conns; l != NULL; l = g_slist_next(l)) {
+ bdaddr_t *bdaddr = l->data;
+ struct btd_device *device;
+ char address[18];
+
+ ba2str(bdaddr, address);
+ DBG("Adding existing connection to %s", address);
+
+ device = adapter_get_device(connection, adapter, address);
+ if (device)
+ adapter_add_connection(adapter, device);
+ }
+
+ g_slist_foreach(conns, (GFunc) g_free, NULL);
+ g_slist_free(conns);
+}
+
+static int get_discoverable_timeout(const char *src)
+{
+ int timeout;
+
+ if (read_discoverable_timeout(src, &timeout) == 0)
+ return timeout;
+
+ return main_opts.discovto;
+}
+
+static int get_pairable_timeout(const char *src)
+{
+ int timeout;
+
+ if (read_pairable_timeout(src, &timeout) == 0)
+ return timeout;
+
+ return main_opts.pairto;
+}
+
+static void call_adapter_powered_callbacks(struct btd_adapter *adapter,
+ gboolean powered)
+{
+ GSList *l;
+
+ for (l = adapter->powered_callbacks; l; l = l->next) {
+ btd_adapter_powered_cb cb = l->data;
+
+ cb(adapter, powered);
+ }
+}
+
+static void emit_device_disappeared(gpointer data, gpointer user_data)
+{
+ struct remote_dev_info *dev = data;
+ struct btd_adapter *adapter = user_data;
+ char address[18];
+ const char *paddr = address;
+
+ ba2str(&dev->bdaddr, address);
+
+ g_dbus_emit_signal(connection, adapter->path,
+ ADAPTER_INTERFACE, "DeviceDisappeared",
+ DBUS_TYPE_STRING, &paddr,
+ DBUS_TYPE_INVALID);
+
+ adapter->found_devices = g_slist_remove(adapter->found_devices, dev);
+}
+
+static void update_oor_devices(struct btd_adapter *adapter)
+{
+ g_slist_foreach(adapter->oor_devices, emit_device_disappeared, adapter);
+ g_slist_foreach(adapter->oor_devices, (GFunc) dev_info_free, NULL);
+ g_slist_free(adapter->oor_devices);
+ adapter->oor_devices = g_slist_copy(adapter->found_devices);
+}
+
+static gboolean bredr_capable(struct btd_adapter *adapter)
+{
+ struct hci_dev *dev = &adapter->dev;
+
+ return (dev->features[4] & LMP_NO_BREDR) == 0 ? TRUE : FALSE;
+}
+
+static gboolean le_capable(struct btd_adapter *adapter)
+{
+ struct hci_dev *dev = &adapter->dev;
+
+ return (dev->features[4] & LMP_LE &&
+ dev->extfeatures[0] & LMP_HOST_LE) ? TRUE : FALSE;
+}
+
+int adapter_get_discover_type(struct btd_adapter *adapter)
+{
+ gboolean le, bredr;
+ int type;
+
+ le = le_capable(adapter);
+ bredr = bredr_capable(adapter);
+
+ if (le)
+ type = bredr ? DISC_INTERLEAVE : DISC_LE;
+ else
+ type = main_opts.discov_interval ? DISC_STDINQ :
+ DISC_PINQ;
+
+ if (main_opts.name_resolv)
+ type |= DISC_RESOLVNAME;
+
+ return type;
+}
+
+void btd_adapter_get_mode(struct btd_adapter *adapter, uint8_t *mode,
+ uint8_t *on_mode, gboolean *pairable)
+{
+ char str[14], address[18];
+
+ ba2str(&adapter->bdaddr, address);
+
+ if (mode) {
+ if (main_opts.remember_powered == FALSE)
+ *mode = main_opts.mode;
+ else if (read_device_mode(address, str, sizeof(str)) == 0)
+ *mode = get_mode(&adapter->bdaddr, str);
+ else
+ *mode = main_opts.mode;
+ }
+
+ if (on_mode) {
+ if (main_opts.remember_powered == FALSE)
+ *on_mode = get_mode(&adapter->bdaddr, "on");
+ else if (read_on_mode(address, str, sizeof(str)) == 0)
+ *on_mode = get_mode(&adapter->bdaddr, str);
+ else
+ *on_mode = main_opts.mode;
+ }
+
+ if (pairable)
+ *pairable = adapter->pairable;
+}
+
+void btd_adapter_start(struct btd_adapter *adapter)
+{
+ char address[18];
+ uint8_t cls[3];
+ gboolean powered;
+
+ ba2str(&adapter->bdaddr, address);
+
+ adapter->dev_class = 0;
+ adapter->off_requested = FALSE;
+ adapter->up = TRUE;
+ adapter->discov_timeout = get_discoverable_timeout(address);
+ adapter->pairable_timeout = get_pairable_timeout(address);
+ adapter->state = STATE_IDLE;
+ adapter->mode = MODE_CONNECTABLE;
+
+ if (main_opts.le)
+ adapter_ops->enable_le(adapter->dev_id);
+
+ adapter_ops->set_name(adapter->dev_id, adapter->dev.name);
+
+ if (read_local_class(&adapter->bdaddr, cls) < 0) {
+ uint32_t class = htobl(main_opts.class);
+ memcpy(cls, &class, 3);
+ }
+
+ btd_adapter_set_class(adapter, cls[1], cls[0]);
+
+ powered = TRUE;
+ emit_property_changed(connection, adapter->path,
+ ADAPTER_INTERFACE, "Powered",
+ DBUS_TYPE_BOOLEAN, &powered);
+
+ call_adapter_powered_callbacks(adapter, TRUE);
+
+ adapter_ops->disable_cod_cache(adapter->dev_id);
+
+ info("Adapter %s has been enabled", adapter->path);
+}
+
+static void reply_pending_requests(struct btd_adapter *adapter)
+{
+ GSList *l;
+
+ if (!adapter)
+ return;
+
+ /* pending bonding */
+ for (l = adapter->devices; l; l = l->next) {
+ struct btd_device *device = l->data;
+
+ if (device_is_bonding(device, NULL))
+ device_cancel_bonding(device,
+ HCI_OE_USER_ENDED_CONNECTION);
+ }
+}
+
+static void remove_driver(gpointer data, gpointer user_data)
+{
+ struct btd_adapter_driver *driver = data;
+ struct btd_adapter *adapter = user_data;
+
+ if (driver->remove)
+ driver->remove(adapter);
+}
+
+static void unload_drivers(struct btd_adapter *adapter)
+{
+ g_slist_foreach(adapter->loaded_drivers, remove_driver, adapter);
+ g_slist_free(adapter->loaded_drivers);
+ adapter->loaded_drivers = NULL;
+}
+
+static void set_mode_complete(struct btd_adapter *adapter)
+{
+ struct session_req *pending;
+ const char *modestr;
+ int err;
+
+ DBG("");
+
+ /*
+ * g_slist_free is not called after g_slist_foreach because the list is
+ * updated using g_slist_remove in session_remove which is called by
+ * session_free, which is called for each element by g_slist_foreach.
+ */
+ if (adapter->mode == MODE_OFF)
+ g_slist_foreach(adapter->mode_sessions, (GFunc) session_free,
+ NULL);
+
+ if (adapter->pending_mode == NULL)
+ return;
+
+ pending = adapter->pending_mode;
+ adapter->pending_mode = NULL;
+
+ err = (pending->mode != adapter->mode) ? -EINVAL : 0;
+
+ if (pending->msg != NULL) {
+ DBusMessage *msg = pending->msg;
+ DBusMessage *reply;
+
+ if (err < 0)
+ reply = btd_error_failed(msg, strerror(-err));
+ else {
+ if (strcmp(dbus_message_get_member(msg),
+ "SetProperty") == 0)
+ adapter->global_mode = adapter->mode;
+ reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+ }
+
+ g_dbus_send_message(connection, reply);
+ }
+
+ modestr = mode2str(adapter->mode);
+
+ DBG("%s", modestr);
+
+ /* restore if the mode doesn't matches the pending */
+ if (err != 0) {
+ write_device_mode(&adapter->bdaddr, modestr);
+ error("unable to set mode: %s", mode2str(pending->mode));
+ }
+
+ session_unref(pending);
+}
+
+int btd_adapter_stop(struct btd_adapter *adapter)
+{
+ gboolean powered, discoverable, pairable;
+
+ /* cancel pending timeout */
+ if (adapter->discov_timeout_id) {
+ g_source_remove(adapter->discov_timeout_id);
+ adapter->discov_timeout_id = 0;
+ }
+
+ /* check pending requests */
+ reply_pending_requests(adapter);
+
+ stop_discovery(adapter, FALSE);
+
+ if (adapter->disc_sessions) {
+ g_slist_foreach(adapter->disc_sessions, (GFunc) session_free,
+ NULL);
+ g_slist_free(adapter->disc_sessions);
+ adapter->disc_sessions = NULL;
+ }
+
+ while (adapter->connections) {
+ struct btd_device *device = adapter->connections->data;
+ adapter_remove_connection(adapter, device);
+ }
+
+ if (adapter->scan_mode == (SCAN_PAGE | SCAN_INQUIRY)) {
+ discoverable = FALSE;
+ emit_property_changed(connection, adapter->path,
+ ADAPTER_INTERFACE, "Discoverable",
+ DBUS_TYPE_BOOLEAN, &discoverable);
+ }
+
+ if ((adapter->scan_mode & SCAN_PAGE) && adapter->pairable == TRUE) {
+ pairable = FALSE;
+ emit_property_changed(connection, adapter->path,
+ ADAPTER_INTERFACE, "Pairable",
+ DBUS_TYPE_BOOLEAN, &pairable);
+ }
+
+ powered = FALSE;
+ emit_property_changed(connection, adapter->path, ADAPTER_INTERFACE,
+ "Powered", DBUS_TYPE_BOOLEAN, &powered);
+
+ adapter->up = 0;
+ adapter->scan_mode = SCAN_DISABLED;
+ adapter->mode = MODE_OFF;
+ adapter->state = STATE_IDLE;
+ adapter->off_requested = FALSE;
+ adapter->name_stored = FALSE;
+
+ call_adapter_powered_callbacks(adapter, FALSE);
+
+ info("Adapter %s has been disabled", adapter->path);
+
+ set_mode_complete(adapter);
+
+ return 0;
+}
+
+int adapter_update_ssp_mode(struct btd_adapter *adapter, uint8_t mode)
+{
+ struct hci_dev *dev = &adapter->dev;
+
+ dev->ssp_mode = mode;
+
+ return 0;
+}
+
+static void adapter_free(gpointer user_data)
+{
+ struct btd_adapter *adapter = user_data;
+
+ agent_free(adapter->agent);
+ adapter->agent = NULL;
+
+ DBG("%p", adapter);
+
+ if (adapter->auth_idle_id)
+ g_source_remove(adapter->auth_idle_id);
+
+ sdp_list_free(adapter->services, NULL);
+
+ g_slist_foreach(adapter->found_devices, (GFunc) dev_info_free, NULL);
+ g_slist_free(adapter->found_devices);
+
+ g_slist_free(adapter->oor_devices);
+
+ g_free(adapter->path);
+ g_free(adapter);
+}
+
+struct btd_adapter *btd_adapter_ref(struct btd_adapter *adapter)
+{
+ adapter->ref++;
+
+ DBG("%p: ref=%d", adapter, adapter->ref);
+
+ return adapter;
+}
+
+void btd_adapter_unref(struct btd_adapter *adapter)
+{
+ gchar *path;
+
+ adapter->ref--;
+
+ DBG("%p: ref=%d", adapter, adapter->ref);
+
+ if (adapter->ref > 0)
+ return;
+
+ path = g_strdup(adapter->path);
+
+ g_dbus_unregister_interface(connection, path, ADAPTER_INTERFACE);
+
+ g_free(path);
+}
+
+gboolean adapter_init(struct btd_adapter *adapter)
+{
+ struct hci_version ver;
+ struct hci_dev *dev;
+ int err;
+
+ /* adapter_ops makes sure that newly registered adapters always
+ * start off as powered */
+ adapter->up = TRUE;
+
+ adapter_ops->read_bdaddr(adapter->dev_id, &adapter->bdaddr);
+
+ if (bacmp(&adapter->bdaddr, BDADDR_ANY) == 0) {
+ error("No address available for hci%d", adapter->dev_id);
+ return FALSE;
+ }
+
+ err = adapter_ops->read_local_version(adapter->dev_id, &ver);
+ if (err < 0) {
+ error("Can't read version info for hci%d: %s (%d)",
+ adapter->dev_id, strerror(-err), -err);
+ return FALSE;
+ }
+
+ dev = &adapter->dev;
+
+ dev->hci_rev = ver.hci_rev;
+ dev->lmp_ver = ver.lmp_ver;
+ dev->lmp_subver = ver.lmp_subver;
+ dev->manufacturer = ver.manufacturer;
+
+ err = adapter_ops->read_local_features(adapter->dev_id, dev->features);
+ if (err < 0) {
+ error("Can't read features for hci%d: %s (%d)",
+ adapter->dev_id, strerror(-err), -err);
+ return FALSE;
+ }
+
+ if (read_local_name(&adapter->bdaddr, adapter->dev.name) < 0)
+ expand_name(adapter->dev.name, MAX_NAME_LENGTH, main_opts.name,
+ adapter->dev_id);
+
+ if (main_opts.attrib_server)
+ attrib_gap_set(GATT_CHARAC_DEVICE_NAME,
+ (const uint8_t *) dev->name, strlen(dev->name));
+
+ sdp_init_services_list(&adapter->bdaddr);
+ load_drivers(adapter);
+ clear_blocked(adapter);
+ load_devices(adapter);
+
+ /* Set pairable mode */
+ if (read_device_pairable(&adapter->bdaddr, &adapter->pairable) < 0)
+ adapter->pairable = TRUE;
+
+ /* retrieve the active connections: address the scenario where
+ * the are active connections before the daemon've started */
+ load_connections(adapter);
+
+ adapter->initialized = TRUE;
+
+ return TRUE;
+}
+
+struct btd_adapter *adapter_create(DBusConnection *conn, int id)
+{
+ char path[MAX_PATH_LENGTH];
+ struct btd_adapter *adapter;
+ const char *base_path = manager_get_base_path();
+
+ if (!connection)
+ connection = conn;
+
+ adapter = g_try_new0(struct btd_adapter, 1);
+ if (!adapter) {
+ error("adapter_create: failed to alloc memory for hci%d", id);
+ return NULL;
+ }
+
+ adapter->dev_id = id;
+
+ snprintf(path, sizeof(path), "%s/hci%d", base_path, id);
+ adapter->path = g_strdup(path);
+
+ if (!g_dbus_register_interface(conn, path, ADAPTER_INTERFACE,
+ adapter_methods, adapter_signals, NULL,
+ adapter, adapter_free)) {
+ error("Adapter interface init failed on path %s", path);
+ adapter_free(adapter);
+ return NULL;
+ }
+
+ return btd_adapter_ref(adapter);
+}
+
+void adapter_remove(struct btd_adapter *adapter)
+{
+ GSList *l;
+
+ DBG("Removing adapter %s", adapter->path);
+
+ for (l = adapter->devices; l; l = l->next)
+ device_remove(l->data, FALSE);
+ g_slist_free(adapter->devices);
+
+ unload_drivers(adapter);
+
+ /* Return adapter to down state if it was not up on init */
+ adapter_ops->restore_powered(adapter->dev_id);
+
+ btd_adapter_unref(adapter);
+}
+
+uint16_t adapter_get_dev_id(struct btd_adapter *adapter)
+{
+ return adapter->dev_id;
+}
+
+const gchar *adapter_get_path(struct btd_adapter *adapter)
+{
+ if (!adapter)
+ return NULL;
+
+ return adapter->path;
+}
+
+void adapter_get_address(struct btd_adapter *adapter, bdaddr_t *bdaddr)
+{
+ bacpy(bdaddr, &adapter->bdaddr);
+}
+
+void adapter_set_state(struct btd_adapter *adapter, int state)
+{
+ const char *path = adapter->path;
+ gboolean discov_active;
+ int previous, type;
+
+ if (adapter->state == state)
+ return;
+
+ previous = adapter->state;
+ adapter->state = state;
+
+ type = adapter_get_discover_type(adapter);
+
+ switch (state) {
+ case STATE_STDINQ:
+ case STATE_PINQ:
+ discov_active = TRUE;
+
+ /* Started a new session while resolving names ? */
+ if (previous & STATE_RESOLVNAME)
+ return;
+ break;
+ case STATE_LE_SCAN:
+ /* Scanning enabled */
+ if (adapter->disc_sessions) {
+ adapter->stop_discov_id = g_timeout_add(5120,
+ stop_scanning,
+ adapter);
+
+ /* For dual mode: don't send "Discovering = TRUE" */
+ if (bredr_capable(adapter) == TRUE)
+ return;
+ }
+
+ /* LE only */
+ discov_active = TRUE;
+
+ break;
+ case STATE_IDLE:
+ /*
+ * Interleave: from inquiry to scanning. Interleave is not
+ * applicable to requests triggered by external applications.
+ */
+ if (adapter->disc_sessions && (type & DISC_INTERLEAVE) &&
+ (previous & STATE_STDINQ)) {
+ adapter_ops->start_scanning(adapter->dev_id);
+ return;
+ }
+ /* BR/EDR only: inquiry finished */
+ discov_active = FALSE;
+ break;
+ default:
+ discov_active = FALSE;
+ break;
+ }
+
+ if (discov_active == FALSE) {
+ if (type & DISC_RESOLVNAME) {
+ if (adapter_resolve_names(adapter) == 0) {
+ adapter->state |= STATE_RESOLVNAME;
+ return;
+ }
+ }
+
+ update_oor_devices(adapter);
+ } else if (adapter->disc_sessions && main_opts.discov_interval)
+ adapter->scheduler_id = g_timeout_add_seconds(
+ main_opts.discov_interval,
+ (GSourceFunc) start_discovery,
+ adapter);
+
+ emit_property_changed(connection, path,
+ ADAPTER_INTERFACE, "Discovering",
+ DBUS_TYPE_BOOLEAN, &discov_active);
+}
+
+int adapter_get_state(struct btd_adapter *adapter)
+{
+ return adapter->state;
+}
+
+struct remote_dev_info *adapter_search_found_devices(struct btd_adapter *adapter,
+ struct remote_dev_info *match)
+{
+ GSList *l;
+
+ l = g_slist_find_custom(adapter->found_devices, match,
+ (GCompareFunc) found_device_cmp);
+ if (l)
+ return l->data;
+
+ return NULL;
+}
+
+static int dev_rssi_cmp(struct remote_dev_info *d1, struct remote_dev_info *d2)
+{
+ int rssi1, rssi2;
+
+ rssi1 = d1->rssi < 0 ? -d1->rssi : d1->rssi;
+ rssi2 = d2->rssi < 0 ? -d2->rssi : d2->rssi;
+
+ return rssi1 - rssi2;
+}
+
+static void append_dict_valist(DBusMessageIter *iter,
+ const char *first_key,
+ va_list var_args)
+{
+ DBusMessageIter dict;
+ const char *key;
+ int type;
+ int n_elements;
+ void *val;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ key = first_key;
+ while (key) {
+ type = va_arg(var_args, int);
+ val = va_arg(var_args, void *);
+ if (type == DBUS_TYPE_ARRAY) {
+ n_elements = va_arg(var_args, int);
+ if (n_elements > 0)
+ dict_append_array(&dict, key, DBUS_TYPE_STRING,
+ val, n_elements);
+ } else
+ dict_append_entry(&dict, key, type, val);
+ key = va_arg(var_args, char *);
+ }
+
+ dbus_message_iter_close_container(iter, &dict);
+}
+
+static void emit_device_found(const char *path, const char *address,
+ const char *first_key, ...)
+{
+ DBusMessage *signal;
+ DBusMessageIter iter;
+ va_list var_args;
+
+ signal = dbus_message_new_signal(path, ADAPTER_INTERFACE,
+ "DeviceFound");
+ if (!signal) {
+ error("Unable to allocate new %s.DeviceFound signal",
+ ADAPTER_INTERFACE);
+ return;
+ }
+ dbus_message_iter_init_append(signal, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &address);
+
+ va_start(var_args, first_key);
+ append_dict_valist(&iter, first_key, var_args);
+ va_end(var_args);
+
+ g_dbus_send_message(connection, signal);
+}
+
+static char **strlist2array(GSList *list)
+{
+ GSList *l;
+ unsigned int i, n;
+ char **array;
+
+ if (list == NULL)
+ return NULL;
+
+ n = g_slist_length(list);
+ array = g_new0(char *, n + 1);
+
+ for (l = list, i = 0; l; l = l->next, i++)
+ array[i] = g_strdup((const gchar *) l->data);
+
+ return array;
+}
+
+void adapter_emit_device_found(struct btd_adapter *adapter,
+ struct remote_dev_info *dev)
+{
+ struct btd_device *device;
+ char peer_addr[18], local_addr[18];
+ const char *icon, *paddr = peer_addr;
+ dbus_bool_t paired = FALSE;
+ dbus_int16_t rssi = dev->rssi;
+ char *alias;
+ size_t uuid_count;
+
+ ba2str(&dev->bdaddr, peer_addr);
+ ba2str(&adapter->bdaddr, local_addr);
+
+ device = adapter_find_device(adapter, paddr);
+ if (device)
+ paired = device_is_paired(device);
+
+ /* The uuids string array is updated only if necessary */
+ uuid_count = g_slist_length(dev->services);
+ if (dev->services && dev->uuid_count != uuid_count) {
+ g_strfreev(dev->uuids);
+ dev->uuids = strlist2array(dev->services);
+ dev->uuid_count = uuid_count;
+ }
+
+ if (dev->le) {
+ gboolean broadcaster;
+
+ if (dev->flags & (EIR_LIM_DISC | EIR_GEN_DISC))
+ broadcaster = FALSE;
+ else
+ broadcaster = TRUE;
+
+ emit_device_found(adapter->path, paddr,
+ "Address", DBUS_TYPE_STRING, &paddr,
+ "RSSI", DBUS_TYPE_INT16, &rssi,
+ "Name", DBUS_TYPE_STRING, &dev->name,
+ "Paired", DBUS_TYPE_BOOLEAN, &paired,
+ "Broadcaster", DBUS_TYPE_BOOLEAN, &broadcaster,
+ "UUIDs", DBUS_TYPE_ARRAY, &dev->uuids, uuid_count,
+ NULL);
+ return;
+ }
+
+ icon = class_to_icon(dev->class);
+
+ if (!dev->alias) {
+ if (!dev->name) {
+ alias = g_strdup(peer_addr);
+ g_strdelimit(alias, ":", '-');
+ } else
+ alias = g_strdup(dev->name);
+ } else
+ alias = g_strdup(dev->alias);
+
+ emit_device_found(adapter->path, paddr,
+ "Address", DBUS_TYPE_STRING, &paddr,
+ "Class", DBUS_TYPE_UINT32, &dev->class,
+ "Icon", DBUS_TYPE_STRING, &icon,
+ "RSSI", DBUS_TYPE_INT16, &rssi,
+ "Name", DBUS_TYPE_STRING, &dev->name,
+ "Alias", DBUS_TYPE_STRING, &alias,
+ "LegacyPairing", DBUS_TYPE_BOOLEAN, &dev->legacy,
+ "Paired", DBUS_TYPE_BOOLEAN, &paired,
+ "UUIDs", DBUS_TYPE_ARRAY, &dev->uuids, uuid_count,
+ NULL);
+
+ g_free(alias);
+}
+
+static struct remote_dev_info *get_found_dev(struct btd_adapter *adapter,
+ const bdaddr_t *bdaddr,
+ gboolean *new_dev)
+{
+ struct remote_dev_info *dev, match;
+
+ memset(&match, 0, sizeof(struct remote_dev_info));
+ bacpy(&match.bdaddr, bdaddr);
+ match.name_status = NAME_ANY;
+
+ dev = adapter_search_found_devices(adapter, &match);
+ if (dev) {
+ *new_dev = FALSE;
+ /* Out of range list update */
+ adapter->oor_devices = g_slist_remove(adapter->oor_devices,
+ dev);
+ } else {
+ *new_dev = TRUE;
+ dev = g_new0(struct remote_dev_info, 1);
+ bacpy(&dev->bdaddr, bdaddr);
+ adapter->found_devices = g_slist_prepend(adapter->found_devices,
+ dev);
+ }
+
+ return dev;
+}
+
+static void remove_same_uuid(gpointer data, gpointer user_data)
+{
+ struct remote_dev_info *dev = user_data;
+ GSList *l;
+
+ for (l = dev->services; l; l = l->next) {
+ char *current_uuid = l->data;
+ char *new_uuid = data;
+
+ if (strcmp(current_uuid, new_uuid) == 0) {
+ g_free(current_uuid);
+ dev->services = g_slist_delete_link(dev->services, l);
+ break;
+ }
+ }
+}
+
+static void dev_prepend_uuid(gpointer data, gpointer user_data)
+{
+ struct remote_dev_info *dev = user_data;
+ char *new_uuid = data;
+
+ dev->services = g_slist_prepend(dev->services, g_strdup(new_uuid));
+}
+
+void adapter_update_device_from_info(struct btd_adapter *adapter,
+ bdaddr_t bdaddr, int8_t rssi,
+ uint8_t evt_type, const char *name,
+ GSList *services, int flags)
+{
+ struct remote_dev_info *dev;
+ gboolean new_dev;
+
+ dev = get_found_dev(adapter, &bdaddr, &new_dev);
+
+ if (new_dev) {
+ dev->le = TRUE;
+ dev->evt_type = evt_type;
+ } else if (dev->rssi == rssi)
+ return;
+
+ dev->rssi = rssi;
+
+ adapter->found_devices = g_slist_sort(adapter->found_devices,
+ (GCompareFunc) dev_rssi_cmp);
+
+ g_slist_foreach(services, remove_same_uuid, dev);
+ g_slist_foreach(services, dev_prepend_uuid, dev);
+
+ if (flags >= 0)
+ dev->flags = flags;
+
+ if (name) {
+ g_free(dev->name);
+ dev->name = g_strdup(name);
+ }
+
+ /* FIXME: check if other information was changed before emitting the
+ * signal */
+ adapter_emit_device_found(adapter, dev);
+}
+
+void adapter_update_found_devices(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+ int8_t rssi, uint32_t class, const char *name,
+ const char *alias, gboolean legacy,
+ GSList *services, name_status_t name_status)
+{
+ struct remote_dev_info *dev;
+ gboolean new_dev;
+
+ dev = get_found_dev(adapter, bdaddr, &new_dev);
+
+ if (new_dev) {
+ if (name)
+ dev->name = g_strdup(name);
+
+ if (alias)
+ dev->alias = g_strdup(alias);
+
+ dev->le = FALSE;
+ dev->class = class;
+ dev->legacy = legacy;
+ dev->name_status = name_status;
+ } else if (dev->rssi == rssi)
+ return;
+
+ dev->rssi = rssi;
+
+ adapter->found_devices = g_slist_sort(adapter->found_devices,
+ (GCompareFunc) dev_rssi_cmp);
+
+ g_slist_foreach(services, remove_same_uuid, dev);
+ g_slist_foreach(services, dev_prepend_uuid, dev);
+
+ adapter_emit_device_found(adapter, dev);
+}
+
+int adapter_remove_found_device(struct btd_adapter *adapter, bdaddr_t *bdaddr)
+{
+ struct remote_dev_info *dev, match;
+
+ memset(&match, 0, sizeof(struct remote_dev_info));
+ bacpy(&match.bdaddr, bdaddr);
+
+ dev = adapter_search_found_devices(adapter, &match);
+ if (!dev)
+ return -1;
+
+ dev->name_status = NAME_NOT_REQUIRED;
+
+ return 0;
+}
+
+void adapter_mode_changed(struct btd_adapter *adapter, uint8_t scan_mode)
+{
+ const gchar *path = adapter_get_path(adapter);
+ gboolean discoverable, pairable;
+
+ DBG("old 0x%02x new 0x%02x", adapter->scan_mode, scan_mode);
+
+ if (adapter->scan_mode == scan_mode)
+ return;
+
+ adapter_remove_discov_timeout(adapter);
+
+ switch (scan_mode) {
+ case SCAN_DISABLED:
+ adapter->mode = MODE_OFF;
+ discoverable = FALSE;
+ pairable = FALSE;
+ break;
+ case SCAN_PAGE:
+ adapter->mode = MODE_CONNECTABLE;
+ discoverable = FALSE;
+ pairable = adapter->pairable;
+ break;
+ case (SCAN_PAGE | SCAN_INQUIRY):
+ adapter->mode = MODE_DISCOVERABLE;
+ discoverable = TRUE;
+ pairable = adapter->pairable;
+ if (adapter->discov_timeout != 0)
+ adapter_set_discov_timeout(adapter,
+ adapter->discov_timeout);
+ break;
+ case SCAN_INQUIRY:
+ /* Address the scenario where a low-level application like
+ * hciconfig changed the scan mode */
+ if (adapter->discov_timeout != 0)
+ adapter_set_discov_timeout(adapter,
+ adapter->discov_timeout);
+
+ /* ignore, this event should not be sent */
+ default:
+ /* ignore, reserved */
+ return;
+ }
+
+ /* If page scanning gets toggled emit the Pairable property */
+ if ((adapter->scan_mode & SCAN_PAGE) != (scan_mode & SCAN_PAGE))
+ emit_property_changed(connection, adapter->path,
+ ADAPTER_INTERFACE, "Pairable",
+ DBUS_TYPE_BOOLEAN, &pairable);
+
+ if (!discoverable)
+ adapter_set_limited_discoverable(adapter, FALSE);
+
+ emit_property_changed(connection, path,
+ ADAPTER_INTERFACE, "Discoverable",
+ DBUS_TYPE_BOOLEAN, &discoverable);
+
+ adapter->scan_mode = scan_mode;
+
+ set_mode_complete(adapter);
+}
+
+struct agent *adapter_get_agent(struct btd_adapter *adapter)
+{
+ if (!adapter || !adapter->agent)
+ return NULL;
+
+ return adapter->agent;
+}
+
+void adapter_add_connection(struct btd_adapter *adapter,
+ struct btd_device *device)
+{
+ if (g_slist_find(adapter->connections, device)) {
+ error("Device is already marked as connected");
+ return;
+ }
+
+ device_add_connection(device, connection);
+
+ adapter->connections = g_slist_append(adapter->connections, device);
+}
+
+void adapter_remove_connection(struct btd_adapter *adapter,
+ struct btd_device *device)
+{
+ bdaddr_t bdaddr;
+
+ DBG("");
+
+ if (!g_slist_find(adapter->connections, device)) {
+ error("No matching connection for device");
+ return;
+ }
+
+ device_remove_connection(device, connection);
+
+ adapter->connections = g_slist_remove(adapter->connections, device);
+
+ /* clean pending HCI cmds */
+ device_get_address(device, &bdaddr);
+
+ if (device_is_authenticating(device))
+ device_cancel_authentication(device, TRUE);
+
+ if (device_is_temporary(device)) {
+ const char *path = device_get_path(device);
+
+ DBG("Removing temporary device %s", path);
+ adapter_remove_device(connection, adapter, device, TRUE);
+ }
+}
+
+gboolean adapter_has_discov_sessions(struct btd_adapter *adapter)
+{
+ if (!adapter || !adapter->disc_sessions)
+ return FALSE;
+
+ return TRUE;
+}
+
+void adapter_suspend_discovery(struct btd_adapter *adapter)
+{
+ if (adapter->disc_sessions == NULL ||
+ adapter->state & STATE_SUSPENDED)
+ return;
+
+ DBG("Suspending discovery");
+
+ stop_discovery(adapter, TRUE);
+ adapter->state |= STATE_SUSPENDED;
+}
+
+void adapter_resume_discovery(struct btd_adapter *adapter)
+{
+ if (adapter->disc_sessions == NULL)
+ return;
+
+ DBG("Resuming discovery");
+
+ adapter->state &= ~STATE_SUSPENDED;
+ start_discovery(adapter);
+}
+
+int btd_register_adapter_driver(struct btd_adapter_driver *driver)
+{
+ adapter_drivers = g_slist_append(adapter_drivers, driver);
+
+ if (driver->probe == NULL)
+ return 0;
+
+ manager_foreach_adapter(probe_driver, driver);
+
+ return 0;
+}
+
+static void unload_driver(struct btd_adapter *adapter, gpointer data)
+{
+ adapter->loaded_drivers = g_slist_remove(adapter->loaded_drivers, data);
+}
+
+void btd_unregister_adapter_driver(struct btd_adapter_driver *driver)
+{
+ adapter_drivers = g_slist_remove(adapter_drivers, driver);
+
+ manager_foreach_adapter(unload_driver, driver);
+}
+
+static void agent_auth_cb(struct agent *agent, DBusError *derr,
+ void *user_data)
+{
+ struct service_auth *auth = user_data;
+
+ device_set_authorizing(auth->device, FALSE);
+
+ auth->cb(derr, auth->user_data);
+}
+
+static gboolean auth_idle_cb(gpointer user_data)
+{
+ struct service_auth *auth = user_data;
+ struct btd_adapter *adapter = auth->adapter;
+
+ adapter->auth_idle_id = 0;
+
+ auth->cb(NULL, auth->user_data);
+
+ return FALSE;
+}
+
+static int adapter_authorize(struct btd_adapter *adapter, const bdaddr_t *dst,
+ const char *uuid, service_auth_cb cb,
+ void *user_data)
+{
+ struct service_auth *auth;
+ struct btd_device *device;
+ struct agent *agent;
+ char address[18];
+ const gchar *dev_path;
+ int err;
+
+ ba2str(dst, address);
+ device = adapter_find_device(adapter, address);
+ if (!device)
+ return -EPERM;
+
+ /* Device connected? */
+ if (!g_slist_find(adapter->connections, device))
+ return -ENOTCONN;
+
+ if (adapter->auth_idle_id)
+ return -EBUSY;
+
+ auth = g_try_new0(struct service_auth, 1);
+ if (!auth)
+ return -ENOMEM;
+
+ auth->cb = cb;
+ auth->user_data = user_data;
+ auth->device = device;
+ auth->adapter = adapter;
+
+ if (device_is_trusted(device) == TRUE) {
+ adapter->auth_idle_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,
+ auth_idle_cb, auth,
+ g_free);
+ return 0;
+ }
+
+ agent = device_get_agent(device);
+ if (!agent) {
+ g_free(auth);
+ return -EPERM;
+ }
+
+ dev_path = device_get_path(device);
+
+ err = agent_authorize(agent, dev_path, uuid, agent_auth_cb, auth, g_free);
+ if (err < 0)
+ g_free(auth);
+ else
+ device_set_authorizing(device, TRUE);
+
+ return err;
+}
+
+int btd_request_authorization(const bdaddr_t *src, const bdaddr_t *dst,
+ const char *uuid, service_auth_cb cb,
+ void *user_data)
+{
+ struct btd_adapter *adapter;
+ GSList *l;
+
+ if (bacmp(src, BDADDR_ANY) != 0) {
+ adapter = manager_find_adapter(src);
+ if (!adapter)
+ return -EPERM;
+
+ return adapter_authorize(adapter, dst, uuid, cb, user_data);
+ }
+
+ for (l = manager_get_adapters(); l != NULL; l = g_slist_next(l)) {
+ int err;
+
+ adapter = l->data;
+
+ err = adapter_authorize(adapter, dst, uuid, cb, user_data);
+ if (err == 0)
+ return 0;
+ }
+
+ return -EPERM;
+}
+
+int btd_cancel_authorization(const bdaddr_t *src, const bdaddr_t *dst)
+{
+ struct btd_adapter *adapter = manager_find_adapter(src);
+ struct btd_device *device;
+ struct agent *agent;
+ char address[18];
+ int err;
+
+ if (!adapter)
+ return -EPERM;
+
+ ba2str(dst, address);
+ device = adapter_find_device(adapter, address);
+ if (!device)
+ return -EPERM;
+
+ if (adapter->auth_idle_id) {
+ g_source_remove(adapter->auth_idle_id);
+ adapter->auth_idle_id = 0;
+ return 0;
+ }
+
+ /*
+ * FIXME: Cancel fails if authorization is requested to adapter's
+ * agent and in the meanwhile CreatePairedDevice is called.
+ */
+
+ agent = device_get_agent(device);
+ if (!agent)
+ return -EPERM;
+
+ err = agent_cancel(agent);
+
+ if (err == 0)
+ device_set_authorizing(device, FALSE);
+
+ return err;
+}
+
+static gchar *adapter_any_path = NULL;
+static int adapter_any_refcount = 0;
+
+const char *adapter_any_get_path(void)
+{
+ return adapter_any_path;
+}
+
+const char *btd_adapter_any_request_path(void)
+{
+ if (adapter_any_refcount++ > 0)
+ return adapter_any_path;
+
+ adapter_any_path = g_strdup_printf("%s/any", manager_get_base_path());
+
+ return adapter_any_path;
+}
+
+void btd_adapter_any_release_path(void)
+{
+ adapter_any_refcount--;
+
+ if (adapter_any_refcount > 0)
+ return;
+
+ g_free(adapter_any_path);
+ adapter_any_path = NULL;
+}
+
+gboolean adapter_is_pairable(struct btd_adapter *adapter)
+{
+ return adapter->pairable;
+}
+
+gboolean adapter_powering_down(struct btd_adapter *adapter)
+{
+ return adapter->off_requested;
+}
+
+int btd_adapter_restore_powered(struct btd_adapter *adapter)
+{
+ char mode[14], address[18];
+ gboolean discoverable;
+
+ if (!adapter_ops)
+ return -EINVAL;
+
+ if (!main_opts.remember_powered)
+ return -EINVAL;
+
+ if (adapter->up)
+ return 0;
+
+ ba2str(&adapter->bdaddr, address);
+ if (read_device_mode(address, mode, sizeof(mode)) == 0 &&
+ g_str_equal(mode, "off"))
+ return 0;
+
+ discoverable = get_mode(&adapter->bdaddr, mode) == MODE_DISCOVERABLE;
+
+ return adapter_ops->set_powered(adapter->dev_id, TRUE);
+}
+
+int btd_adapter_switch_online(struct btd_adapter *adapter)
+{
+ if (!adapter_ops)
+ return -EINVAL;
+
+ if (adapter->up)
+ return 0;
+
+ return adapter_ops->set_powered(adapter->dev_id, TRUE);
+}
+
+int btd_adapter_switch_offline(struct btd_adapter *adapter)
+{
+ if (!adapter_ops)
+ return -EINVAL;
+
+ if (!adapter->up)
+ return 0;
+
+ return adapter_ops->set_powered(adapter->dev_id, FALSE);
+}
+
+int btd_register_adapter_ops(struct btd_adapter_ops *ops, gboolean priority)
+{
+ if (ops->setup == NULL)
+ return -EINVAL;
+
+ if (priority)
+ ops_candidates = g_slist_prepend(ops_candidates, ops);
+ else
+ ops_candidates = g_slist_append(ops_candidates, ops);
+
+ return 0;
+}
+
+void btd_adapter_cleanup_ops(struct btd_adapter_ops *ops)
+{
+ ops_candidates = g_slist_remove(ops_candidates, ops);
+ ops->cleanup();
+
+ if (adapter_ops == ops)
+ adapter_ops = NULL;
+}
+
+int adapter_ops_setup(void)
+{
+ GSList *l;
+ int ret;
+
+ if (!ops_candidates)
+ return -EINVAL;
+
+ for (l = ops_candidates; l != NULL; l = g_slist_next(l)) {
+ struct btd_adapter_ops *ops = l->data;
+
+ ret = ops->setup();
+ if (ret < 0)
+ continue;
+
+ adapter_ops = ops;
+ break;
+ }
+
+ return ret;
+}
+
+void btd_adapter_register_powered_callback(struct btd_adapter *adapter,
+ btd_adapter_powered_cb cb)
+{
+ adapter->powered_callbacks =
+ g_slist_append(adapter->powered_callbacks, cb);
+}
+
+void btd_adapter_unregister_powered_callback(struct btd_adapter *adapter,
+ btd_adapter_powered_cb cb)
+{
+ adapter->powered_callbacks =
+ g_slist_remove(adapter->powered_callbacks, cb);
+}
+
+int btd_adapter_set_fast_connectable(struct btd_adapter *adapter,
+ gboolean enable)
+{
+ if (!adapter_ops)
+ return -EINVAL;
+
+ if (!adapter->up)
+ return -EINVAL;
+
+ return adapter_ops->set_fast_connectable(adapter->dev_id, enable);
+}
+
+int btd_adapter_read_clock(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+ int which, int timeout, uint32_t *clock,
+ uint16_t *accuracy)
+{
+ if (!adapter_ops)
+ return -EINVAL;
+
+ if (!adapter->up)
+ return -EINVAL;
+
+ return adapter_ops->read_clock(adapter->dev_id, bdaddr, which,
+ timeout, clock, accuracy);
+}
+
+int btd_adapter_disconnect_device(struct btd_adapter *adapter, bdaddr_t *bdaddr)
+{
+ return adapter_ops->disconnect(adapter->dev_id, bdaddr);
+}
+
+int btd_adapter_remove_bonding(struct btd_adapter *adapter, bdaddr_t *bdaddr)
+{
+ return adapter_ops->remove_bonding(adapter->dev_id, bdaddr);
+}
+
+int btd_adapter_pincode_reply(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+ const char *pin)
+{
+ return adapter_ops->pincode_reply(adapter->dev_id, bdaddr, pin);
+}
+
+int btd_adapter_confirm_reply(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+ gboolean success)
+{
+ return adapter_ops->confirm_reply(adapter->dev_id, bdaddr, success);
+}
+
+int btd_adapter_passkey_reply(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+ uint32_t passkey)
+{
+ return adapter_ops->passkey_reply(adapter->dev_id, bdaddr, passkey);
+}
+
+void btd_adapter_update_local_ext_features(struct btd_adapter *adapter,
+ const uint8_t *features)
+{
+ struct hci_dev *dev = &adapter->dev;
+
+ memcpy(dev->extfeatures, features, 8);
+}
+
+int btd_adapter_encrypt_link(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+ bt_hci_result_t cb, gpointer user_data)
+{
+ return adapter_ops->encrypt_link(adapter->dev_id, bdaddr, cb, user_data);
+}
+
+int btd_adapter_set_did(struct btd_adapter *adapter, uint16_t vendor,
+ uint16_t product, uint16_t version)
+{
+ return adapter_ops->set_did(adapter->dev_id, vendor, product, version);
+}
+
+int adapter_create_bonding(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+ uint8_t io_cap)
+{
+ return adapter_ops->create_bonding(adapter->dev_id, bdaddr, io_cap);
+}
+
+int adapter_cancel_bonding(struct btd_adapter *adapter, bdaddr_t *bdaddr)
+{
+ return adapter_ops->cancel_bonding(adapter->dev_id, bdaddr);
+}
+
+int btd_adapter_read_local_oob_data(struct btd_adapter *adapter)
+{
+ return adapter_ops->read_local_oob_data(adapter->dev_id);
+}
+
+int btd_adapter_add_remote_oob_data(struct btd_adapter *adapter,
+ bdaddr_t *bdaddr, uint8_t *hash, uint8_t *randomizer)
+{
+ return adapter_ops->add_remote_oob_data(adapter->dev_id, bdaddr, hash,
+ randomizer);
+}
+
+int btd_adapter_remove_remote_oob_data(struct btd_adapter *adapter,
+ bdaddr_t *bdaddr)
+{
+ return adapter_ops->remove_remote_oob_data(adapter->dev_id, bdaddr);
+}
diff --git a/src/adapter.h b/src/adapter.h
new file mode 100644
index 0000000..308af75
--- /dev/null
+++ b/src/adapter.h
@@ -0,0 +1,302 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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
+ *
+ */
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <dbus/dbus.h>
+#include <glib.h>
+
+#define ADAPTER_INTERFACE "org.bluez.Adapter"
+
+#define MODE_OFF 0x00
+#define MODE_CONNECTABLE 0x01
+#define MODE_DISCOVERABLE 0x02
+#define MODE_UNKNOWN 0xff
+
+/* Discover states */
+#define STATE_IDLE 0x00
+#define STATE_LE_SCAN 0x01
+#define STATE_STDINQ 0x02
+#define STATE_PINQ 0x04
+#define STATE_RESOLVNAME 0x08
+#define STATE_SUSPENDED 0x10
+
+/* Supported host/controller discover type */
+#define DISC_LE 0x01
+#define DISC_STDINQ 0x02
+#define DISC_INTERLEAVE 0x04
+#define DISC_PINQ 0x08
+#define DISC_RESOLVNAME 0x10
+
+#define MAX_NAME_LENGTH 248
+
+/* Invalid SSP passkey value used to indicate negative replies */
+#define INVALID_PASSKEY 0xffffffff
+
+typedef enum {
+ NAME_ANY,
+ NAME_NOT_REQUIRED, /* used by get remote name without name resolving */
+ NAME_REQUIRED, /* remote name needs be resolved */
+ NAME_REQUESTED, /* HCI remote name request was sent */
+} name_status_t;
+
+struct btd_adapter;
+
+struct link_key_info {
+ bdaddr_t bdaddr;
+ unsigned char key[16];
+ uint8_t type;
+ uint8_t pin_len;
+};
+
+struct remote_dev_info {
+ bdaddr_t bdaddr;
+ int8_t rssi;
+ uint32_t class;
+ char *name;
+ char *alias;
+ dbus_bool_t legacy;
+ name_status_t name_status;
+ gboolean le;
+ char **uuids;
+ size_t uuid_count;
+ GSList *services;
+ uint8_t evt_type;
+ uint8_t bdaddr_type;
+ uint8_t flags;
+};
+
+struct hci_dev {
+ uint8_t features[8];
+ uint8_t extfeatures[8];
+ uint8_t lmp_ver;
+ uint16_t lmp_subver;
+ uint16_t hci_rev;
+ uint16_t manufacturer;
+
+ uint8_t ssp_mode;
+ char name[MAX_NAME_LENGTH + 1];
+};
+
+void btd_adapter_start(struct btd_adapter *adapter);
+
+int btd_adapter_stop(struct btd_adapter *adapter);
+
+void btd_adapter_get_mode(struct btd_adapter *adapter, uint8_t *mode,
+ uint8_t *on_mode, gboolean *pairable);
+
+int adapter_update_ssp_mode(struct btd_adapter *adapter, uint8_t mode);
+
+struct btd_device *adapter_get_device(DBusConnection *conn,
+ struct btd_adapter *adapter, const char *address);
+
+struct btd_device *adapter_find_device(struct btd_adapter *adapter, const char *dest);
+
+void adapter_remove_device(DBusConnection *conn, struct btd_adapter *adapter,
+ struct btd_device *device,
+ gboolean remove_storage);
+
+int adapter_resolve_names(struct btd_adapter *adapter);
+
+struct btd_adapter *adapter_create(DBusConnection *conn, int id);
+gboolean adapter_init(struct btd_adapter *adapter);
+void adapter_remove(struct btd_adapter *adapter);
+uint16_t adapter_get_dev_id(struct btd_adapter *adapter);
+const gchar *adapter_get_path(struct btd_adapter *adapter);
+void adapter_get_address(struct btd_adapter *adapter, bdaddr_t *bdaddr);
+void adapter_set_state(struct btd_adapter *adapter, int state);
+int adapter_get_state(struct btd_adapter *adapter);
+int adapter_get_discover_type(struct btd_adapter *adapter);
+struct remote_dev_info *adapter_search_found_devices(struct btd_adapter *adapter,
+ struct remote_dev_info *match);
+void adapter_update_device_from_info(struct btd_adapter *adapter,
+ bdaddr_t bdaddr, int8_t rssi,
+ uint8_t evt_type, const char *name,
+ GSList *services, int flags);
+void adapter_update_found_devices(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+ int8_t rssi, uint32_t class, const char *name,
+ const char *alias, gboolean legacy,
+ GSList *services, name_status_t name_status);
+int adapter_remove_found_device(struct btd_adapter *adapter, bdaddr_t *bdaddr);
+void adapter_emit_device_found(struct btd_adapter *adapter,
+ struct remote_dev_info *dev);
+void adapter_mode_changed(struct btd_adapter *adapter, uint8_t scan_mode);
+void adapter_update_local_name(struct btd_adapter *adapter, const char *name);
+void adapter_service_insert(struct btd_adapter *adapter, void *rec);
+void adapter_service_remove(struct btd_adapter *adapter, void *rec);
+void btd_adapter_class_changed(struct btd_adapter *adapter,
+ uint32_t new_class);
+void btd_adapter_pairable_changed(struct btd_adapter *adapter,
+ gboolean pairable);
+
+struct agent *adapter_get_agent(struct btd_adapter *adapter);
+void adapter_add_connection(struct btd_adapter *adapter,
+ struct btd_device *device);
+void adapter_remove_connection(struct btd_adapter *adapter,
+ struct btd_device *device);
+gboolean adapter_has_discov_sessions(struct btd_adapter *adapter);
+void adapter_suspend_discovery(struct btd_adapter *adapter);
+void adapter_resume_discovery(struct btd_adapter *adapter);
+
+struct btd_adapter *btd_adapter_ref(struct btd_adapter *adapter);
+void btd_adapter_unref(struct btd_adapter *adapter);
+
+int btd_adapter_set_class(struct btd_adapter *adapter, uint8_t major,
+ uint8_t minor);
+
+struct btd_adapter_driver {
+ const char *name;
+ int (*probe) (struct btd_adapter *adapter);
+ void (*remove) (struct btd_adapter *adapter);
+};
+
+typedef void (*service_auth_cb) (DBusError *derr, void *user_data);
+
+int btd_register_adapter_driver(struct btd_adapter_driver *driver);
+void btd_unregister_adapter_driver(struct btd_adapter_driver *driver);
+int btd_request_authorization(const bdaddr_t *src, const bdaddr_t *dst,
+ const char *uuid, service_auth_cb cb, void *user_data);
+int btd_cancel_authorization(const bdaddr_t *src, const bdaddr_t *dst);
+
+const char *adapter_any_get_path(void);
+
+const char *btd_adapter_any_request_path(void);
+void btd_adapter_any_release_path(void);
+gboolean adapter_is_pairable(struct btd_adapter *adapter);
+gboolean adapter_powering_down(struct btd_adapter *adapter);
+
+int btd_adapter_restore_powered(struct btd_adapter *adapter);
+int btd_adapter_switch_online(struct btd_adapter *adapter);
+int btd_adapter_switch_offline(struct btd_adapter *adapter);
+
+typedef void (*bt_hci_result_t) (uint8_t status, gpointer user_data);
+
+struct btd_adapter_ops {
+ int (*setup) (void);
+ void (*cleanup) (void);
+ int (*set_powered) (int index, gboolean powered);
+ int (*set_discoverable) (int index, gboolean discoverable);
+ int (*set_pairable) (int index, gboolean pairable);
+ int (*set_limited_discoverable) (int index, gboolean limited);
+ int (*start_inquiry) (int index, uint8_t length, gboolean periodic);
+ int (*stop_inquiry) (int index);
+ int (*start_scanning) (int index);
+ int (*stop_scanning) (int index);
+
+ int (*resolve_name) (int index, bdaddr_t *bdaddr);
+ int (*cancel_resolve_name) (int index, bdaddr_t *bdaddr);
+ int (*set_name) (int index, const char *name);
+ int (*set_dev_class) (int index, uint8_t major, uint8_t minor);
+ int (*set_fast_connectable) (int index, gboolean enable);
+ int (*read_clock) (int index, bdaddr_t *bdaddr, int which, int timeout,
+ uint32_t *clock, uint16_t *accuracy);
+ int (*read_bdaddr) (int index, bdaddr_t *bdaddr);
+ int (*block_device) (int index, bdaddr_t *bdaddr);
+ int (*unblock_device) (int index, bdaddr_t *bdaddr);
+ int (*get_conn_list) (int index, GSList **conns);
+ int (*read_local_version) (int index, struct hci_version *ver);
+ int (*read_local_features) (int index, uint8_t *features);
+ int (*disconnect) (int index, bdaddr_t *bdaddr);
+ int (*remove_bonding) (int index, bdaddr_t *bdaddr);
+ int (*pincode_reply) (int index, bdaddr_t *bdaddr, const char *pin);
+ int (*confirm_reply) (int index, bdaddr_t *bdaddr, gboolean success);
+ int (*passkey_reply) (int index, bdaddr_t *bdaddr, uint32_t passkey);
+ int (*enable_le) (int index);
+ int (*encrypt_link) (int index, bdaddr_t *bdaddr, bt_hci_result_t cb,
+ gpointer user_data);
+ int (*set_did) (int index, uint16_t vendor, uint16_t product,
+ uint16_t version);
+ int (*add_uuid) (int index, uuid_t *uuid, uint8_t svc_hint);
+ int (*remove_uuid) (int index, uuid_t *uuid);
+ int (*disable_cod_cache) (int index);
+ int (*restore_powered) (int index);
+ int (*load_keys) (int index, GSList *keys, gboolean debug_keys);
+ int (*set_io_capability) (int index, uint8_t io_capability);
+ int (*create_bonding) (int index, bdaddr_t *bdaddr, uint8_t io_cap);
+ int (*cancel_bonding) (int index, bdaddr_t *bdaddr);
+ int (*read_local_oob_data) (int index);
+ int (*add_remote_oob_data) (int index, bdaddr_t *bdaddr, uint8_t *hash,
+ uint8_t *randomizer);
+ int (*remove_remote_oob_data) (int index, bdaddr_t *bdaddr);
+};
+
+int btd_register_adapter_ops(struct btd_adapter_ops *ops, gboolean priority);
+void btd_adapter_cleanup_ops(struct btd_adapter_ops *btd_adapter_ops);
+int adapter_ops_setup(void);
+
+typedef void (*btd_adapter_powered_cb) (struct btd_adapter *adapter,
+ gboolean powered);
+void btd_adapter_register_powered_callback(struct btd_adapter *adapter,
+ btd_adapter_powered_cb cb);
+void btd_adapter_unregister_powered_callback(struct btd_adapter *adapter,
+ btd_adapter_powered_cb cb);
+
+/* If TRUE, enables fast connectabe, i.e. reduces page scan interval and changes
+ * type. If FALSE, disables fast connectable, i.e. sets page scan interval and
+ * type to default values. Valid for both connectable and discoverable modes. */
+int btd_adapter_set_fast_connectable(struct btd_adapter *adapter,
+ gboolean enable);
+
+int btd_adapter_read_clock(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+ int which, int timeout, uint32_t *clock,
+ uint16_t *accuracy);
+
+int btd_adapter_block_address(struct btd_adapter *adapter, bdaddr_t *bdaddr);
+int btd_adapter_unblock_address(struct btd_adapter *adapter, bdaddr_t *bdaddr);
+
+int btd_adapter_disconnect_device(struct btd_adapter *adapter,
+ bdaddr_t *bdaddr);
+
+int btd_adapter_remove_bonding(struct btd_adapter *adapter, bdaddr_t *bdaddr);
+
+int btd_adapter_pincode_reply(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+ const char *pin);
+int btd_adapter_confirm_reply(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+ gboolean success);
+int btd_adapter_passkey_reply(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+ uint32_t passkey);
+
+void btd_adapter_update_local_ext_features(struct btd_adapter *adapter,
+ const uint8_t *features);
+
+int btd_adapter_encrypt_link(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+ bt_hci_result_t cb, gpointer user_data);
+
+int btd_adapter_set_did(struct btd_adapter *adapter, uint16_t vendor,
+ uint16_t product, uint16_t version);
+
+int adapter_create_bonding(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+ uint8_t io_cap);
+
+int adapter_cancel_bonding(struct btd_adapter *adapter, bdaddr_t *bdaddr);
+
+int btd_adapter_read_local_oob_data(struct btd_adapter *adapter);
+
+int btd_adapter_add_remote_oob_data(struct btd_adapter *adapter,
+ bdaddr_t *bdaddr, uint8_t *hash, uint8_t *randomizer);
+
+int btd_adapter_remove_remote_oob_data(struct btd_adapter *adapter,
+ bdaddr_t *bdaddr);
diff --git a/src/agent.c b/src/agent.c
new file mode 100644
index 0000000..f87f253
--- /dev/null
+++ b/src/agent.c
@@ -0,0 +1,794 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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 <sys/socket.h>
+#include <sys/ioctl.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+
+#include "hcid.h"
+#include "adapter.h"
+#include "device.h"
+#include "agent.h"
+
+#define REQUEST_TIMEOUT (60 * 1000) /* 60 seconds */
+
+typedef enum {
+ AGENT_REQUEST_PASSKEY,
+ AGENT_REQUEST_CONFIRMATION,
+ AGENT_REQUEST_PINCODE,
+ AGENT_REQUEST_AUTHORIZE,
+ AGENT_REQUEST_CONFIRM_MODE
+} agent_request_type_t;
+
+struct agent {
+ struct btd_adapter *adapter;
+ char *name;
+ char *path;
+ uint8_t capability;
+ struct agent_request *request;
+ int exited;
+ agent_remove_cb remove_cb;
+ void *remove_cb_data;
+ guint listener_id;
+};
+
+struct agent_request {
+ agent_request_type_t type;
+ struct agent *agent;
+ DBusMessage *msg;
+ DBusPendingCall *call;
+ void *cb;
+ void *user_data;
+ GDestroyNotify destroy;
+};
+
+static DBusConnection *connection = NULL;
+
+static int request_fallback(struct agent_request *req,
+ DBusPendingCallNotifyFunction function);
+
+static void agent_release(struct agent *agent)
+{
+ DBusMessage *message;
+
+ DBG("Releasing agent %s, %s", agent->name, agent->path);
+
+ if (agent->request)
+ agent_cancel(agent);
+
+ message = dbus_message_new_method_call(agent->name, agent->path,
+ "org.bluez.Agent", "Release");
+ if (message == NULL) {
+ error("Couldn't allocate D-Bus message");
+ return;
+ }
+
+ g_dbus_send_message(connection, message);
+}
+
+static int send_cancel_request(struct agent_request *req)
+{
+ DBusMessage *message;
+
+ DBG("Sending Cancel request to %s, %s", req->agent->name,
+ req->agent->path);
+
+ message = dbus_message_new_method_call(req->agent->name, req->agent->path,
+ "org.bluez.Agent", "Cancel");
+ if (message == NULL) {
+ error("Couldn't allocate D-Bus message");
+ return -ENOMEM;
+ }
+
+ g_dbus_send_message(connection, message);
+
+ return 0;
+}
+
+static void agent_request_free(struct agent_request *req, gboolean destroy)
+{
+ if (req->msg)
+ dbus_message_unref(req->msg);
+ if (req->call)
+ dbus_pending_call_unref(req->call);
+ if (req->agent && req->agent->request)
+ req->agent->request = NULL;
+ if (destroy && req->destroy)
+ req->destroy(req->user_data);
+ g_free(req);
+}
+
+static void agent_exited(DBusConnection *conn, void *user_data)
+{
+ struct agent *agent = user_data;
+
+ DBG("Agent exited without calling Unregister");
+
+ agent->exited = TRUE;
+
+ agent_free(agent);
+}
+
+void agent_free(struct agent *agent)
+{
+ if (!agent)
+ return;
+
+ if (agent->remove_cb)
+ agent->remove_cb(agent, agent->remove_cb_data);
+
+ if (agent->request) {
+ DBusError err;
+ agent_pincode_cb pincode_cb;
+ agent_cb cb;
+
+ dbus_error_init(&err);
+ dbus_set_error_const(&err, "org.bluez.Error.Failed", "Canceled");
+
+ switch (agent->request->type) {
+ case AGENT_REQUEST_PINCODE:
+ pincode_cb = agent->request->cb;
+ pincode_cb(agent, &err, NULL, agent->request->user_data);
+ break;
+ default:
+ cb = agent->request->cb;
+ cb(agent, &err, agent->request->user_data);
+ }
+
+ dbus_error_free(&err);
+
+ agent_cancel(agent);
+ }
+
+ if (!agent->exited) {
+ g_dbus_remove_watch(connection, agent->listener_id);
+ agent_release(agent);
+ }
+
+ g_free(agent->name);
+ g_free(agent->path);
+
+ g_free(agent);
+}
+
+struct agent *agent_create(struct btd_adapter *adapter, const char *name,
+ const char *path, uint8_t capability,
+ agent_remove_cb cb, void *remove_cb_data)
+{
+ struct agent *agent;
+
+ agent = g_new0(struct agent, 1);
+
+ agent->adapter = adapter;
+ agent->name = g_strdup(name);
+ agent->path = g_strdup(path);
+ agent->capability = capability;
+ agent->remove_cb = cb;
+ agent->remove_cb_data = remove_cb_data;
+
+ agent->listener_id = g_dbus_add_disconnect_watch(connection, name,
+ agent_exited, agent,
+ NULL);
+
+ return agent;
+}
+
+static struct agent_request *agent_request_new(struct agent *agent,
+ agent_request_type_t type,
+ void *cb,
+ void *user_data,
+ GDestroyNotify destroy)
+{
+ struct agent_request *req;
+
+ req = g_new0(struct agent_request, 1);
+
+ req->agent = agent;
+ req->type = type;
+ req->cb = cb;
+ req->user_data = user_data;
+ req->destroy = destroy;
+
+ return req;
+}
+
+int agent_cancel(struct agent *agent)
+{
+ if (!agent->request)
+ return -EINVAL;
+
+ if (agent->request->call)
+ dbus_pending_call_cancel(agent->request->call);
+
+ if (!agent->exited)
+ send_cancel_request(agent->request);
+
+ agent_request_free(agent->request, TRUE);
+ agent->request = NULL;
+
+ return 0;
+}
+
+static void simple_agent_reply(DBusPendingCall *call, void *user_data)
+{
+ struct agent_request *req = user_data;
+ struct agent *agent = req->agent;
+ DBusMessage *message;
+ DBusError err;
+ agent_cb cb = req->cb;
+
+ /* steal_reply will always return non-NULL since the callback
+ * is only called after a reply has been received */
+ message = dbus_pending_call_steal_reply(call);
+
+ dbus_error_init(&err);
+ if (dbus_set_error_from_message(&err, message)) {
+ if ((g_str_equal(DBUS_ERROR_UNKNOWN_METHOD, err.name) ||
+ g_str_equal(DBUS_ERROR_NO_REPLY, err.name)) &&
+ request_fallback(req, simple_agent_reply) == 0) {
+ dbus_error_free(&err);
+ return;
+ }
+
+ error("Agent replied with an error: %s, %s",
+ err.name, err.message);
+
+ cb(agent, &err, req->user_data);
+
+ if (dbus_error_has_name(&err, DBUS_ERROR_NO_REPLY)) {
+ agent_cancel(agent);
+ dbus_message_unref(message);
+ dbus_error_free(&err);
+ return;
+ }
+
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ dbus_error_init(&err);
+ if (!dbus_message_get_args(message, &err, DBUS_TYPE_INVALID)) {
+ error("Wrong reply signature: %s", err.message);
+ cb(agent, &err, req->user_data);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ cb(agent, NULL, req->user_data);
+done:
+ dbus_message_unref(message);
+
+ agent->request = NULL;
+ agent_request_free(req, TRUE);
+}
+
+static int agent_call_authorize(struct agent_request *req,
+ const char *device_path,
+ const char *uuid)
+{
+ struct agent *agent = req->agent;
+
+ req->msg = dbus_message_new_method_call(agent->name, agent->path,
+ "org.bluez.Agent", "Authorize");
+ if (!req->msg) {
+ error("Couldn't allocate D-Bus message");
+ return -ENOMEM;
+ }
+
+ dbus_message_append_args(req->msg,
+ DBUS_TYPE_OBJECT_PATH, &device_path,
+ DBUS_TYPE_STRING, &uuid,
+ DBUS_TYPE_INVALID);
+
+ if (dbus_connection_send_with_reply(connection, req->msg,
+ &req->call, REQUEST_TIMEOUT) == FALSE) {
+ error("D-Bus send failed");
+ return -EIO;
+ }
+
+ dbus_pending_call_set_notify(req->call, simple_agent_reply, req, NULL);
+ return 0;
+}
+
+int agent_authorize(struct agent *agent,
+ const char *path,
+ const char *uuid,
+ agent_cb cb,
+ void *user_data,
+ GDestroyNotify destroy)
+{
+ struct agent_request *req;
+ int err;
+
+ if (agent->request)
+ return -EBUSY;
+
+ req = agent_request_new(agent, AGENT_REQUEST_AUTHORIZE, cb,
+ user_data, destroy);
+
+ err = agent_call_authorize(req, path, uuid);
+ if (err < 0) {
+ agent_request_free(req, FALSE);
+ return -ENOMEM;
+ }
+
+ agent->request = req;
+
+ DBG("authorize request was sent for %s", path);
+
+ return 0;
+}
+
+static void pincode_reply(DBusPendingCall *call, void *user_data)
+{
+ struct agent_request *req = user_data;
+ struct agent *agent = req->agent;
+ struct btd_adapter *adapter = agent->adapter;
+ agent_pincode_cb cb = req->cb;
+ DBusMessage *message;
+ DBusError err;
+ bdaddr_t sba;
+ size_t len;
+ char *pin;
+
+ adapter_get_address(adapter, &sba);
+
+ /* steal_reply will always return non-NULL since the callback
+ * is only called after a reply has been received */
+ message = dbus_pending_call_steal_reply(call);
+
+ dbus_error_init(&err);
+ if (dbus_set_error_from_message(&err, message)) {
+ if ((g_str_equal(DBUS_ERROR_UNKNOWN_METHOD, err.name) ||
+ g_str_equal(DBUS_ERROR_NO_REPLY, err.name)) &&
+ request_fallback(req, pincode_reply) == 0) {
+ dbus_error_free(&err);
+ return;
+ }
+
+ error("Agent replied with an error: %s, %s",
+ err.name, err.message);
+
+ cb(agent, &err, NULL, req->user_data);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ dbus_error_init(&err);
+ if (!dbus_message_get_args(message, &err,
+ DBUS_TYPE_STRING, &pin,
+ DBUS_TYPE_INVALID)) {
+ error("Wrong passkey reply signature: %s", err.message);
+ cb(agent, &err, NULL, req->user_data);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ len = strlen(pin);
+
+ dbus_error_init(&err);
+ if (len > 16 || len < 1) {
+ error("Invalid PIN length (%zu) from agent", len);
+ dbus_set_error_const(&err, "org.bluez.Error.InvalidArgs",
+ "Invalid passkey length");
+ cb(agent, &err, NULL, req->user_data);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ cb(agent, NULL, pin, req->user_data);
+
+done:
+ if (message)
+ dbus_message_unref(message);
+
+ dbus_pending_call_cancel(req->call);
+ agent->request = NULL;
+ agent_request_free(req, TRUE);
+}
+
+static int pincode_request_new(struct agent_request *req, const char *device_path,
+ dbus_bool_t numeric)
+{
+ struct agent *agent = req->agent;
+
+ req->msg = dbus_message_new_method_call(agent->name, agent->path,
+ "org.bluez.Agent", "RequestPinCode");
+ if (req->msg == NULL) {
+ error("Couldn't allocate D-Bus message");
+ return -ENOMEM;
+ }
+
+ dbus_message_append_args(req->msg, DBUS_TYPE_OBJECT_PATH, &device_path,
+ DBUS_TYPE_INVALID);
+
+ if (dbus_connection_send_with_reply(connection, req->msg,
+ &req->call, REQUEST_TIMEOUT) == FALSE) {
+ error("D-Bus send failed");
+ return -EIO;
+ }
+
+ dbus_pending_call_set_notify(req->call, pincode_reply, req, NULL);
+ return 0;
+}
+
+int agent_request_pincode(struct agent *agent, struct btd_device *device,
+ agent_pincode_cb cb, void *user_data,
+ GDestroyNotify destroy)
+{
+ struct agent_request *req;
+ const gchar *dev_path = device_get_path(device);
+ int err;
+
+ if (agent->request)
+ return -EBUSY;
+
+ req = agent_request_new(agent, AGENT_REQUEST_PINCODE, cb,
+ user_data, destroy);
+
+ err = pincode_request_new(req, dev_path, FALSE);
+ if (err < 0)
+ goto failed;
+
+ agent->request = req;
+
+ return 0;
+
+failed:
+ g_free(req);
+ return err;
+}
+
+static int confirm_mode_change_request_new(struct agent_request *req,
+ const char *mode)
+{
+ struct agent *agent = req->agent;
+
+ req->msg = dbus_message_new_method_call(agent->name, agent->path,
+ "org.bluez.Agent", "ConfirmModeChange");
+ if (req->msg == NULL) {
+ error("Couldn't allocate D-Bus message");
+ return -ENOMEM;
+ }
+
+ dbus_message_append_args(req->msg,
+ DBUS_TYPE_STRING, &mode,
+ DBUS_TYPE_INVALID);
+
+ if (dbus_connection_send_with_reply(connection, req->msg,
+ &req->call, REQUEST_TIMEOUT) == FALSE) {
+ error("D-Bus send failed");
+ return -EIO;
+ }
+
+ dbus_pending_call_set_notify(req->call, simple_agent_reply, req, NULL);
+ return 0;
+}
+
+int agent_confirm_mode_change(struct agent *agent, const char *new_mode,
+ agent_cb cb, void *user_data,
+ GDestroyNotify destroy)
+{
+ struct agent_request *req;
+ int err;
+
+ if (agent->request)
+ return -EBUSY;
+
+ DBG("Calling Agent.ConfirmModeChange: name=%s, path=%s, mode=%s",
+ agent->name, agent->path, new_mode);
+
+ req = agent_request_new(agent, AGENT_REQUEST_CONFIRM_MODE,
+ cb, user_data, destroy);
+
+ err = confirm_mode_change_request_new(req, new_mode);
+ if (err < 0)
+ goto failed;
+
+ agent->request = req;
+
+ return 0;
+
+failed:
+ agent_request_free(req, FALSE);
+ return err;
+}
+
+static void passkey_reply(DBusPendingCall *call, void *user_data)
+{
+ struct agent_request *req = user_data;
+ struct agent *agent = req->agent;
+ agent_passkey_cb cb = req->cb;
+ DBusMessage *message;
+ DBusError err;
+ uint32_t passkey;
+
+ /* steal_reply will always return non-NULL since the callback
+ * is only called after a reply has been received */
+ message = dbus_pending_call_steal_reply(call);
+
+ dbus_error_init(&err);
+ if (dbus_set_error_from_message(&err, message)) {
+ if ((g_str_equal(DBUS_ERROR_UNKNOWN_METHOD, err.name) ||
+ g_str_equal(DBUS_ERROR_NO_REPLY, err.name)) &&
+ request_fallback(req, passkey_reply) == 0) {
+ dbus_error_free(&err);
+ return;
+ }
+
+ error("Agent replied with an error: %s, %s",
+ err.name, err.message);
+ cb(agent, &err, 0, req->user_data);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ dbus_error_init(&err);
+ if (!dbus_message_get_args(message, &err,
+ DBUS_TYPE_UINT32, &passkey,
+ DBUS_TYPE_INVALID)) {
+ error("Wrong passkey reply signature: %s", err.message);
+ cb(agent, &err, 0, req->user_data);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ cb(agent, NULL, passkey, req->user_data);
+
+done:
+ if (message)
+ dbus_message_unref(message);
+
+ dbus_pending_call_cancel(req->call);
+ agent->request = NULL;
+ agent_request_free(req, TRUE);
+}
+
+static int passkey_request_new(struct agent_request *req,
+ const char *device_path)
+{
+ struct agent *agent = req->agent;
+
+ req->msg = dbus_message_new_method_call(agent->name, agent->path,
+ "org.bluez.Agent", "RequestPasskey");
+ if (req->msg == NULL) {
+ error("Couldn't allocate D-Bus message");
+ return -ENOMEM;
+ }
+
+ dbus_message_append_args(req->msg, DBUS_TYPE_OBJECT_PATH, &device_path,
+ DBUS_TYPE_INVALID);
+
+ if (dbus_connection_send_with_reply(connection, req->msg,
+ &req->call, REQUEST_TIMEOUT) == FALSE) {
+ error("D-Bus send failed");
+ return -EIO;
+ }
+
+ dbus_pending_call_set_notify(req->call, passkey_reply, req, NULL);
+ return 0;
+}
+
+int agent_request_passkey(struct agent *agent, struct btd_device *device,
+ agent_passkey_cb cb, void *user_data,
+ GDestroyNotify destroy)
+{
+ struct agent_request *req;
+ const gchar *dev_path = device_get_path(device);
+ int err;
+
+ if (agent->request)
+ return -EBUSY;
+
+ DBG("Calling Agent.RequestPasskey: name=%s, path=%s",
+ agent->name, agent->path);
+
+ req = agent_request_new(agent, AGENT_REQUEST_PASSKEY, cb,
+ user_data, destroy);
+
+ err = passkey_request_new(req, dev_path);
+ if (err < 0)
+ goto failed;
+
+ agent->request = req;
+
+ return 0;
+
+failed:
+ agent_request_free(req, FALSE);
+ return err;
+}
+
+static int confirmation_request_new(struct agent_request *req,
+ const char *device_path,
+ uint32_t passkey)
+{
+ struct agent *agent = req->agent;
+
+ req->msg = dbus_message_new_method_call(agent->name, agent->path,
+ "org.bluez.Agent", "RequestConfirmation");
+ if (req->msg == NULL) {
+ error("Couldn't allocate D-Bus message");
+ return -ENOMEM;
+ }
+
+ dbus_message_append_args(req->msg,
+ DBUS_TYPE_OBJECT_PATH, &device_path,
+ DBUS_TYPE_UINT32, &passkey,
+ DBUS_TYPE_INVALID);
+
+ if (dbus_connection_send_with_reply(connection, req->msg,
+ &req->call, REQUEST_TIMEOUT) == FALSE) {
+ error("D-Bus send failed");
+ return -EIO;
+ }
+
+ dbus_pending_call_set_notify(req->call, simple_agent_reply, req, NULL);
+
+ return 0;
+}
+
+int agent_request_confirmation(struct agent *agent, struct btd_device *device,
+ uint32_t passkey, agent_cb cb,
+ void *user_data, GDestroyNotify destroy)
+{
+ struct agent_request *req;
+ const gchar *dev_path = device_get_path(device);
+ int err;
+
+ if (agent->request)
+ return -EBUSY;
+
+ DBG("Calling Agent.RequestConfirmation: name=%s, path=%s, passkey=%06u",
+ agent->name, agent->path, passkey);
+
+ req = agent_request_new(agent, AGENT_REQUEST_CONFIRMATION, cb,
+ user_data, destroy);
+
+ err = confirmation_request_new(req, dev_path, passkey);
+ if (err < 0)
+ goto failed;
+
+ agent->request = req;
+
+ return 0;
+
+failed:
+ agent_request_free(req, FALSE);
+ return err;
+}
+
+static int request_fallback(struct agent_request *req,
+ DBusPendingCallNotifyFunction function)
+{
+ struct btd_adapter *adapter = req->agent->adapter;
+ struct agent *adapter_agent = adapter_get_agent(adapter);
+ DBusMessage *msg;
+
+ if (req->agent == adapter_agent || adapter_agent == NULL)
+ return -EINVAL;
+
+ dbus_pending_call_cancel(req->call);
+ dbus_pending_call_unref(req->call);
+
+ msg = dbus_message_copy(req->msg);
+
+ dbus_message_set_destination(msg, adapter_agent->name);
+ dbus_message_set_path(msg, adapter_agent->path);
+
+ if (dbus_connection_send_with_reply(connection, msg,
+ &req->call, REQUEST_TIMEOUT) == FALSE) {
+ error("D-Bus send failed");
+ dbus_message_unref(msg);
+ return -EIO;
+ }
+
+ req->agent->request = NULL;
+ req->agent = adapter_agent;
+ req->agent->request = req;
+
+ dbus_message_unref(req->msg);
+ req->msg = msg;
+
+ dbus_pending_call_set_notify(req->call, function, req, NULL);
+
+ return 0;
+}
+
+int agent_display_passkey(struct agent *agent, struct btd_device *device,
+ uint32_t passkey)
+{
+ DBusMessage *message;
+ const gchar *dev_path = device_get_path(device);
+
+ message = dbus_message_new_method_call(agent->name, agent->path,
+ "org.bluez.Agent", "DisplayPasskey");
+ if (!message) {
+ error("Couldn't allocate D-Bus message");
+ return -1;
+ }
+
+ dbus_message_append_args(message,
+ DBUS_TYPE_OBJECT_PATH, &dev_path,
+ DBUS_TYPE_UINT32, &passkey,
+ DBUS_TYPE_INVALID);
+
+ if (!g_dbus_send_message(connection, message)) {
+ error("D-Bus send failed");
+ dbus_message_unref(message);
+ return -1;
+ }
+
+ return 0;
+}
+
+uint8_t agent_get_io_capability(struct agent *agent)
+{
+ return agent->capability;
+}
+
+gboolean agent_matches(struct agent *agent, const char *name, const char *path)
+{
+ if (g_str_equal(agent->name, name) && g_str_equal(agent->path, path))
+ return TRUE;
+
+ return FALSE;
+}
+
+gboolean agent_is_busy(struct agent *agent, void *user_data)
+{
+ if (!agent->request)
+ return FALSE;
+
+ if (user_data && user_data != agent->request->user_data)
+ return FALSE;
+
+ return TRUE;
+}
+
+void agent_exit(void)
+{
+ dbus_connection_unref(connection);
+ connection = NULL;
+}
+
+void agent_init(void)
+{
+ connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+}
diff --git a/src/agent.h b/src/agent.h
new file mode 100644
index 0000000..e184250
--- /dev/null
+++ b/src/agent.h
@@ -0,0 +1,77 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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
+ *
+ */
+
+struct agent;
+
+typedef void (*agent_cb) (struct agent *agent, DBusError *err,
+ void *user_data);
+
+typedef void (*agent_pincode_cb) (struct agent *agent, DBusError *err,
+ const char *pincode, void *user_data);
+
+typedef void (*agent_passkey_cb) (struct agent *agent, DBusError *err,
+ uint32_t passkey, void *user_data);
+
+typedef void (*agent_remove_cb) (struct agent *agent, void *user_data);
+
+struct agent *agent_create(struct btd_adapter *adapter, const char *name,
+ const char *path, uint8_t capability,
+ agent_remove_cb cb, void *remove_cb_data);
+
+void agent_free(struct agent *agent);
+
+int agent_authorize(struct agent *agent, const char *path,
+ const char *uuid, agent_cb cb, void *user_data,
+ GDestroyNotify destroy);
+
+int agent_request_pincode(struct agent *agent, struct btd_device *device,
+ agent_pincode_cb cb, void *user_data,
+ GDestroyNotify destroy);
+
+int agent_confirm_mode_change(struct agent *agent, const char *new_mode,
+ agent_cb cb, void *user_data,
+ GDestroyNotify destroy);
+
+int agent_request_passkey(struct agent *agent, struct btd_device *device,
+ agent_passkey_cb cb, void *user_data,
+ GDestroyNotify destroy);
+
+int agent_request_confirmation(struct agent *agent, struct btd_device *device,
+ uint32_t passkey, agent_cb cb,
+ void *user_data, GDestroyNotify destroy);
+
+int agent_display_passkey(struct agent *agent, struct btd_device *device,
+ uint32_t passkey);
+
+int agent_cancel(struct agent *agent);
+
+gboolean agent_is_busy(struct agent *agent, void *user_data);
+
+uint8_t agent_get_io_capability(struct agent *agent);
+
+gboolean agent_matches(struct agent *agent, const char *name, const char *path);
+
+void agent_init(void);
+void agent_exit(void);
+
diff --git a/src/attrib-server.c b/src/attrib-server.c
new file mode 100644
index 0000000..98bd6b1
--- /dev/null
+++ b/src/attrib-server.c
@@ -0,0 +1,1310 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2010 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 <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <glib.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/uuid.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include "log.h"
+#include "glib-helper.h"
+#include "btio.h"
+#include "sdpd.h"
+#include "hcid.h"
+#include "att.h"
+#include "gattrib.h"
+
+#include "attrib-server.h"
+
+#define GATT_PSM 0x1f
+#define GATT_CID 4
+
+static GSList *database = NULL;
+
+struct gatt_channel {
+ bdaddr_t src;
+ bdaddr_t dst;
+ GSList *configs;
+ GSList *notify;
+ GSList *indicate;
+ GAttrib *attrib;
+ guint mtu;
+ gboolean le;
+ guint id;
+ gboolean encrypted;
+};
+
+struct group_elem {
+ uint16_t handle;
+ uint16_t end;
+ uint8_t *data;
+ uint16_t len;
+};
+
+static GIOChannel *l2cap_io = NULL;
+static GIOChannel *le_io = NULL;
+static GSList *clients = NULL;
+static uint32_t gatt_sdp_handle = 0;
+static uint32_t gap_sdp_handle = 0;
+
+/* GAP attribute handles */
+static uint16_t name_handle = 0x0000;
+static uint16_t appearance_handle = 0x0000;
+
+static bt_uuid_t prim_uuid = {
+ .type = BT_UUID16,
+ .value.u16 = GATT_PRIM_SVC_UUID
+};
+static bt_uuid_t snd_uuid = {
+ .type = BT_UUID16,
+ .value.u16 = GATT_SND_SVC_UUID
+};
+
+static sdp_record_t *server_record_new(uuid_t *uuid, uint16_t start, uint16_t end)
+{
+ sdp_list_t *svclass_id, *apseq, *proto[2], *root, *aproto;
+ uuid_t root_uuid, proto_uuid, l2cap;
+ sdp_record_t *record;
+ sdp_data_t *psm, *sh, *eh;
+ uint16_t lp = GATT_PSM;
+
+ if (uuid == NULL)
+ return NULL;
+
+ if (start > end)
+ return NULL;
+
+ record = sdp_record_alloc();
+ if (record == NULL)
+ return NULL;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(record, root);
+ sdp_list_free(root, NULL);
+
+ svclass_id = sdp_list_append(NULL, uuid);
+ sdp_set_service_classes(record, svclass_id);
+ sdp_list_free(svclass_id, NULL);
+
+ sdp_uuid16_create(&l2cap, L2CAP_UUID);
+ proto[0] = sdp_list_append(NULL, &l2cap);
+ psm = sdp_data_alloc(SDP_UINT16, &lp);
+ proto[0] = sdp_list_append(proto[0], psm);
+ apseq = sdp_list_append(NULL, proto[0]);
+
+ sdp_uuid16_create(&proto_uuid, ATT_UUID);
+ proto[1] = sdp_list_append(NULL, &proto_uuid);
+ sh = sdp_data_alloc(SDP_UINT16, &start);
+ proto[1] = sdp_list_append(proto[1], sh);
+ eh = sdp_data_alloc(SDP_UINT16, &end);
+ proto[1] = sdp_list_append(proto[1], eh);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(NULL, apseq);
+ sdp_set_access_protos(record, aproto);
+
+ sdp_data_free(psm);
+ sdp_data_free(sh);
+ sdp_data_free(eh);
+ sdp_list_free(proto[0], NULL);
+ sdp_list_free(proto[1], NULL);
+ sdp_list_free(apseq, NULL);
+ sdp_list_free(aproto, NULL);
+
+ return record;
+}
+
+static int handle_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct attribute *attrib = a;
+ uint16_t handle = GPOINTER_TO_UINT(b);
+
+ return attrib->handle - handle;
+}
+
+static int attribute_cmp(gconstpointer a1, gconstpointer a2)
+{
+ const struct attribute *attrib1 = a1;
+ const struct attribute *attrib2 = a2;
+
+ return attrib1->handle - attrib2->handle;
+}
+
+static uint8_t att_check_reqs(struct gatt_channel *channel, uint8_t opcode,
+ int reqs)
+{
+ /* FIXME: currently, it is assumed an encrypted link is enough for
+ * authentication. This will allow to enable the SMP negotiation once
+ * it is on upstream kernel. High security level should be mapped
+ * to authentication and medium to encryption permission. */
+ if (!channel->encrypted)
+ channel->encrypted = g_attrib_is_encrypted(channel->attrib);
+ if (reqs == ATT_AUTHENTICATION && !channel->encrypted)
+ return ATT_ECODE_INSUFF_AUTHEN;
+ else if (reqs == ATT_AUTHORIZATION)
+ return ATT_ECODE_INSUFF_AUTHO;
+
+ switch (opcode) {
+ case ATT_OP_READ_BY_GROUP_REQ:
+ case ATT_OP_READ_BY_TYPE_REQ:
+ case ATT_OP_READ_REQ:
+ case ATT_OP_READ_BLOB_REQ:
+ case ATT_OP_READ_MULTI_REQ:
+ if (reqs == ATT_NOT_PERMITTED)
+ return ATT_ECODE_READ_NOT_PERM;
+ break;
+ case ATT_OP_PREP_WRITE_REQ:
+ case ATT_OP_WRITE_REQ:
+ case ATT_OP_WRITE_CMD:
+ if (reqs == ATT_NOT_PERMITTED)
+ return ATT_ECODE_WRITE_NOT_PERM;
+ break;
+ }
+
+ return 0;
+}
+
+static uint8_t client_set_notifications(struct attribute *attr,
+ gpointer user_data)
+{
+ struct gatt_channel *channel = user_data;
+ struct attribute *last_chr_val = NULL;
+ uint16_t cfg_val;
+ uint8_t props;
+ bt_uuid_t uuid;
+ GSList *l;
+
+ cfg_val = att_get_u16(attr->data);
+
+ bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+ for (l = database, props = 0; l != NULL; l = l->next) {
+ struct attribute *a = l->data;
+ static uint16_t handle = 0;
+
+ if (a->handle >= attr->handle)
+ break;
+
+ if (bt_uuid_cmp(&a->uuid, &uuid) == 0) {
+ props = att_get_u8(&a->data[0]);
+ handle = att_get_u16(&a->data[1]);
+ continue;
+ }
+
+ if (handle && a->handle == handle)
+ last_chr_val = a;
+ }
+
+ if (last_chr_val == NULL)
+ return 0;
+
+ if ((cfg_val & 0x0001) && !(props & ATT_CHAR_PROPER_NOTIFY))
+ return ATT_ECODE_WRITE_NOT_PERM;
+
+ if ((cfg_val & 0x0002) && !(props & ATT_CHAR_PROPER_INDICATE))
+ return ATT_ECODE_WRITE_NOT_PERM;
+
+ if (cfg_val & 0x0001)
+ channel->notify = g_slist_append(channel->notify, last_chr_val);
+ else
+ channel->notify = g_slist_remove(channel->notify, last_chr_val);
+
+ if (cfg_val & 0x0002)
+ channel->indicate = g_slist_append(channel->indicate,
+ last_chr_val);
+ else
+ channel->indicate = g_slist_remove(channel->indicate,
+ last_chr_val);
+
+ return 0;
+}
+
+static struct attribute *client_cfg_attribute(struct gatt_channel *channel,
+ struct attribute *orig_attr,
+ const uint8_t *value, int vlen)
+{
+ guint handle = orig_attr->handle;
+ bt_uuid_t uuid;
+ GSList *l;
+
+ bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+ if (bt_uuid_cmp(&orig_attr->uuid, &uuid) != 0)
+ return NULL;
+
+ /* Value is unchanged, not need to create a private copy yet */
+ if (vlen == orig_attr->len && memcmp(orig_attr->data, value, vlen) == 0)
+ return orig_attr;
+
+ l = g_slist_find_custom(channel->configs, GUINT_TO_POINTER(handle),
+ handle_cmp);
+ if (!l) {
+ struct attribute *a;
+
+ /* Create a private copy of the Client Characteristic
+ * Configuration attribute */
+ a = g_malloc0(sizeof(*a) + vlen);
+ memcpy(a, orig_attr, sizeof(*a));
+ memcpy(a->data, value, vlen);
+ a->write_cb = client_set_notifications;
+ a->cb_user_data = channel;
+
+ channel->configs = g_slist_insert_sorted(channel->configs, a,
+ attribute_cmp);
+
+ return a;
+ }
+
+ return l->data;
+}
+
+static uint16_t read_by_group(struct gatt_channel *channel, uint16_t start,
+ uint16_t end, bt_uuid_t *uuid,
+ uint8_t *pdu, int len)
+{
+ struct att_data_list *adl;
+ struct attribute *a;
+ struct group_elem *cur, *old = NULL;
+ GSList *l, *groups;
+ uint16_t length, last_handle, last_size = 0;
+ uint8_t status;
+ int i;
+
+ if (start > end || start == 0x0000)
+ return enc_error_resp(ATT_OP_READ_BY_GROUP_REQ, start,
+ ATT_ECODE_INVALID_HANDLE, pdu, len);
+
+ /*
+ * Only <<Primary Service>> and <<Secondary Service>> grouping
+ * types may be used in the Read By Group Type Request.
+ */
+
+ if (bt_uuid_cmp(uuid, &prim_uuid) != 0 &&
+ bt_uuid_cmp(uuid, &snd_uuid) != 0)
+ return enc_error_resp(ATT_OP_READ_BY_GROUP_REQ, 0x0000,
+ ATT_ECODE_UNSUPP_GRP_TYPE, pdu, len);
+
+ last_handle = end;
+ for (l = database, groups = NULL, cur = NULL; l; l = l->next) {
+ struct attribute *client_attr;
+
+ a = l->data;
+
+ if (a->handle < start)
+ continue;
+
+ if (a->handle >= end)
+ break;
+
+ /* The old group ends when a new one starts */
+ if (old && (bt_uuid_cmp(&a->uuid, &prim_uuid) == 0 ||
+ bt_uuid_cmp(&a->uuid, &snd_uuid) == 0)) {
+ old->end = last_handle;
+ old = NULL;
+ }
+
+ if (bt_uuid_cmp(&a->uuid, uuid) != 0) {
+ /* Still inside a service, update its last handle */
+ if (old)
+ last_handle = a->handle;
+ continue;
+ }
+
+ if (last_size && (last_size != a->len))
+ break;
+
+ status = att_check_reqs(channel, ATT_OP_READ_BY_GROUP_REQ,
+ a->read_reqs);
+
+ client_attr = client_cfg_attribute(channel, a, a->data, a->len);
+ if (client_attr)
+ a = client_attr;
+
+ if (status == 0x00 && a->read_cb)
+ status = a->read_cb(a, a->cb_user_data);
+
+ if (status) {
+ g_slist_foreach(groups, (GFunc) g_free, NULL);
+ g_slist_free(groups);
+ return enc_error_resp(ATT_OP_READ_BY_GROUP_REQ,
+ a->handle, status, pdu, len);
+ }
+
+ cur = g_new0(struct group_elem, 1);
+ cur->handle = a->handle;
+ cur->data = a->data;
+ cur->len = a->len;
+
+ /* Attribute Grouping Type found */
+ groups = g_slist_append(groups, cur);
+
+ last_size = a->len;
+ old = cur;
+ last_handle = cur->handle;
+ }
+
+ if (groups == NULL)
+ return enc_error_resp(ATT_OP_READ_BY_GROUP_REQ, start,
+ ATT_ECODE_ATTR_NOT_FOUND, pdu, len);
+
+ if (l == NULL)
+ cur->end = a->handle;
+ else
+ cur->end = last_handle;
+
+ length = g_slist_length(groups);
+
+ adl = att_data_list_alloc(length, last_size + 4);
+
+ for (i = 0, l = groups; l; l = l->next, i++) {
+ uint8_t *value;
+
+ cur = l->data;
+
+ value = (void *) adl->data[i];
+
+ att_put_u16(cur->handle, value);
+ att_put_u16(cur->end, &value[2]);
+ /* Attribute Value */
+ memcpy(&value[4], cur->data, cur->len);
+ }
+
+ length = enc_read_by_grp_resp(adl, pdu, len);
+
+ att_data_list_free(adl);
+ g_slist_foreach(groups, (GFunc) g_free, NULL);
+ g_slist_free(groups);
+
+ return length;
+}
+
+static uint16_t read_by_type(struct gatt_channel *channel, uint16_t start,
+ uint16_t end, bt_uuid_t *uuid,
+ uint8_t *pdu, int len)
+{
+ struct att_data_list *adl;
+ GSList *l, *types;
+ struct attribute *a;
+ uint16_t num, length;
+ uint8_t status;
+ int i;
+
+ if (start > end || start == 0x0000)
+ return enc_error_resp(ATT_OP_READ_BY_TYPE_REQ, start,
+ ATT_ECODE_INVALID_HANDLE, pdu, len);
+
+ for (l = database, length = 0, types = NULL; l; l = l->next) {
+ struct attribute *client_attr;
+
+ a = l->data;
+
+ if (a->handle < start)
+ continue;
+
+ if (a->handle >= end)
+ break;
+
+ if (bt_uuid_cmp(&a->uuid, uuid) != 0)
+ continue;
+
+ status = att_check_reqs(channel, ATT_OP_READ_BY_TYPE_REQ,
+ a->read_reqs);
+
+ client_attr = client_cfg_attribute(channel, a, a->data, a->len);
+ if (client_attr)
+ a = client_attr;
+
+ if (status == 0x00 && a->read_cb)
+ status = a->read_cb(a, a->cb_user_data);
+
+ if (status) {
+ g_slist_free(types);
+ return enc_error_resp(ATT_OP_READ_BY_TYPE_REQ,
+ a->handle, status, pdu, len);
+ }
+
+ /* All elements must have the same length */
+ if (length == 0)
+ length = a->len;
+ else if (a->len != length)
+ break;
+
+ types = g_slist_append(types, a);
+ }
+
+ if (types == NULL)
+ return enc_error_resp(ATT_OP_READ_BY_TYPE_REQ, start,
+ ATT_ECODE_ATTR_NOT_FOUND, pdu, len);
+
+ num = g_slist_length(types);
+
+ /* Handle length plus attribute value length */
+ length += 2;
+
+ adl = att_data_list_alloc(num, length);
+
+ for (i = 0, l = types; l; i++, l = l->next) {
+ uint8_t *value;
+
+ a = l->data;
+
+ value = (void *) adl->data[i];
+
+ att_put_u16(a->handle, value);
+
+ /* Attribute Value */
+ memcpy(&value[2], a->data, a->len);
+ }
+
+ length = enc_read_by_type_resp(adl, pdu, len);
+
+ att_data_list_free(adl);
+ g_slist_free(types);
+
+ return length;
+}
+
+static int find_info(uint16_t start, uint16_t end, uint8_t *pdu, int len)
+{
+ struct attribute *a;
+ struct att_data_list *adl;
+ GSList *l, *info;
+ uint8_t format, last_type = BT_UUID_UNSPEC;
+ uint16_t length, num;
+ int i;
+
+ if (start > end || start == 0x0000)
+ return enc_error_resp(ATT_OP_FIND_INFO_REQ, start,
+ ATT_ECODE_INVALID_HANDLE, pdu, len);
+
+ for (l = database, info = NULL, num = 0; l; l = l->next) {
+ a = l->data;
+
+ if (a->handle < start)
+ continue;
+
+ if (a->handle > end)
+ break;
+
+ if (last_type == BT_UUID_UNSPEC)
+ last_type = a->uuid.type;
+
+ if (a->uuid.type != last_type)
+ break;
+
+ info = g_slist_append(info, a);
+ num++;
+
+ last_type = a->uuid.type;
+ }
+
+ if (info == NULL)
+ return enc_error_resp(ATT_OP_FIND_INFO_REQ, start,
+ ATT_ECODE_ATTR_NOT_FOUND, pdu, len);
+
+ if (last_type == BT_UUID16) {
+ length = 2;
+ format = 0x01;
+ } else if (last_type == BT_UUID128) {
+ length = 16;
+ format = 0x02;
+ } else {
+ g_slist_free(info);
+ return 0;
+ }
+
+ adl = att_data_list_alloc(num, length + 2);
+
+ for (i = 0, l = info; l; i++, l = l->next) {
+ uint8_t *value;
+
+ a = l->data;
+
+ value = (void *) adl->data[i];
+
+ att_put_u16(a->handle, value);
+
+ /* Attribute Value */
+ att_put_uuid(a->uuid, &value[2]);
+ }
+
+ length = enc_find_info_resp(format, adl, pdu, len);
+
+ att_data_list_free(adl);
+ g_slist_free(info);
+
+ return length;
+}
+
+static int find_by_type(uint16_t start, uint16_t end, bt_uuid_t *uuid,
+ const uint8_t *value, int vlen, uint8_t *opdu, int mtu)
+{
+ struct attribute *a;
+ struct att_range *range;
+ GSList *l, *matches;
+ int len;
+
+ if (start > end || start == 0x0000)
+ return enc_error_resp(ATT_OP_FIND_BY_TYPE_REQ, start,
+ ATT_ECODE_INVALID_HANDLE, opdu, mtu);
+
+ /* Searching first requested handle number */
+ for (l = database, matches = NULL, range = NULL; l; l = l->next) {
+ a = l->data;
+
+ if (a->handle < start)
+ continue;
+
+ if (a->handle > end)
+ break;
+
+ /* Primary service? Attribute value matches? */
+ if ((bt_uuid_cmp(&a->uuid, uuid) == 0) && (a->len == vlen) &&
+ (memcmp(a->data, value, vlen) == 0)) {
+
+ range = g_new0(struct att_range, 1);
+ range->start = a->handle;
+ /* It is allowed to have end group handle the same as
+ * start handle, for groups with only one attribute. */
+ range->end = a->handle;
+
+ matches = g_slist_append(matches, range);
+ } else if (range) {
+ /* Update the last found handle or reset the pointer
+ * to track that a new group started: Primary or
+ * Secondary service. */
+ if (bt_uuid_cmp(&a->uuid, &prim_uuid) == 0 ||
+ bt_uuid_cmp(&a->uuid, &snd_uuid) == 0)
+ range = NULL;
+ else
+ range->end = a->handle;
+ }
+ }
+
+ if (matches == NULL)
+ return enc_error_resp(ATT_OP_FIND_BY_TYPE_REQ, start,
+ ATT_ECODE_ATTR_NOT_FOUND, opdu, mtu);
+
+ len = enc_find_by_type_resp(matches, opdu, mtu);
+
+ g_slist_foreach(matches, (GFunc) g_free, NULL);
+ g_slist_free(matches);
+
+ return len;
+}
+
+static struct attribute *find_primary_range(uint16_t start, uint16_t *end)
+{
+ struct attribute *attrib;
+ guint h = start;
+ GSList *l;
+
+ if (end == NULL)
+ return NULL;
+
+ l = g_slist_find_custom(database, GUINT_TO_POINTER(h), handle_cmp);
+ if (!l)
+ return NULL;
+
+ attrib = l->data;
+
+ if (bt_uuid_cmp(&attrib->uuid, &prim_uuid) != 0)
+ return NULL;
+
+ *end = start;
+
+ for (l = l->next; l; l = l->next) {
+ struct attribute *a = l->data;
+
+ if (bt_uuid_cmp(&a->uuid, &prim_uuid) == 0 ||
+ bt_uuid_cmp(&a->uuid, &snd_uuid) == 0)
+ break;
+
+ *end = a->handle;
+ }
+
+ return attrib;
+}
+
+static uint16_t read_value(struct gatt_channel *channel, uint16_t handle,
+ uint8_t *pdu, int len)
+{
+ struct attribute *a, *client_attr;
+ uint8_t status;
+ GSList *l;
+ guint h = handle;
+
+ l = g_slist_find_custom(database, GUINT_TO_POINTER(h), handle_cmp);
+ if (!l)
+ return enc_error_resp(ATT_OP_READ_REQ, handle,
+ ATT_ECODE_INVALID_HANDLE, pdu, len);
+
+ a = l->data;
+
+ status = att_check_reqs(channel, ATT_OP_READ_REQ, a->read_reqs);
+
+ client_attr = client_cfg_attribute(channel, a, a->data, a->len);
+ if (client_attr)
+ a = client_attr;
+
+ if (status == 0x00 && a->read_cb)
+ status = a->read_cb(a, a->cb_user_data);
+
+ if (status)
+ return enc_error_resp(ATT_OP_READ_REQ, handle, status, pdu,
+ len);
+
+ return enc_read_resp(a->data, a->len, pdu, len);
+}
+
+static uint16_t read_blob(struct gatt_channel *channel, uint16_t handle,
+ uint16_t offset, uint8_t *pdu, int len)
+{
+ struct attribute *a, *client_attr;
+ uint8_t status;
+ GSList *l;
+ guint h = handle;
+
+ l = g_slist_find_custom(database, GUINT_TO_POINTER(h), handle_cmp);
+ if (!l)
+ return enc_error_resp(ATT_OP_READ_BLOB_REQ, handle,
+ ATT_ECODE_INVALID_HANDLE, pdu, len);
+
+ a = l->data;
+
+ if (a->len <= offset)
+ return enc_error_resp(ATT_OP_READ_BLOB_REQ, handle,
+ ATT_ECODE_INVALID_OFFSET, pdu, len);
+
+ status = att_check_reqs(channel, ATT_OP_READ_BLOB_REQ, a->read_reqs);
+
+ client_attr = client_cfg_attribute(channel, a, a->data, a->len);
+ if (client_attr)
+ a = client_attr;
+
+ if (status == 0x00 && a->read_cb)
+ status = a->read_cb(a, a->cb_user_data);
+
+ if (status)
+ return enc_error_resp(ATT_OP_READ_BLOB_REQ, handle, status,
+ pdu, len);
+
+ return enc_read_blob_resp(a->data, a->len, offset, pdu, len);
+}
+
+static uint16_t write_value(struct gatt_channel *channel, uint16_t handle,
+ const uint8_t *value, int vlen,
+ uint8_t *pdu, int len)
+{
+ struct attribute *a, *client_attr;
+ uint8_t status;
+ GSList *l;
+ guint h = handle;
+
+ l = g_slist_find_custom(database, GUINT_TO_POINTER(h), handle_cmp);
+ if (!l)
+ return enc_error_resp(ATT_OP_WRITE_REQ, handle,
+ ATT_ECODE_INVALID_HANDLE, pdu, len);
+
+ a = l->data;
+
+ status = att_check_reqs(channel, ATT_OP_WRITE_REQ, a->write_reqs);
+ if (status)
+ return enc_error_resp(ATT_OP_WRITE_REQ, handle, status, pdu,
+ len);
+
+ client_attr = client_cfg_attribute(channel, a, value, vlen);
+ if (client_attr)
+ a = client_attr;
+ else
+ attrib_db_update(a->handle, NULL, value, vlen, &a);
+
+ if (a->write_cb) {
+ status = a->write_cb(a, a->cb_user_data);
+ if (status)
+ return enc_error_resp(ATT_OP_WRITE_REQ, handle, status,
+ pdu, len);
+ }
+
+ DBG("Notifications: %d, indications: %d",
+ g_slist_length(channel->notify),
+ g_slist_length(channel->indicate));
+
+ return enc_write_resp(pdu, len);
+}
+
+static uint16_t mtu_exchange(struct gatt_channel *channel, uint16_t mtu,
+ uint8_t *pdu, int len)
+{
+ guint old_mtu = channel->mtu;
+
+ if (mtu < ATT_DEFAULT_LE_MTU)
+ channel->mtu = ATT_DEFAULT_LE_MTU;
+ else
+ channel->mtu = MIN(mtu, channel->mtu);
+
+ bt_io_set(le_io, BT_IO_L2CAP, NULL,
+ BT_IO_OPT_OMTU, channel->mtu,
+ BT_IO_OPT_INVALID);
+
+ return enc_mtu_resp(old_mtu, pdu, len);
+}
+
+static void channel_disconnect(void *user_data)
+{
+ struct gatt_channel *channel = user_data;
+
+ g_attrib_unref(channel->attrib);
+ clients = g_slist_remove(clients, channel);
+
+ g_slist_free(channel->notify);
+ g_slist_free(channel->indicate);
+ g_slist_foreach(channel->configs, (GFunc) g_free, NULL);
+ g_slist_free(channel->configs);
+
+ g_free(channel);
+}
+
+static void channel_handler(const uint8_t *ipdu, uint16_t len,
+ gpointer user_data)
+{
+ struct gatt_channel *channel = user_data;
+ uint8_t opdu[ATT_MAX_MTU], value[ATT_MAX_MTU];
+ uint16_t length, start, end, mtu, offset;
+ bt_uuid_t uuid;
+ uint8_t status = 0;
+ int vlen;
+
+ DBG("op 0x%02x", ipdu[0]);
+
+ switch (ipdu[0]) {
+ case ATT_OP_READ_BY_GROUP_REQ:
+ length = dec_read_by_grp_req(ipdu, len, &start, &end, &uuid);
+ if (length == 0) {
+ status = ATT_ECODE_INVALID_PDU;
+ goto done;
+ }
+
+ length = read_by_group(channel, start, end, &uuid, opdu,
+ channel->mtu);
+ break;
+ case ATT_OP_READ_BY_TYPE_REQ:
+ length = dec_read_by_type_req(ipdu, len, &start, &end, &uuid);
+ if (length == 0) {
+ status = ATT_ECODE_INVALID_PDU;
+ goto done;
+ }
+
+ length = read_by_type(channel, start, end, &uuid, opdu,
+ channel->mtu);
+ break;
+ case ATT_OP_READ_REQ:
+ length = dec_read_req(ipdu, len, &start);
+ if (length == 0) {
+ status = ATT_ECODE_INVALID_PDU;
+ goto done;
+ }
+
+ length = read_value(channel, start, opdu, channel->mtu);
+ break;
+ case ATT_OP_READ_BLOB_REQ:
+ length = dec_read_blob_req(ipdu, len, &start, &offset);
+ if (length == 0) {
+ status = ATT_ECODE_INVALID_PDU;
+ goto done;
+ }
+
+ length = read_blob(channel, start, offset, opdu, channel->mtu);
+ break;
+ case ATT_OP_MTU_REQ:
+ if (!channel->le) {
+ status = ATT_ECODE_REQ_NOT_SUPP;
+ goto done;
+ }
+
+ length = dec_mtu_req(ipdu, len, &mtu);
+ if (length == 0) {
+ status = ATT_ECODE_INVALID_PDU;
+ goto done;
+ }
+
+ length = mtu_exchange(channel, mtu, opdu, channel->mtu);
+ break;
+ case ATT_OP_FIND_INFO_REQ:
+ length = dec_find_info_req(ipdu, len, &start, &end);
+ if (length == 0) {
+ status = ATT_ECODE_INVALID_PDU;
+ goto done;
+ }
+
+ length = find_info(start, end, opdu, channel->mtu);
+ break;
+ case ATT_OP_WRITE_REQ:
+ length = dec_write_req(ipdu, len, &start, value, &vlen);
+ if (length == 0) {
+ status = ATT_ECODE_INVALID_PDU;
+ goto done;
+ }
+
+ length = write_value(channel, start, value, vlen, opdu,
+ channel->mtu);
+ break;
+ case ATT_OP_WRITE_CMD:
+ length = dec_write_cmd(ipdu, len, &start, value, &vlen);
+ if (length > 0)
+ write_value(channel, start, value, vlen, opdu,
+ channel->mtu);
+ return;
+ case ATT_OP_FIND_BY_TYPE_REQ:
+ length = dec_find_by_type_req(ipdu, len, &start, &end,
+ &uuid, value, &vlen);
+ if (length == 0) {
+ status = ATT_ECODE_INVALID_PDU;
+ goto done;
+ }
+
+ length = find_by_type(start, end, &uuid, value, vlen,
+ opdu, channel->mtu);
+ break;
+ case ATT_OP_HANDLE_CNF:
+ return;
+ case ATT_OP_READ_MULTI_REQ:
+ case ATT_OP_PREP_WRITE_REQ:
+ case ATT_OP_EXEC_WRITE_REQ:
+ default:
+ DBG("Unsupported request 0x%02x", ipdu[0]);
+ status = ATT_ECODE_REQ_NOT_SUPP;
+ goto done;
+ }
+
+ if (length == 0)
+ status = ATT_ECODE_IO;
+
+done:
+ if (status)
+ length = enc_error_resp(ipdu[0], 0x0000, status, opdu,
+ channel->mtu);
+
+ g_attrib_send(channel->attrib, 0, opdu[0], opdu, length,
+ NULL, NULL, NULL);
+}
+
+static void connect_event(GIOChannel *io, GError *err, void *user_data)
+{
+ struct gatt_channel *channel;
+ uint16_t cid;
+ GError *gerr = NULL;
+
+ if (err) {
+ error("%s", err->message);
+ return;
+ }
+
+ channel = g_new0(struct gatt_channel, 1);
+
+ bt_io_get(io, BT_IO_L2CAP, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, &channel->src,
+ BT_IO_OPT_DEST_BDADDR, &channel->dst,
+ BT_IO_OPT_CID, &cid,
+ BT_IO_OPT_OMTU, &channel->mtu,
+ BT_IO_OPT_INVALID);
+ if (gerr) {
+ error("bt_io_get: %s", gerr->message);
+ g_error_free(gerr);
+ g_free(channel);
+ g_io_channel_shutdown(io, TRUE, NULL);
+ return;
+ }
+
+ if (channel->mtu > ATT_MAX_MTU)
+ channel->mtu = ATT_MAX_MTU;
+
+ if (cid != GATT_CID)
+ channel->le = FALSE;
+ else
+ channel->le = TRUE;
+
+ channel->attrib = g_attrib_new(io);
+ g_io_channel_unref(io);
+
+ channel->id = g_attrib_register(channel->attrib, GATTRIB_ALL_EVENTS,
+ channel_handler, channel, NULL);
+
+ g_attrib_set_disconnect_function(channel->attrib, channel_disconnect,
+ channel);
+
+ clients = g_slist_append(clients, channel);
+}
+
+static void confirm_event(GIOChannel *io, void *user_data)
+{
+ GError *gerr = NULL;
+
+ if (bt_io_accept(io, connect_event, user_data, NULL, &gerr) == FALSE) {
+ error("bt_io_accept: %s", gerr->message);
+ g_error_free(gerr);
+ g_io_channel_unref(io);
+ }
+
+ return;
+}
+
+static void attrib_notify_clients(struct attribute *attr)
+{
+ guint handle = attr->handle;
+ GSList *l;
+
+ for (l = clients; l; l = l->next) {
+ struct gatt_channel *channel = l->data;
+
+ /* Notification */
+ if (g_slist_find_custom(channel->notify,
+ GUINT_TO_POINTER(handle), handle_cmp)) {
+ uint8_t pdu[ATT_MAX_MTU];
+ uint16_t len;
+
+ len = enc_notification(attr, pdu, channel->mtu);
+ if (len == 0)
+ continue;
+
+ g_attrib_send(channel->attrib, 0, pdu[0], pdu, len,
+ NULL, NULL, NULL);
+ }
+
+ /* Indication */
+ if (g_slist_find_custom(channel->indicate,
+ GUINT_TO_POINTER(handle), handle_cmp)) {
+ uint8_t pdu[ATT_MAX_MTU];
+ uint16_t len;
+
+ len = enc_indication(attr, pdu, channel->mtu);
+ if (len == 0)
+ return;
+
+ g_attrib_send(channel->attrib, 0, pdu[0], pdu, len,
+ NULL, NULL, NULL);
+ }
+ }
+}
+
+static gboolean register_core_services(void)
+{
+ uint8_t atval[256];
+ bt_uuid_t uuid;
+ uint16_t appearance = 0x0000;
+
+ /* GAP service: primary service definition */
+ bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
+ att_put_u16(GENERIC_ACCESS_PROFILE_ID, &atval[0]);
+ attrib_db_add(0x0001, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2);
+
+ /* GAP service: device name characteristic */
+ name_handle = 0x0006;
+ bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+ atval[0] = ATT_CHAR_PROPER_READ;
+ att_put_u16(name_handle, &atval[1]);
+ att_put_u16(GATT_CHARAC_DEVICE_NAME, &atval[3]);
+ attrib_db_add(0x0004, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
+
+ /* GAP service: device name attribute */
+ bt_uuid16_create(&uuid, GATT_CHARAC_DEVICE_NAME);
+ attrib_db_add(name_handle, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ NULL, 0);
+
+ /* GAP service: device appearance characteristic */
+ appearance_handle = 0x0008;
+ bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+ atval[0] = ATT_CHAR_PROPER_READ;
+ att_put_u16(appearance_handle, &atval[1]);
+ att_put_u16(GATT_CHARAC_APPEARANCE, &atval[3]);
+ attrib_db_add(0x0007, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
+
+ /* GAP service: device appearance attribute */
+ bt_uuid16_create(&uuid, GATT_CHARAC_APPEARANCE);
+ att_put_u16(appearance, &atval[0]);
+ attrib_db_add(appearance_handle, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ atval, 2);
+ gap_sdp_handle = attrib_create_sdp(0x0001, "Generic Access Profile");
+ if (gap_sdp_handle == 0) {
+ error("Failed to register GAP service record");
+ goto failed;
+ }
+
+ /* GATT service: primary service definition */
+ bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
+ att_put_u16(GENERIC_ATTRIB_PROFILE_ID, &atval[0]);
+ attrib_db_add(0x0010, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2);
+
+ gatt_sdp_handle = attrib_create_sdp(0x0010,
+ "Generic Attribute Profile");
+ if (gatt_sdp_handle == 0) {
+ error("Failed to register GATT service record");
+ goto failed;
+ }
+
+ return TRUE;
+
+failed:
+ if (gap_sdp_handle)
+ remove_record_from_server(gap_sdp_handle);
+
+ return FALSE;
+}
+
+int attrib_server_init(void)
+{
+ GError *gerr = NULL;
+
+ /* BR/EDR socket */
+ l2cap_io = bt_io_listen(BT_IO_L2CAP, NULL, confirm_event,
+ NULL, NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, BDADDR_ANY,
+ BT_IO_OPT_PSM, GATT_PSM,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_INVALID);
+ if (l2cap_io == NULL) {
+ error("%s", gerr->message);
+ g_error_free(gerr);
+ return -1;
+ }
+
+ if (!register_core_services())
+ goto failed;
+
+ if (!main_opts.le)
+ return 0;
+
+ /* LE socket */
+ le_io = bt_io_listen(BT_IO_L2CAP, NULL, confirm_event,
+ &le_io, NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, BDADDR_ANY,
+ BT_IO_OPT_CID, GATT_CID,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_INVALID);
+ if (le_io == NULL) {
+ error("%s", gerr->message);
+ g_error_free(gerr);
+ /* Doesn't have LE support, continue */
+ }
+
+ return 0;
+
+failed:
+ g_io_channel_unref(l2cap_io);
+ l2cap_io = NULL;
+
+ if (le_io) {
+ g_io_channel_unref(le_io);
+ le_io = NULL;
+ }
+
+ return -1;
+}
+
+void attrib_server_exit(void)
+{
+ GSList *l;
+
+ g_slist_foreach(database, (GFunc) g_free, NULL);
+ g_slist_free(database);
+
+ if (l2cap_io) {
+ g_io_channel_unref(l2cap_io);
+ g_io_channel_shutdown(l2cap_io, FALSE, NULL);
+ }
+
+ if (le_io) {
+ g_io_channel_unref(le_io);
+ g_io_channel_shutdown(le_io, FALSE, NULL);
+ }
+
+ for (l = clients; l; l = l->next) {
+ struct gatt_channel *channel = l->data;
+
+ g_slist_free(channel->notify);
+ g_slist_free(channel->indicate);
+ g_slist_foreach(channel->configs, (GFunc) g_free, NULL);
+ g_slist_free(channel->configs);
+
+ g_attrib_unref(channel->attrib);
+ g_free(channel);
+ }
+
+ g_slist_free(clients);
+
+ if (gatt_sdp_handle)
+ remove_record_from_server(gatt_sdp_handle);
+
+ if (gap_sdp_handle)
+ remove_record_from_server(gap_sdp_handle);
+}
+
+uint32_t attrib_create_sdp(uint16_t handle, const char *name)
+{
+ sdp_record_t *record;
+ struct attribute *a;
+ uint16_t end = 0;
+ uuid_t svc, gap_uuid;
+
+ a = find_primary_range(handle, &end);
+
+ if (a == NULL)
+ return 0;
+
+ if (a->len == 2)
+ sdp_uuid16_create(&svc, att_get_u16(a->data));
+ else if (a->len == 16)
+ sdp_uuid128_create(&svc, a->data);
+ else
+ return 0;
+
+ record = server_record_new(&svc, handle, end);
+ if (record == NULL)
+ return 0;
+
+ if (name)
+ sdp_set_info_attr(record, name, "BlueZ", NULL);
+
+ sdp_uuid16_create(&gap_uuid, GENERIC_ACCESS_PROFILE_ID);
+ if (sdp_uuid_cmp(&svc, &gap_uuid) == 0) {
+ sdp_set_url_attr(record, "http://www.bluez.org/",
+ "http://www.bluez.org/",
+ "http://www.bluez.org/");
+ }
+
+ if (add_record_to_server(BDADDR_ANY, record) < 0)
+ sdp_record_free(record);
+ else
+ return record->handle;
+
+ return 0;
+}
+
+void attrib_free_sdp(uint32_t sdp_handle)
+{
+ remove_record_from_server(sdp_handle);
+}
+
+struct attribute *attrib_db_add(uint16_t handle, bt_uuid_t *uuid, int read_reqs,
+ int write_reqs, const uint8_t *value, int len)
+{
+ struct attribute *a;
+ guint h = handle;
+
+ DBG("handle=0x%04x", handle);
+
+ if (g_slist_find_custom(database, GUINT_TO_POINTER(h), handle_cmp))
+ return NULL;
+
+ a = g_malloc0(sizeof(struct attribute) + len);
+ a->handle = handle;
+ memcpy(&a->uuid, uuid, sizeof(bt_uuid_t));
+ a->read_reqs = read_reqs;
+ a->write_reqs = write_reqs;
+ a->len = len;
+ memcpy(a->data, value, len);
+
+ database = g_slist_insert_sorted(database, a, attribute_cmp);
+
+ return a;
+}
+
+int attrib_db_update(uint16_t handle, bt_uuid_t *uuid, const uint8_t *value,
+ int len, struct attribute **attr)
+{
+ struct attribute *a;
+ GSList *l;
+ guint h = handle;
+
+ DBG("handle=0x%04x", handle);
+
+ l = g_slist_find_custom(database, GUINT_TO_POINTER(h), handle_cmp);
+ if (!l)
+ return -ENOENT;
+
+ a = g_try_realloc(l->data, sizeof(struct attribute) + len);
+ if (a == NULL)
+ return -ENOMEM;
+
+ l->data = a;
+ if (uuid != NULL)
+ memcpy(&a->uuid, uuid, sizeof(bt_uuid_t));
+ a->len = len;
+ memcpy(a->data, value, len);
+
+ attrib_notify_clients(a);
+
+ if (attr)
+ *attr = a;
+
+ return 0;
+}
+
+int attrib_db_del(uint16_t handle)
+{
+ struct attribute *a;
+ GSList *l;
+ guint h = handle;
+
+ DBG("handle=0x%04x", handle);
+
+ l = g_slist_find_custom(database, GUINT_TO_POINTER(h), handle_cmp);
+ if (!l)
+ return -ENOENT;
+
+ a = l->data;
+ database = g_slist_remove(database, a);
+ g_free(a);
+
+ return 0;
+}
+
+int attrib_gap_set(uint16_t uuid, const uint8_t *value, int len)
+{
+ uint16_t handle;
+
+ /* FIXME: Missing Privacy and Reconnection Address */
+
+ switch (uuid) {
+ case GATT_CHARAC_DEVICE_NAME:
+ handle = name_handle;
+ break;
+ case GATT_CHARAC_APPEARANCE:
+ handle = appearance_handle;
+ break;
+ default:
+ return -ENOSYS;
+ }
+
+ return attrib_db_update(handle, NULL, value, len, NULL);
+}
diff --git a/src/attrib-server.h b/src/attrib-server.h
new file mode 100644
index 0000000..38a1f05
--- /dev/null
+++ b/src/attrib-server.h
@@ -0,0 +1,35 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2010 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
+ *
+ */
+
+int attrib_server_init(void);
+void attrib_server_exit(void);
+
+struct attribute *attrib_db_add(uint16_t handle, bt_uuid_t *uuid, int read_reqs,
+ int write_reqs, const uint8_t *value, int len);
+int attrib_db_update(uint16_t handle, bt_uuid_t *uuid, const uint8_t *value,
+ int len, struct attribute **attr);
+int attrib_db_del(uint16_t handle);
+int attrib_gap_set(uint16_t uuid, const uint8_t *value, int len);
+uint32_t attrib_create_sdp(uint16_t handle, const char *name);
+void attrib_free_sdp(uint32_t sdp_handle);
diff --git a/src/bluetooth.conf b/src/bluetooth.conf
new file mode 100644
index 0000000..853f926
--- /dev/null
+++ b/src/bluetooth.conf
@@ -0,0 +1,33 @@
+<!-- This configuration file specifies the required security policies
+ for Bluetooth core daemon to work. -->
+
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+
+ <!-- ../system.conf have denied everything, so we just punch some holes -->
+
+ <policy user="root">
+ <allow own="org.bluez"/>
+ <allow send_destination="org.bluez"/>
+ <allow send_interface="org.bluez.Agent"/>
+ <allow send_interface="org.bluez.HandsfreeAgent"/>
+ <allow send_interface="org.bluez.MediaEndpoint"/>
+ <allow send_interface="org.bluez.Watcher"/>
+ </policy>
+
+ <policy at_console="true">
+ <allow send_destination="org.bluez"/>
+ </policy>
+
+ <!-- allow users of lp group (printing subsystem) to
+ communicate with bluetoothd -->
+ <policy group="lp">
+ <allow send_destination="org.bluez"/>
+ </policy>
+
+ <policy context="default">
+ <deny send_destination="org.bluez"/>
+ </policy>
+
+</busconfig>
diff --git a/src/bluetooth.ver b/src/bluetooth.ver
new file mode 100644
index 0000000..b71c70d
--- /dev/null
+++ b/src/bluetooth.ver
@@ -0,0 +1,10 @@
+{
+ global:
+ btd_*;
+ g_dbus_*;
+ info;
+ error;
+ debug;
+ local:
+ *;
+};
diff --git a/src/bluetoothd.8.in b/src/bluetoothd.8.in
new file mode 100644
index 0000000..a7ae77b
--- /dev/null
+++ b/src/bluetoothd.8.in
@@ -0,0 +1,91 @@
+.\"
+.TH "BLUETOOTHD" "8" "March 2004" "Bluetooth daemon" "System management commands"
+.SH "NAME"
+bluetoothd \- Bluetooth daemon
+
+.SH "SYNOPSIS"
+.B bluetoothd
+[
+.B \-n
+]
+
+.SH "DESCRIPTION"
+This manual page documents briefly the
+.B bluetoothd
+daemon, which manages all the Bluetooth devices.
+.B bluetoothd
+itself does not accept many command\-line options, as most of its
+configuration is done in the
+.B @CONFIGDIR@/main.conf
+file, which has its own man page.
+.B bluetoothd
+can also provide a number of services via the D-Bus message bus
+system.
+.SH "OPTIONS"
+.TP
+.BI \-n
+Don't run as daemon in background.
+.TP
+.BI \-d
+Enable debug information output.
+.TP
+.BI \-m\ mtu\-size
+Use specific MTU size for SDP server.
+
+.SH "FILES"
+.TP
+.I @CONFIGDIR@/main.conf
+Default location of the global configuration file.
+
+.TP
+.I @STORAGEDIR@/nn:nn:nn:nn:nn:nn/linkkeys
+Default location for link keys of paired devices. The directory
+\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP
+is the address of the local device. The file is line separated, with
+the following columns separated by whitespace:
+
+\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP Remote device address.
+
+\fInnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn\fP Link key.
+
+\fIn\fP Link type integer.
+
+.TP
+.I @STORAGEDIR@/nn:nn:nn:nn:nn:nn/names
+Default location for the device name cache. The directory
+\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP
+is the address of the local device. The file is line separated, with
+the following columns separated by whitespace:
+
+\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP Remote device address.
+
+\fIname\fP Remote device name, terminated with newline.
+
+.TP
+.I @STORAGEDIR@/nn:nn:nn:nn:nn:nn/features
+Default location for the features cache. The directory
+\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP
+is the address of the local device. The file is line separated, with
+the following columns separated by whitespace:
+
+\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP Remote device address.
+
+\fInnnnnnnnnnnnnnnn\fP Remote device LMP features coded as an 8 byte bitfield.
+
+.TP
+.I @STORAGEDIR@/nn:nn:nn:nn:nn:nn/manufacturers
+Default location for the manufacturers cache. The directory
+\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP
+is the address of the local device. The file is line separated, with
+the following columns separated by whitespace:
+
+\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP Remote device address.
+
+\fIn\fP Remote device manufacturer integer.
+
+\fIn\fP Remote device LMP version integer.
+
+\fIn\fP Remote device LMP sub-version integer.
+
+.SH "AUTHOR"
+This manual page was written by Marcel Holtmann, Philipp Matthias Hahn and Fredrik Noring.
diff --git a/src/dbus-common.c b/src/dbus-common.c
new file mode 100644
index 0000000..8436100
--- /dev/null
+++ b/src/dbus-common.c
@@ -0,0 +1,254 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2005-2007 Johan Hedberg <johan.hedberg@nokia.com>
+ *
+ *
+ * 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 <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#include <bluetooth/bluetooth.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+
+#include "adapter.h"
+#include "manager.h"
+#include "event.h"
+#include "dbus-common.h"
+
+static DBusConnection *connection = NULL;
+
+static void append_variant(DBusMessageIter *iter, int type, void *val)
+{
+ DBusMessageIter value;
+ char sig[2] = { type, '\0' };
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, sig, &value);
+
+ dbus_message_iter_append_basic(&value, type, val);
+
+ dbus_message_iter_close_container(iter, &value);
+}
+
+static void append_array_variant(DBusMessageIter *iter, int type, void *val,
+ int n_elements)
+{
+ DBusMessageIter variant, array;
+ char type_sig[2] = { type, '\0' };
+ char array_sig[3] = { DBUS_TYPE_ARRAY, type, '\0' };
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+ array_sig, &variant);
+
+ dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
+ type_sig, &array);
+
+ if (dbus_type_is_fixed(type) == TRUE) {
+ dbus_message_iter_append_fixed_array(&array, type, val,
+ n_elements);
+ } else if (type == DBUS_TYPE_STRING || type == DBUS_TYPE_OBJECT_PATH) {
+ const char ***str_array = val;
+ int i;
+
+ for (i = 0; i < n_elements; i++)
+ dbus_message_iter_append_basic(&array, type,
+ &((*str_array)[i]));
+ }
+
+ dbus_message_iter_close_container(&variant, &array);
+
+ dbus_message_iter_close_container(iter, &variant);
+}
+
+void dict_append_entry(DBusMessageIter *dict,
+ const char *key, int type, void *val)
+{
+ DBusMessageIter entry;
+
+ if (type == DBUS_TYPE_STRING) {
+ const char *str = *((const char **) val);
+ if (str == NULL)
+ return;
+ }
+
+ dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+ NULL, &entry);
+
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+ append_variant(&entry, type, val);
+
+ dbus_message_iter_close_container(dict, &entry);
+}
+
+void dict_append_array(DBusMessageIter *dict, const char *key, int type,
+ void *val, int n_elements)
+{
+ DBusMessageIter entry;
+
+ dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+ NULL, &entry);
+
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+ append_array_variant(&entry, type, val, n_elements);
+
+ dbus_message_iter_close_container(dict, &entry);
+}
+
+dbus_bool_t emit_property_changed(DBusConnection *conn,
+ const char *path,
+ const char *interface,
+ const char *name,
+ int type, void *value)
+{
+ DBusMessage *signal;
+ DBusMessageIter iter;
+
+ signal = dbus_message_new_signal(path, interface, "PropertyChanged");
+
+ if (!signal) {
+ error("Unable to allocate new %s.PropertyChanged signal",
+ interface);
+ return FALSE;
+ }
+
+ dbus_message_iter_init_append(signal, &iter);
+
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);
+
+ append_variant(&iter, type, value);
+
+ return g_dbus_send_message(conn, signal);
+}
+
+dbus_bool_t emit_array_property_changed(DBusConnection *conn,
+ const char *path,
+ const char *interface,
+ const char *name,
+ int type, void *value, int num)
+{
+ DBusMessage *signal;
+ DBusMessageIter iter;
+
+ signal = dbus_message_new_signal(path, interface, "PropertyChanged");
+
+ if (!signal) {
+ error("Unable to allocate new %s.PropertyChanged signal",
+ interface);
+ return FALSE;
+ }
+
+ dbus_message_iter_init_append(signal, &iter);
+
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);
+
+ append_array_variant(&iter, type, value, num);
+
+ return g_dbus_send_message(conn, signal);
+}
+
+void set_dbus_connection(DBusConnection *conn)
+{
+ connection = conn;
+}
+
+DBusConnection *get_dbus_connection(void)
+{
+ return connection;
+}
+
+const char *class_to_icon(uint32_t class)
+{
+ switch ((class & 0x1f00) >> 8) {
+ case 0x01:
+ return "computer";
+ case 0x02:
+ switch ((class & 0xfc) >> 2) {
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x05:
+ return "phone";
+ case 0x04:
+ return "modem";
+ }
+ break;
+ case 0x03:
+ return "network-wireless";
+ case 0x04:
+ switch ((class & 0xfc) >> 2) {
+ case 0x01:
+ case 0x02:
+ return "audio-card"; /* Headset */
+ case 0x06:
+ return "audio-card"; /* Headphone */
+ case 0x0b: /* VCR */
+ case 0x0c: /* Video Camera */
+ case 0x0d: /* Camcorder */
+ return "camera-video";
+ default:
+ return "audio-card"; /* Other audio device */
+ }
+ break;
+ case 0x05:
+ switch ((class & 0xc0) >> 6) {
+ case 0x00:
+ switch ((class & 0x1e) >> 2) {
+ case 0x01:
+ case 0x02:
+ return "input-gaming";
+ }
+ break;
+ case 0x01:
+ return "input-keyboard";
+ case 0x02:
+ switch ((class & 0x1e) >> 2) {
+ case 0x05:
+ return "input-tablet";
+ default:
+ return "input-mouse";
+ }
+ }
+ break;
+ case 0x06:
+ if (class & 0x80)
+ return "printer";
+ if (class & 0x20)
+ return "camera-photo";
+ break;
+ }
+
+ return NULL;
+}
diff --git a/src/dbus-common.h b/src/dbus-common.h
new file mode 100644
index 0000000..b196a1b
--- /dev/null
+++ b/src/dbus-common.h
@@ -0,0 +1,47 @@
+/* *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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
+ *
+ */
+
+#define MAX_PATH_LENGTH 64
+
+void dict_append_entry(DBusMessageIter *dict,
+ const char *key, int type, void *val);
+
+void dict_append_array(DBusMessageIter *dict, const char *key, int type,
+ void *val, int n_elements);
+
+dbus_bool_t emit_property_changed(DBusConnection *conn,
+ const char *path,
+ const char *interface,
+ const char *name,
+ int type, void *value);
+
+dbus_bool_t emit_array_property_changed(DBusConnection *conn,
+ const char *path,
+ const char *interface,
+ const char *name,
+ int type, void *value, int num);
+
+void set_dbus_connection(DBusConnection *conn);
+DBusConnection *get_dbus_connection(void);
+
+const char *class_to_icon(uint32_t class);
diff --git a/src/device.c b/src/device.c
new file mode 100644
index 0000000..771a908
--- /dev/null
+++ b/src/device.c
@@ -0,0 +1,2388 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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 <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/uuid.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+#include "textfile.h"
+
+#include "att.h"
+#include "hcid.h"
+#include "adapter.h"
+#include "device.h"
+#include "dbus-common.h"
+#include "event.h"
+#include "error.h"
+#include "glib-helper.h"
+#include "gattrib.h"
+#include "gatt.h"
+#include "agent.h"
+#include "sdp-xml.h"
+#include "storage.h"
+#include "btio.h"
+
+#define DISCONNECT_TIMER 2
+#define DISCOVERY_TIMER 2
+
+/* When all services should trust a remote device */
+#define GLOBAL_TRUST "[all]"
+
+struct btd_disconnect_data {
+ guint id;
+ disconnect_watch watch;
+ void *user_data;
+ GDestroyNotify destroy;
+};
+
+struct bonding_req {
+ DBusConnection *conn;
+ DBusMessage *msg;
+ GIOChannel *io;
+ guint listener_id;
+ struct btd_device *device;
+};
+
+struct authentication_req {
+ auth_type_t type;
+ void *cb;
+ struct agent *agent;
+ struct btd_device *device;
+};
+
+struct browse_req {
+ DBusConnection *conn;
+ DBusMessage *msg;
+ GAttrib *attrib;
+ GIOChannel *io;
+ struct btd_device *device;
+ GSList *match_uuids;
+ GSList *profiles_added;
+ GSList *profiles_removed;
+ sdp_list_t *records;
+ int search_uuid;
+ int reconnect_attempt;
+ guint listener_id;
+};
+
+struct btd_device {
+ bdaddr_t bdaddr;
+ device_type_t type;
+ gchar *path;
+ char name[MAX_NAME_LENGTH + 1];
+ char *alias;
+ struct btd_adapter *adapter;
+ GSList *uuids;
+ GSList *services; /* Primary services path */
+ GSList *primaries; /* List of primary services */
+ GSList *drivers; /* List of device drivers */
+ GSList *watches; /* List of disconnect_data */
+ gboolean temporary;
+ struct agent *agent;
+ guint disconn_timer;
+ guint discov_timer;
+ struct browse_req *browse; /* service discover request */
+ struct bonding_req *bonding;
+ struct authentication_req *authr; /* authentication request */
+ GSList *disconnects; /* disconnects message */
+
+ gboolean connected;
+
+ /* Whether were creating a security mode 3 connection */
+ gboolean secmode3;
+
+ sdp_list_t *tmp_records;
+
+ gboolean trusted;
+ gboolean paired;
+ gboolean blocked;
+
+ gboolean authorizing;
+ gint ref;
+};
+
+static uint16_t uuid_list[] = {
+ L2CAP_UUID,
+ PNP_INFO_SVCLASS_ID,
+ PUBLIC_BROWSE_GROUP,
+ 0
+};
+
+static GSList *device_drivers = NULL;
+
+static void browse_request_free(struct browse_req *req)
+{
+ if (req->listener_id)
+ g_dbus_remove_watch(req->conn, req->listener_id);
+ if (req->msg)
+ dbus_message_unref(req->msg);
+ if (req->conn)
+ dbus_connection_unref(req->conn);
+ if (req->device)
+ btd_device_unref(req->device);
+ g_slist_foreach(req->profiles_added, (GFunc) g_free, NULL);
+ g_slist_free(req->profiles_added);
+ g_slist_free(req->profiles_removed);
+ if (req->records)
+ sdp_list_free(req->records, (sdp_free_func_t) sdp_record_free);
+
+ if (req->io) {
+ g_attrib_unref(req->attrib);
+ g_io_channel_unref(req->io);
+ g_io_channel_shutdown(req->io, FALSE, NULL);
+ }
+
+ g_free(req);
+}
+
+static void browse_request_cancel(struct browse_req *req)
+{
+ struct btd_device *device = req->device;
+ struct btd_adapter *adapter = device->adapter;
+ bdaddr_t src;
+
+ if (device_is_creating(device, NULL))
+ device_set_temporary(device, TRUE);
+
+ adapter_get_address(adapter, &src);
+
+ bt_cancel_discovery(&src, &device->bdaddr);
+
+ device->browse = NULL;
+ browse_request_free(req);
+}
+
+static void device_free(gpointer user_data)
+{
+ struct btd_device *device = user_data;
+ struct btd_adapter *adapter = device->adapter;
+ struct agent *agent = adapter_get_agent(adapter);
+
+ if (device->agent)
+ agent_free(device->agent);
+
+ if (agent && (agent_is_busy(agent, device) ||
+ agent_is_busy(agent, device->authr)))
+ agent_cancel(agent);
+
+ g_slist_foreach(device->services, (GFunc) g_free, NULL);
+ g_slist_free(device->services);
+
+ g_slist_foreach(device->uuids, (GFunc) g_free, NULL);
+ g_slist_free(device->uuids);
+
+ g_slist_foreach(device->primaries, (GFunc) g_free, NULL);
+ g_slist_free(device->primaries);
+
+ if (device->tmp_records)
+ sdp_list_free(device->tmp_records,
+ (sdp_free_func_t) sdp_record_free);
+
+ if (device->disconn_timer)
+ g_source_remove(device->disconn_timer);
+
+ if (device->discov_timer)
+ g_source_remove(device->discov_timer);
+
+ DBG("%p", device);
+
+ g_free(device->authr);
+ g_free(device->path);
+ g_free(device->alias);
+ g_free(device);
+}
+
+gboolean device_is_paired(struct btd_device *device)
+{
+ return device->paired;
+}
+
+gboolean device_is_trusted(struct btd_device *device)
+{
+ return device->trusted;
+}
+
+static DBusMessage *get_properties(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct btd_device *device = user_data;
+ struct btd_adapter *adapter = device->adapter;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ bdaddr_t src;
+ char name[MAX_NAME_LENGTH + 1], srcaddr[18], dstaddr[18];
+ char **str;
+ const char *ptr;
+ dbus_bool_t boolean;
+ uint32_t class;
+ int i;
+ GSList *l;
+
+ ba2str(&device->bdaddr, dstaddr);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ /* Address */
+ ptr = dstaddr;
+ dict_append_entry(&dict, "Address", DBUS_TYPE_STRING, &ptr);
+
+ /* Name */
+ ptr = NULL;
+ memset(name, 0, sizeof(name));
+ adapter_get_address(adapter, &src);
+ ba2str(&src, srcaddr);
+
+ ptr = device->name;
+ dict_append_entry(&dict, "Name", DBUS_TYPE_STRING, &ptr);
+
+ /* Alias (fallback to name or address) */
+ if (device->alias != NULL)
+ ptr = device->alias;
+ else if (strlen(ptr) == 0) {
+ g_strdelimit(dstaddr, ":", '-');
+ ptr = dstaddr;
+ }
+
+ dict_append_entry(&dict, "Alias", DBUS_TYPE_STRING, &ptr);
+
+ /* Class */
+ if (read_remote_class(&src, &device->bdaddr, &class) == 0) {
+ const char *icon = class_to_icon(class);
+
+ dict_append_entry(&dict, "Class", DBUS_TYPE_UINT32, &class);
+
+ if (icon)
+ dict_append_entry(&dict, "Icon",
+ DBUS_TYPE_STRING, &icon);
+ }
+
+ /* Paired */
+ boolean = device_is_paired(device);
+ dict_append_entry(&dict, "Paired", DBUS_TYPE_BOOLEAN, &boolean);
+
+ /* Trusted */
+ boolean = device_is_trusted(device);
+ dict_append_entry(&dict, "Trusted", DBUS_TYPE_BOOLEAN, &boolean);
+
+ /* Blocked */
+ boolean = device->blocked;
+ dict_append_entry(&dict, "Blocked", DBUS_TYPE_BOOLEAN, &boolean);
+
+ /* Connected */
+ dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN,
+ &device->connected);
+
+ /* UUIDs */
+ str = g_new0(char *, g_slist_length(device->uuids) + 1);
+ for (i = 0, l = device->uuids; l; l = l->next, i++)
+ str[i] = l->data;
+ dict_append_array(&dict, "UUIDs", DBUS_TYPE_STRING, &str, i);
+ g_free(str);
+
+ /* Services */
+ str = g_new0(char *, g_slist_length(device->services) + 1);
+ for (i = 0, l = device->services; l; l = l->next, i++)
+ str[i] = l->data;
+ dict_append_array(&dict, "Services", DBUS_TYPE_OBJECT_PATH, &str, i);
+ g_free(str);
+
+ /* Adapter */
+ ptr = adapter_get_path(adapter);
+ dict_append_entry(&dict, "Adapter", DBUS_TYPE_OBJECT_PATH, &ptr);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static DBusMessage *set_alias(DBusConnection *conn, DBusMessage *msg,
+ const char *alias, void *data)
+{
+ struct btd_device *device = data;
+ struct btd_adapter *adapter = device->adapter;
+ char srcaddr[18], dstaddr[18];
+ bdaddr_t src;
+ int err;
+
+ /* No change */
+ if ((device->alias == NULL && g_str_equal(alias, "")) ||
+ g_strcmp0(device->alias, alias) == 0)
+ return dbus_message_new_method_return(msg);
+
+ adapter_get_address(adapter, &src);
+ ba2str(&src, srcaddr);
+ ba2str(&device->bdaddr, dstaddr);
+
+ /* Remove alias if empty string */
+ err = write_device_alias(srcaddr, dstaddr,
+ g_str_equal(alias, "") ? NULL : alias);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+ g_free(device->alias);
+ device->alias = g_str_equal(alias, "") ? NULL : g_strdup(alias);
+
+ emit_property_changed(conn, dbus_message_get_path(msg),
+ DEVICE_INTERFACE, "Alias",
+ DBUS_TYPE_STRING, &alias);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *set_trust(DBusConnection *conn, DBusMessage *msg,
+ gboolean value, void *data)
+{
+ struct btd_device *device = data;
+ struct btd_adapter *adapter = device->adapter;
+ char srcaddr[18], dstaddr[18];
+ bdaddr_t src;
+ int err;
+
+ if (device->trusted == value)
+ return dbus_message_new_method_return(msg);
+
+ adapter_get_address(adapter, &src);
+ ba2str(&src, srcaddr);
+ ba2str(&device->bdaddr, dstaddr);
+
+ err = write_trust(srcaddr, dstaddr, GLOBAL_TRUST, value);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+ device->trusted = value;
+
+ emit_property_changed(conn, dbus_message_get_path(msg),
+ DEVICE_INTERFACE, "Trusted",
+ DBUS_TYPE_BOOLEAN, &value);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static void driver_remove(struct btd_device_driver *driver,
+ struct btd_device *device)
+{
+ driver->remove(device);
+
+ device->drivers = g_slist_remove(device->drivers, driver);
+}
+
+static gboolean do_disconnect(gpointer user_data)
+{
+ struct btd_device *device = user_data;
+
+ device->disconn_timer = 0;
+
+ btd_adapter_disconnect_device(device->adapter, &device->bdaddr);
+
+ return FALSE;
+}
+
+static int device_block(DBusConnection *conn, struct btd_device *device)
+{
+ int err;
+ bdaddr_t src;
+
+ if (device->blocked)
+ return 0;
+
+ if (device->connected)
+ do_disconnect(device);
+
+ g_slist_foreach(device->drivers, (GFunc) driver_remove, device);
+
+ err = btd_adapter_block_address(device->adapter, &device->bdaddr);
+ if (err < 0)
+ return err;
+
+ device->blocked = TRUE;
+
+ adapter_get_address(device->adapter, &src);
+
+ err = write_blocked(&src, &device->bdaddr, TRUE);
+ if (err < 0)
+ error("write_blocked(): %s (%d)", strerror(-err), -err);
+
+ device_set_temporary(device, FALSE);
+
+ emit_property_changed(conn, device->path, DEVICE_INTERFACE, "Blocked",
+ DBUS_TYPE_BOOLEAN, &device->blocked);
+
+ return 0;
+}
+
+static int device_unblock(DBusConnection *conn, struct btd_device *device,
+ gboolean silent)
+{
+ int err;
+ bdaddr_t src;
+
+ if (!device->blocked)
+ return 0;
+
+ err = btd_adapter_unblock_address(device->adapter, &device->bdaddr);
+ if (err < 0)
+ return err;
+
+ device->blocked = FALSE;
+
+ adapter_get_address(device->adapter, &src);
+
+ err = write_blocked(&src, &device->bdaddr, FALSE);
+ if (err < 0)
+ error("write_blocked(): %s (%d)", strerror(-err), -err);
+
+ if (!silent) {
+ emit_property_changed(conn, device->path,
+ DEVICE_INTERFACE, "Blocked",
+ DBUS_TYPE_BOOLEAN, &device->blocked);
+ device_probe_drivers(device, device->uuids);
+ }
+
+ return 0;
+}
+
+static DBusMessage *set_blocked(DBusConnection *conn, DBusMessage *msg,
+ gboolean value, void *data)
+{
+ struct btd_device *device = data;
+ int err;
+
+ if (value)
+ err = device_block(conn, device);
+ else
+ err = device_unblock(conn, device, FALSE);
+
+ switch (-err) {
+ case 0:
+ return dbus_message_new_method_return(msg);
+ case EINVAL:
+ return btd_error_failed(msg, "Kernel lacks blacklist support");
+ default:
+ return btd_error_failed(msg, strerror(-err));
+ }
+}
+
+static DBusMessage *set_property(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessageIter iter;
+ DBusMessageIter sub;
+ const char *property;
+
+ if (!dbus_message_iter_init(msg, &iter))
+ return btd_error_invalid_args(msg);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&iter, &property);
+ dbus_message_iter_next(&iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+ return btd_error_invalid_args(msg);
+ dbus_message_iter_recurse(&iter, &sub);
+
+ if (g_str_equal("Trusted", property)) {
+ dbus_bool_t value;
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN)
+ return btd_error_invalid_args(msg);
+ dbus_message_iter_get_basic(&sub, &value);
+
+ return set_trust(conn, msg, value, data);
+ } else if (g_str_equal("Alias", property)) {
+ const char *alias;
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING)
+ return btd_error_invalid_args(msg);
+ dbus_message_iter_get_basic(&sub, &alias);
+
+ return set_alias(conn, msg, alias, data);
+ } else if (g_str_equal("Blocked", property)) {
+ dbus_bool_t value;
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&sub, &value);
+
+ return set_blocked(conn, msg, value, data);
+ }
+
+ return btd_error_invalid_args(msg);
+}
+
+static void discover_services_req_exit(DBusConnection *conn, void *user_data)
+{
+ struct browse_req *req = user_data;
+
+ DBG("DiscoverServices requestor exited");
+
+ browse_request_cancel(req);
+}
+
+static DBusMessage *discover_services(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct btd_device *device = user_data;
+ const char *pattern;
+ int err;
+
+ if (device->browse)
+ return btd_error_in_progress(msg);
+
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &pattern,
+ DBUS_TYPE_INVALID) == FALSE)
+ return btd_error_invalid_args(msg);
+
+ if (strlen(pattern) == 0) {
+ err = device_browse_sdp(device, conn, msg, NULL, FALSE);
+ if (err < 0)
+ goto fail;
+ } else {
+ uuid_t uuid;
+
+ if (bt_string2uuid(&uuid, pattern) < 0)
+ return btd_error_invalid_args(msg);
+
+ sdp_uuid128_to_uuid(&uuid);
+
+ err = device_browse_sdp(device, conn, msg, &uuid, FALSE);
+ if (err < 0)
+ goto fail;
+ }
+
+ return NULL;
+
+fail:
+ return btd_error_failed(msg, strerror(-err));
+}
+
+static const char *browse_request_get_requestor(struct browse_req *req)
+{
+ if (!req->msg)
+ return NULL;
+
+ return dbus_message_get_sender(req->msg);
+}
+
+static void iter_append_record(DBusMessageIter *dict, uint32_t handle,
+ const char *record)
+{
+ DBusMessageIter entry;
+
+ dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+ NULL, &entry);
+
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_UINT32, &handle);
+
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &record);
+
+ dbus_message_iter_close_container(dict, &entry);
+}
+
+static void discover_services_reply(struct browse_req *req, int err,
+ sdp_list_t *recs)
+{
+ DBusMessage *reply;
+ DBusMessageIter iter, dict;
+ sdp_list_t *seq;
+
+ if (err) {
+ const char *err_if;
+
+ if (err == -EHOSTDOWN)
+ err_if = ERROR_INTERFACE ".ConnectionAttemptFailed";
+ else
+ err_if = ERROR_INTERFACE ".Failed";
+
+ reply = dbus_message_new_error(req->msg, err_if,
+ strerror(-err));
+ g_dbus_send_message(req->conn, reply);
+ return;
+ }
+
+ reply = dbus_message_new_method_return(req->msg);
+ if (!reply)
+ return;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_UINT32_AS_STRING DBUS_TYPE_STRING_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ for (seq = recs; seq; seq = seq->next) {
+ sdp_record_t *rec = (sdp_record_t *) seq->data;
+ GString *result;
+
+ if (!rec)
+ break;
+
+ result = g_string_new(NULL);
+
+ convert_sdp_record_to_xml(rec, result,
+ (void *) g_string_append);
+
+ if (result->len)
+ iter_append_record(&dict, rec->handle, result->str);
+
+ g_string_free(result, TRUE);
+ }
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ g_dbus_send_message(req->conn, reply);
+}
+
+static DBusMessage *cancel_discover(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct btd_device *device = user_data;
+ const char *sender = dbus_message_get_sender(msg);
+ const char *requestor;
+
+ if (!device->browse)
+ return btd_error_does_not_exist(msg);
+
+ if (!dbus_message_is_method_call(device->browse->msg, DEVICE_INTERFACE,
+ "DiscoverServices"))
+ return btd_error_not_authorized(msg);
+
+ requestor = browse_request_get_requestor(device->browse);
+
+ /* only the discover requestor can cancel the inquiry process */
+ if (!requestor || !g_str_equal(requestor, sender))
+ return btd_error_not_authorized(msg);
+
+ discover_services_reply(device->browse, -ECANCELED, NULL);
+
+ browse_request_cancel(device->browse);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static void bonding_request_cancel(struct bonding_req *bonding)
+{
+ struct btd_device *device = bonding->device;
+ struct btd_adapter *adapter = device->adapter;
+
+ adapter_cancel_bonding(adapter, &device->bdaddr);
+}
+
+void device_request_disconnect(struct btd_device *device, DBusMessage *msg)
+{
+ DBusConnection *conn = get_dbus_connection();
+
+ if (device->bonding)
+ bonding_request_cancel(device->bonding);
+
+ if (device->browse) {
+ discover_services_reply(device->browse, -ECANCELED, NULL);
+ browse_request_cancel(device->browse);
+ }
+
+ if (msg)
+ device->disconnects = g_slist_append(device->disconnects,
+ dbus_message_ref(msg));
+
+ if (device->disconn_timer)
+ return;
+
+ while (device->watches) {
+ struct btd_disconnect_data *data = device->watches->data;
+
+ if (data->watch)
+ /* temporary is set if device is going to be removed */
+ data->watch(device, device->temporary,
+ data->user_data);
+
+ /* Check if the watch has been removed by callback function */
+ if (!g_slist_find(device->watches, data))
+ continue;
+
+ device->watches = g_slist_remove(device->watches, data);
+ g_free(data);
+ }
+
+ device->disconn_timer = g_timeout_add_seconds(DISCONNECT_TIMER,
+ do_disconnect, device);
+
+ g_dbus_emit_signal(conn, device->path,
+ DEVICE_INTERFACE, "DisconnectRequested",
+ DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *disconnect(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct btd_device *device = user_data;
+
+ if (!device->connected)
+ return btd_error_not_connected(msg);
+
+ device_request_disconnect(device, msg);
+
+ return NULL;
+}
+
+static GDBusMethodTable device_methods[] = {
+ { "GetProperties", "", "a{sv}", get_properties },
+ { "SetProperty", "sv", "", set_property },
+ { "DiscoverServices", "s", "a{us}", discover_services,
+ G_DBUS_METHOD_FLAG_ASYNC},
+ { "CancelDiscovery", "", "", cancel_discover },
+ { "Disconnect", "", "", disconnect,
+ G_DBUS_METHOD_FLAG_ASYNC},
+ { }
+};
+
+static GDBusSignalTable device_signals[] = {
+ { "PropertyChanged", "sv" },
+ { "DisconnectRequested", "" },
+ { }
+};
+
+gboolean device_is_connected(struct btd_device *device)
+{
+ return device->connected;
+}
+
+void device_add_connection(struct btd_device *device, DBusConnection *conn)
+{
+ if (device->connected) {
+ char addr[18];
+ ba2str(&device->bdaddr, addr);
+ error("Device %s is already connected", addr);
+ return;
+ }
+
+ device->connected = TRUE;
+
+ emit_property_changed(conn, device->path,
+ DEVICE_INTERFACE, "Connected",
+ DBUS_TYPE_BOOLEAN, &device->connected);
+}
+
+void device_remove_connection(struct btd_device *device, DBusConnection *conn)
+{
+ if (!device->connected) {
+ char addr[18];
+ ba2str(&device->bdaddr, addr);
+ error("Device %s isn't connected", addr);
+ return;
+ }
+
+ device->connected = FALSE;
+
+ if (device->disconn_timer > 0) {
+ g_source_remove(device->disconn_timer);
+ device->disconn_timer = 0;
+ }
+
+ while (device->disconnects) {
+ DBusMessage *msg = device->disconnects->data;
+
+ g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID);
+ device->disconnects = g_slist_remove(device->disconnects, msg);
+ }
+
+ emit_property_changed(conn, device->path,
+ DEVICE_INTERFACE, "Connected",
+ DBUS_TYPE_BOOLEAN, &device->connected);
+}
+
+guint device_add_disconnect_watch(struct btd_device *device,
+ disconnect_watch watch, void *user_data,
+ GDestroyNotify destroy)
+{
+ struct btd_disconnect_data *data;
+ static guint id = 0;
+
+ data = g_new0(struct btd_disconnect_data, 1);
+ data->id = ++id;
+ data->watch = watch;
+ data->user_data = user_data;
+ data->destroy = destroy;
+
+ device->watches = g_slist_append(device->watches, data);
+
+ return data->id;
+}
+
+void device_remove_disconnect_watch(struct btd_device *device, guint id)
+{
+ GSList *l;
+
+ for (l = device->watches; l; l = l->next) {
+ struct btd_disconnect_data *data = l->data;
+
+ if (data->id == id) {
+ device->watches = g_slist_remove(device->watches,
+ data);
+ if (data->destroy)
+ data->destroy(data->user_data);
+ g_free(data);
+ return;
+ }
+ }
+}
+
+struct btd_device *device_create(DBusConnection *conn,
+ struct btd_adapter *adapter,
+ const gchar *address, device_type_t type)
+{
+ gchar *address_up;
+ struct btd_device *device;
+ const gchar *adapter_path = adapter_get_path(adapter);
+ bdaddr_t src;
+ char srcaddr[18], alias[MAX_NAME_LENGTH + 1];
+
+ device = g_try_malloc0(sizeof(struct btd_device));
+ if (device == NULL)
+ return NULL;
+
+ address_up = g_ascii_strup(address, -1);
+ device->path = g_strdup_printf("%s/dev_%s", adapter_path, address_up);
+ g_strdelimit(device->path, ":", '_');
+ g_free(address_up);
+
+ DBG("Creating device %s", device->path);
+
+ if (g_dbus_register_interface(conn, device->path, DEVICE_INTERFACE,
+ device_methods, device_signals, NULL,
+ device, device_free) == FALSE) {
+ device_free(device);
+ return NULL;
+ }
+
+ str2ba(address, &device->bdaddr);
+ device->adapter = adapter;
+ device->type = type;
+ adapter_get_address(adapter, &src);
+ ba2str(&src, srcaddr);
+ read_device_name(srcaddr, address, device->name);
+ if (read_device_alias(srcaddr, address, alias, sizeof(alias)) == 0)
+ device->alias = g_strdup(alias);
+ device->trusted = read_trust(&src, address, GLOBAL_TRUST);
+
+ if (read_blocked(&src, &device->bdaddr))
+ device_block(conn, device);
+
+ if (read_link_key(&src, &device->bdaddr, NULL, NULL) == 0)
+ device->paired = TRUE;
+
+ return btd_device_ref(device);
+}
+
+void device_set_name(struct btd_device *device, const char *name)
+{
+ DBusConnection *conn = get_dbus_connection();
+
+ if (strncmp(name, device->name, MAX_NAME_LENGTH) == 0)
+ return;
+
+ strncpy(device->name, name, MAX_NAME_LENGTH);
+
+ emit_property_changed(conn, device->path,
+ DEVICE_INTERFACE, "Name",
+ DBUS_TYPE_STRING, &name);
+
+ if (device->alias != NULL)
+ return;
+
+ emit_property_changed(conn, device->path,
+ DEVICE_INTERFACE, "Alias",
+ DBUS_TYPE_STRING, &name);
+}
+
+void device_get_name(struct btd_device *device, char *name, size_t len)
+{
+ strncpy(name, device->name, len);
+}
+
+device_type_t device_get_type(struct btd_device *device)
+{
+ return device->type;
+}
+
+void device_remove_bonding(struct btd_device *device)
+{
+ char filename[PATH_MAX + 1];
+ char srcaddr[18], dstaddr[18];
+ bdaddr_t bdaddr;
+
+ adapter_get_address(device->adapter, &bdaddr);
+ ba2str(&bdaddr, srcaddr);
+ ba2str(&device->bdaddr, dstaddr);
+
+ create_name(filename, PATH_MAX, STORAGEDIR, srcaddr,
+ "linkkeys");
+
+ /* Delete the link key from storage */
+ textfile_casedel(filename, dstaddr);
+
+ btd_adapter_remove_bonding(device->adapter, &device->bdaddr);
+}
+
+static void device_remove_stored(struct btd_device *device)
+{
+ bdaddr_t src;
+ char addr[18];
+ DBusConnection *conn = get_dbus_connection();
+
+ adapter_get_address(device->adapter, &src);
+ ba2str(&device->bdaddr, addr);
+
+ if (device->paired)
+ device_remove_bonding(device);
+ delete_entry(&src, "profiles", addr);
+ delete_entry(&src, "trusts", addr);
+ delete_entry(&src, "types", addr);
+ delete_entry(&src, "primary", addr);
+ delete_all_records(&src, &device->bdaddr);
+ delete_device_service(&src, &device->bdaddr);
+
+ if (device->blocked)
+ device_unblock(conn, device, TRUE);
+}
+
+void device_remove(struct btd_device *device, gboolean remove_stored)
+{
+
+ DBG("Removing device %s", device->path);
+
+ if (device->agent)
+ agent_free(device->agent);
+
+ if (device->bonding) {
+ uint8_t status;
+
+ if (device->connected)
+ status = HCI_OE_USER_ENDED_CONNECTION;
+ else
+ status = HCI_PAGE_TIMEOUT;
+
+ device_cancel_bonding(device, status);
+ }
+
+ if (device->browse) {
+ discover_services_reply(device->browse, -ECANCELED, NULL);
+ browse_request_cancel(device->browse);
+ }
+
+ if (device->connected)
+ do_disconnect(device);
+
+ if (remove_stored)
+ device_remove_stored(device);
+
+ g_slist_foreach(device->drivers, (GFunc) driver_remove, device);
+ g_slist_free(device->drivers);
+ device->drivers = NULL;
+
+ btd_device_unref(device);
+}
+
+gint device_address_cmp(struct btd_device *device, const gchar *address)
+{
+ char addr[18];
+
+ ba2str(&device->bdaddr, addr);
+ return strcasecmp(addr, address);
+}
+
+static gboolean record_has_uuid(const sdp_record_t *rec,
+ const char *profile_uuid)
+{
+ sdp_list_t *pat;
+
+ for (pat = rec->pattern; pat != NULL; pat = pat->next) {
+ char *uuid;
+ int ret;
+
+ uuid = bt_uuid2string(pat->data);
+ if (!uuid)
+ continue;
+
+ ret = strcasecmp(uuid, profile_uuid);
+
+ g_free(uuid);
+
+ if (ret == 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static GSList *device_match_pattern(struct btd_device *device,
+ const char *match_uuid,
+ GSList *profiles)
+{
+ GSList *l, *uuids = NULL;
+
+ for (l = profiles; l; l = l->next) {
+ char *profile_uuid = l->data;
+ const sdp_record_t *rec;
+
+ rec = btd_device_get_record(device, profile_uuid);
+ if (!rec)
+ continue;
+
+ if (record_has_uuid(rec, match_uuid))
+ uuids = g_slist_append(uuids, profile_uuid);
+ }
+
+ return uuids;
+}
+
+static GSList *device_match_driver(struct btd_device *device,
+ struct btd_device_driver *driver,
+ GSList *profiles)
+{
+ const char **uuid;
+ GSList *uuids = NULL;
+
+ for (uuid = driver->uuids; *uuid; uuid++) {
+ GSList *match;
+
+ /* skip duplicated uuids */
+ if (g_slist_find_custom(uuids, *uuid,
+ (GCompareFunc) strcasecmp))
+ continue;
+
+ /* match profile driver */
+ match = g_slist_find_custom(profiles, *uuid,
+ (GCompareFunc) strcasecmp);
+ if (match) {
+ uuids = g_slist_append(uuids, match->data);
+ continue;
+ }
+
+ /* match pattern driver */
+ match = device_match_pattern(device, *uuid, profiles);
+ uuids = g_slist_concat(uuids, match);
+ }
+
+ return uuids;
+}
+
+void device_probe_drivers(struct btd_device *device, GSList *profiles)
+{
+ GSList *list;
+ char addr[18];
+ int err;
+
+ ba2str(&device->bdaddr, addr);
+
+ if (device->blocked) {
+ DBG("Skipping drivers for blocked device %s", addr);
+ goto add_uuids;
+ }
+
+ DBG("Probing drivers for %s", addr);
+
+ for (list = device_drivers; list; list = list->next) {
+ struct btd_device_driver *driver = list->data;
+ GSList *probe_uuids;
+
+ probe_uuids = device_match_driver(device, driver, profiles);
+
+ if (!probe_uuids)
+ continue;
+
+ err = driver->probe(device, probe_uuids);
+ if (err < 0) {
+ error("%s driver probe failed for device %s",
+ driver->name, addr);
+ g_slist_free(probe_uuids);
+ continue;
+ }
+
+ device->drivers = g_slist_append(device->drivers, driver);
+ g_slist_free(probe_uuids);
+ }
+
+add_uuids:
+ for (list = profiles; list; list = list->next) {
+ GSList *l = g_slist_find_custom(device->uuids, list->data,
+ (GCompareFunc) strcasecmp);
+ if (l)
+ continue;
+
+ device->uuids = g_slist_insert_sorted(device->uuids,
+ g_strdup(list->data),
+ (GCompareFunc) strcasecmp);
+ }
+}
+
+static void device_remove_drivers(struct btd_device *device, GSList *uuids)
+{
+ struct btd_adapter *adapter = device_get_adapter(device);
+ GSList *list, *next;
+ char srcaddr[18], dstaddr[18];
+ bdaddr_t src;
+ sdp_list_t *records;
+
+ adapter_get_address(adapter, &src);
+ ba2str(&src, srcaddr);
+ ba2str(&device->bdaddr, dstaddr);
+
+ records = read_records(&src, &device->bdaddr);
+
+ DBG("Removing drivers for %s", dstaddr);
+
+ for (list = device->drivers; list; list = next) {
+ struct btd_device_driver *driver = list->data;
+ const char **uuid;
+
+ next = list->next;
+
+ for (uuid = driver->uuids; *uuid; uuid++) {
+ if (!g_slist_find_custom(uuids, *uuid,
+ (GCompareFunc) strcasecmp))
+ continue;
+
+ DBG("UUID %s was removed from device %s",
+ *uuid, dstaddr);
+
+ driver->remove(device);
+ device->drivers = g_slist_remove(device->drivers,
+ driver);
+ break;
+ }
+ }
+
+ for (list = uuids; list; list = list->next) {
+ sdp_record_t *rec;
+
+ device->uuids = g_slist_remove(device->uuids, list->data);
+
+ rec = find_record_in_list(records, list->data);
+ if (!rec)
+ continue;
+
+ delete_record(srcaddr, dstaddr, rec->handle);
+
+ records = sdp_list_remove(records, rec);
+ sdp_record_free(rec);
+
+ }
+
+ if (records)
+ sdp_list_free(records, (sdp_free_func_t) sdp_record_free);
+}
+
+static void services_changed(struct btd_device *device)
+{
+ DBusConnection *conn = get_dbus_connection();
+ char **uuids;
+ GSList *l;
+ int i;
+
+ uuids = g_new0(char *, g_slist_length(device->uuids) + 1);
+ for (i = 0, l = device->uuids; l; l = l->next, i++)
+ uuids[i] = l->data;
+
+ emit_array_property_changed(conn, device->path, DEVICE_INTERFACE,
+ "UUIDs", DBUS_TYPE_STRING, &uuids, i);
+
+ g_free(uuids);
+}
+
+static int rec_cmp(const void *a, const void *b)
+{
+ const sdp_record_t *r1 = a;
+ const sdp_record_t *r2 = b;
+
+ return r1->handle - r2->handle;
+}
+
+static void update_services(struct browse_req *req, sdp_list_t *recs)
+{
+ struct btd_device *device = req->device;
+ struct btd_adapter *adapter = device_get_adapter(device);
+ sdp_list_t *seq;
+ char srcaddr[18], dstaddr[18];
+ bdaddr_t src;
+
+ adapter_get_address(adapter, &src);
+ ba2str(&src, srcaddr);
+ ba2str(&device->bdaddr, dstaddr);
+
+ for (seq = recs; seq; seq = seq->next) {
+ sdp_record_t *rec = (sdp_record_t *) seq->data;
+ sdp_list_t *svcclass = NULL;
+ gchar *profile_uuid;
+ GSList *l;
+
+ if (!rec)
+ break;
+
+ if (sdp_get_service_classes(rec, &svcclass) < 0)
+ continue;
+
+ /* Check for empty service classes list */
+ if (svcclass == NULL) {
+ DBG("Skipping record with no service classes");
+ continue;
+ }
+
+ /* Extract the first element and skip the remainning */
+ profile_uuid = bt_uuid2string(svcclass->data);
+ if (!profile_uuid) {
+ sdp_list_free(svcclass, free);
+ continue;
+ }
+
+ if (!strcasecmp(profile_uuid, PNP_UUID)) {
+ uint16_t source, vendor, product, version;
+ sdp_data_t *pdlist;
+
+ pdlist = sdp_data_get(rec, SDP_ATTR_VENDOR_ID_SOURCE);
+ source = pdlist ? pdlist->val.uint16 : 0x0000;
+
+ pdlist = sdp_data_get(rec, SDP_ATTR_VENDOR_ID);
+ vendor = pdlist ? pdlist->val.uint16 : 0x0000;
+
+ pdlist = sdp_data_get(rec, SDP_ATTR_PRODUCT_ID);
+ product = pdlist ? pdlist->val.uint16 : 0x0000;
+
+ pdlist = sdp_data_get(rec, SDP_ATTR_VERSION);
+ version = pdlist ? pdlist->val.uint16 : 0x0000;
+
+ if (source || vendor || product || version)
+ store_device_id(srcaddr, dstaddr, source,
+ vendor, product, version);
+ }
+
+ /* Check for duplicates */
+ if (sdp_list_find(req->records, rec, rec_cmp)) {
+ g_free(profile_uuid);
+ sdp_list_free(svcclass, free);
+ continue;
+ }
+
+ store_record(srcaddr, dstaddr, rec);
+
+ /* Copy record */
+ req->records = sdp_list_append(req->records,
+ sdp_copy_record(rec));
+
+ l = g_slist_find_custom(device->uuids, profile_uuid,
+ (GCompareFunc) strcmp);
+ if (!l)
+ req->profiles_added =
+ g_slist_append(req->profiles_added,
+ profile_uuid);
+ else {
+ req->profiles_removed =
+ g_slist_remove(req->profiles_removed,
+ l->data);
+ g_free(profile_uuid);
+ }
+
+ sdp_list_free(svcclass, free);
+ }
+}
+
+static void store_profiles(struct btd_device *device)
+{
+ struct btd_adapter *adapter = device->adapter;
+ bdaddr_t src;
+ char *str;
+
+ adapter_get_address(adapter, &src);
+
+ if (!device->uuids) {
+ write_device_profiles(&src, &device->bdaddr, "");
+ return;
+ }
+
+ str = bt_list2string(device->uuids);
+ write_device_profiles(&src, &device->bdaddr, str);
+ g_free(str);
+}
+
+static void create_device_reply(struct btd_device *device, struct browse_req *req)
+{
+ DBusMessage *reply;
+
+ reply = dbus_message_new_method_return(req->msg);
+ if (!reply)
+ return;
+
+ dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &device->path,
+ DBUS_TYPE_INVALID);
+
+ g_dbus_send_message(req->conn, reply);
+}
+
+static void search_cb(sdp_list_t *recs, int err, gpointer user_data)
+{
+ struct browse_req *req = user_data;
+ struct btd_device *device = req->device;
+ char addr[18];
+
+ ba2str(&device->bdaddr, addr);
+
+ if (err < 0) {
+ error("%s: error updating services: %s (%d)",
+ addr, strerror(-err), -err);
+ goto send_reply;
+ }
+
+ update_services(req, recs);
+
+ if (device->tmp_records)
+ sdp_list_free(device->tmp_records,
+ (sdp_free_func_t) sdp_record_free);
+
+ device->tmp_records = req->records;
+ req->records = NULL;
+
+ if (!req->profiles_added && !req->profiles_removed) {
+ DBG("%s: No service update", addr);
+ goto send_reply;
+ }
+
+ /* Probe matching drivers for services added */
+ if (req->profiles_added)
+ device_probe_drivers(device, req->profiles_added);
+
+ /* Remove drivers for services removed */
+ if (req->profiles_removed)
+ device_remove_drivers(device, req->profiles_removed);
+
+ /* Propagate services changes */
+ services_changed(req->device);
+
+send_reply:
+ if (!req->msg)
+ goto cleanup;
+
+ if (dbus_message_is_method_call(req->msg, DEVICE_INTERFACE,
+ "DiscoverServices"))
+ discover_services_reply(req, err, device->tmp_records);
+ else if (dbus_message_is_method_call(req->msg, ADAPTER_INTERFACE,
+ "CreatePairedDevice"))
+ create_device_reply(device, req);
+ else if (dbus_message_is_method_call(req->msg, ADAPTER_INTERFACE,
+ "CreateDevice")) {
+ if (err < 0) {
+ DBusMessage *reply;
+ reply = btd_error_failed(req->msg, strerror(-err));
+ g_dbus_send_message(req->conn, reply);
+ goto cleanup;
+ }
+
+ create_device_reply(device, req);
+ device_set_temporary(device, FALSE);
+ }
+
+cleanup:
+ if (!device->temporary) {
+ bdaddr_t sba, dba;
+
+ adapter_get_address(device->adapter, &sba);
+ device_get_address(device, &dba);
+
+ store_profiles(device);
+ write_device_type(&sba, &dba, device->type);
+ }
+
+ device->browse = NULL;
+ browse_request_free(req);
+}
+
+static void browse_cb(sdp_list_t *recs, int err, gpointer user_data)
+{
+ struct browse_req *req = user_data;
+ struct btd_device *device = req->device;
+ struct btd_adapter *adapter = device->adapter;
+ bdaddr_t src;
+ uuid_t uuid;
+
+ /* If we have a valid response and req->search_uuid == 2, then L2CAP
+ * UUID & PNP searching was successful -- we are done */
+ if (err < 0 || (req->search_uuid == 2 && req->records)) {
+ if (err == -ECONNRESET && req->reconnect_attempt < 1) {
+ req->search_uuid--;
+ req->reconnect_attempt++;
+ } else
+ goto done;
+ }
+
+ update_services(req, recs);
+
+ adapter_get_address(adapter, &src);
+
+ /* Search for mandatory uuids */
+ if (uuid_list[req->search_uuid]) {
+ sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]);
+ bt_search_service(&src, &device->bdaddr, &uuid,
+ browse_cb, user_data, NULL);
+ return;
+ }
+
+done:
+ search_cb(recs, err, user_data);
+}
+
+static void init_browse(struct browse_req *req, gboolean reverse)
+{
+ GSList *l;
+
+ /* If we are doing reverse-SDP don't try to detect removed profiles
+ * since some devices hide their service records while they are
+ * connected
+ */
+ if (reverse)
+ return;
+
+ for (l = req->device->uuids; l; l = l->next)
+ req->profiles_removed = g_slist_append(req->profiles_removed,
+ l->data);
+}
+
+static char *primary_list_to_string(GSList *primary_list)
+{
+ GString *services;
+ GSList *l;
+
+ services = g_string_new(NULL);
+
+ for (l = primary_list; l; l = l->next) {
+ struct att_primary *primary = l->data;
+ char service[64];
+
+ memset(service, 0, sizeof(service));
+
+ snprintf(service, sizeof(service), "%04X#%04X#%s ",
+ primary->start, primary->end, primary->uuid);
+
+ services = g_string_append(services, service);
+ }
+
+ return g_string_free(services, FALSE);
+}
+
+static void primary_cb(GSList *services, guint8 status, gpointer user_data)
+{
+ struct browse_req *req = user_data;
+ struct btd_device *device = req->device;
+ struct btd_adapter *adapter = device->adapter;
+ GSList *l, *uuids = NULL;
+ bdaddr_t dba, sba;
+ char *str;
+
+ if (status) {
+ DBusMessage *reply;
+ reply = btd_error_failed(req->msg, att_ecode2str(status));
+ g_dbus_send_message(req->conn, reply);
+ goto done;
+ }
+
+ services_changed(device);
+ device_set_temporary(device, FALSE);
+
+ for (l = services; l; l = l->next) {
+ struct att_primary *prim = l->data;
+ uuids = g_slist_append(uuids, prim->uuid);
+ device_add_primary(device, prim);
+ }
+
+ device_probe_drivers(device, uuids);
+ g_slist_free(uuids);
+
+ create_device_reply(device, req);
+
+ str = primary_list_to_string(services);
+
+ adapter_get_address(adapter, &sba);
+ device_get_address(device, &dba);
+
+ write_device_type(&sba, &dba, device->type);
+ write_device_services(&sba, &dba, str);
+ g_free(str);
+
+done:
+ device->browse = NULL;
+ browse_request_free(req);
+}
+
+static void gatt_connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
+{
+ struct browse_req *req = user_data;
+ struct btd_device *device = req->device;
+
+ if (gerr) {
+ DBusMessage *reply;
+
+ DBG("%s", gerr->message);
+
+ reply = btd_error_failed(req->msg, gerr->message);
+ g_dbus_send_message(req->conn, reply);
+
+ device->browse = NULL;
+ browse_request_free(req);
+
+ return;
+ }
+
+ req->attrib = g_attrib_new(io);
+ gatt_discover_primary(req->attrib, NULL, primary_cb, req);
+}
+
+int device_browse_primary(struct btd_device *device, DBusConnection *conn,
+ DBusMessage *msg, gboolean secure)
+{
+ struct btd_adapter *adapter = device->adapter;
+ struct browse_req *req;
+ BtIOSecLevel sec_level;
+ bdaddr_t src;
+
+ if (device->browse)
+ return -EBUSY;
+
+ req = g_new0(struct browse_req, 1);
+ req->device = btd_device_ref(device);
+
+ adapter_get_address(adapter, &src);
+
+ sec_level = secure ? BT_IO_SEC_HIGH : BT_IO_SEC_LOW;
+
+ req->io = bt_io_connect(BT_IO_L2CAP, gatt_connect_cb, req, NULL, NULL,
+ BT_IO_OPT_SOURCE_BDADDR, &src,
+ BT_IO_OPT_DEST_BDADDR, &device->bdaddr,
+ BT_IO_OPT_CID, GATT_CID,
+ BT_IO_OPT_SEC_LEVEL, sec_level,
+ BT_IO_OPT_INVALID);
+
+ if (req->io == NULL ) {
+ browse_request_free(req);
+ return -EIO;
+ }
+
+ if (conn == NULL)
+ conn = get_dbus_connection();
+
+ req->conn = dbus_connection_ref(conn);
+ device->browse = req;
+
+ if (msg) {
+ const char *sender = dbus_message_get_sender(msg);
+
+ req->msg = dbus_message_ref(msg);
+ /* Track the request owner to cancel it
+ * automatically if the owner exits */
+ req->listener_id = g_dbus_add_disconnect_watch(conn,
+ sender,
+ discover_services_req_exit,
+ req, NULL);
+ }
+
+ return 0;
+}
+
+int device_browse_sdp(struct btd_device *device, DBusConnection *conn,
+ DBusMessage *msg, uuid_t *search, gboolean reverse)
+{
+ struct btd_adapter *adapter = device->adapter;
+ struct browse_req *req;
+ bt_callback_t cb;
+ bdaddr_t src;
+ uuid_t uuid;
+ int err;
+
+ if (device->browse)
+ return -EBUSY;
+
+ adapter_get_address(adapter, &src);
+
+ req = g_new0(struct browse_req, 1);
+ req->device = btd_device_ref(device);
+ if (search) {
+ memcpy(&uuid, search, sizeof(uuid_t));
+ cb = search_cb;
+ } else {
+ sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]);
+ init_browse(req, reverse);
+ cb = browse_cb;
+ }
+
+ err = bt_search_service(&src, &device->bdaddr, &uuid, cb, req, NULL);
+ if (err < 0) {
+ browse_request_free(req);
+ return err;
+ }
+
+ if (conn == NULL)
+ conn = get_dbus_connection();
+
+ req->conn = dbus_connection_ref(conn);
+ device->browse = req;
+
+ if (msg) {
+ const char *sender = dbus_message_get_sender(msg);
+
+ req->msg = dbus_message_ref(msg);
+ /* Track the request owner to cancel it
+ * automatically if the owner exits */
+ req->listener_id = g_dbus_add_disconnect_watch(conn,
+ sender,
+ discover_services_req_exit,
+ req, NULL);
+ }
+
+ return err;
+}
+
+struct btd_adapter *device_get_adapter(struct btd_device *device)
+{
+ if (!device)
+ return NULL;
+
+ return device->adapter;
+}
+
+void device_get_address(struct btd_device *device, bdaddr_t *bdaddr)
+{
+ bacpy(bdaddr, &device->bdaddr);
+}
+
+const gchar *device_get_path(struct btd_device *device)
+{
+ if (!device)
+ return NULL;
+
+ return device->path;
+}
+
+struct agent *device_get_agent(struct btd_device *device)
+{
+ if (!device)
+ return NULL;
+
+ if (device->agent)
+ return device->agent;
+
+ return adapter_get_agent(device->adapter);
+}
+
+gboolean device_is_busy(struct btd_device *device)
+{
+ return device->browse ? TRUE : FALSE;
+}
+
+gboolean device_is_temporary(struct btd_device *device)
+{
+ return device->temporary;
+}
+
+void device_set_temporary(struct btd_device *device, gboolean temporary)
+{
+ if (!device)
+ return;
+
+ DBG("temporary %d", temporary);
+
+ device->temporary = temporary;
+}
+
+void device_set_type(struct btd_device *device, device_type_t type)
+{
+ if (!device)
+ return;
+
+ device->type = type;
+}
+
+static gboolean start_discovery(gpointer user_data)
+{
+ struct btd_device *device = user_data;
+
+ device_browse_sdp(device, NULL, NULL, NULL, TRUE);
+
+ device->discov_timer = 0;
+
+ return FALSE;
+}
+
+static DBusMessage *new_authentication_return(DBusMessage *msg, int status)
+{
+ switch (status) {
+ case 0x00: /* success */
+ return dbus_message_new_method_return(msg);
+
+ case 0x04: /* page timeout */
+ return dbus_message_new_error(msg,
+ ERROR_INTERFACE ".ConnectionAttemptFailed",
+ "Page Timeout");
+ case 0x08: /* connection timeout */
+ return dbus_message_new_error(msg,
+ ERROR_INTERFACE ".ConnectionAttemptFailed",
+ "Connection Timeout");
+ case 0x10: /* connection accept timeout */
+ case 0x22: /* LMP response timeout */
+ case 0x28: /* instant passed - is this a timeout? */
+ return dbus_message_new_error(msg,
+ ERROR_INTERFACE ".AuthenticationTimeout",
+ "Authentication Timeout");
+ case 0x17: /* too frequent pairing attempts */
+ return dbus_message_new_error(msg,
+ ERROR_INTERFACE ".RepeatedAttempts",
+ "Repeated Attempts");
+
+ case 0x06:
+ case 0x18: /* pairing not allowed (e.g. gw rejected attempt) */
+ return dbus_message_new_error(msg,
+ ERROR_INTERFACE ".AuthenticationRejected",
+ "Authentication Rejected");
+
+ case 0x07: /* memory capacity */
+ case 0x09: /* connection limit */
+ case 0x0a: /* synchronous connection limit */
+ case 0x0d: /* limited resources */
+ case 0x13: /* user ended the connection */
+ case 0x14: /* terminated due to low resources */
+ case 0x16: /* connection terminated */
+ return dbus_message_new_error(msg,
+ ERROR_INTERFACE ".AuthenticationCanceled",
+ "Authentication Canceled");
+
+ case 0x05: /* authentication failure */
+ case 0x0E: /* rejected due to security reasons - is this auth failure? */
+ case 0x25: /* encryption mode not acceptable - is this auth failure? */
+ case 0x26: /* link key cannot be changed - is this auth failure? */
+ case 0x29: /* pairing with unit key unsupported - is this auth failure? */
+ case 0x2f: /* insufficient security - is this auth failure? */
+ default:
+ return dbus_message_new_error(msg,
+ ERROR_INTERFACE ".AuthenticationFailed",
+ "Authentication Failed");
+ }
+}
+
+static void bonding_request_free(struct bonding_req *bonding)
+{
+ struct btd_device *device;
+
+ if (!bonding)
+ return;
+
+ if (bonding->listener_id)
+ g_dbus_remove_watch(bonding->conn, bonding->listener_id);
+
+ if (bonding->msg)
+ dbus_message_unref(bonding->msg);
+
+ if (bonding->conn)
+ dbus_connection_unref(bonding->conn);
+
+ if (bonding->io)
+ g_io_channel_unref(bonding->io);
+
+ device = bonding->device;
+ g_free(bonding);
+
+ if (!device)
+ return;
+
+ device->bonding = NULL;
+
+ adapter_resume_discovery(device->adapter);
+
+ if (!device->agent)
+ return;
+
+ agent_cancel(device->agent);
+ agent_free(device->agent);
+ device->agent = NULL;
+}
+
+void device_set_paired(struct btd_device *device, gboolean value)
+{
+ DBusConnection *conn = get_dbus_connection();
+
+ if (device->paired == value)
+ return;
+
+ device->paired = value;
+
+ emit_property_changed(conn, device->path, DEVICE_INTERFACE, "Paired",
+ DBUS_TYPE_BOOLEAN, &value);
+}
+
+static void device_agent_removed(struct agent *agent, void *user_data)
+{
+ struct btd_device *device = user_data;
+
+ device->agent = NULL;
+
+ if (device->authr)
+ device->authr->agent = NULL;
+}
+
+static struct bonding_req *bonding_request_new(DBusConnection *conn,
+ DBusMessage *msg,
+ struct btd_device *device,
+ const char *agent_path,
+ uint8_t capability)
+{
+ struct bonding_req *bonding;
+ const char *name = dbus_message_get_sender(msg);
+ struct agent *agent;
+ char addr[18];
+
+ ba2str(&device->bdaddr, addr);
+ DBG("Requesting bonding for %s", addr);
+
+ if (!agent_path)
+ goto proceed;
+
+ agent = agent_create(device->adapter, name, agent_path,
+ capability,
+ device_agent_removed,
+ device);
+ if (!agent) {
+ error("Unable to create a new agent");
+ return NULL;
+ }
+
+ device->agent = agent;
+
+ DBG("Temporary agent registered for %s at %s:%s",
+ addr, name, agent_path);
+
+proceed:
+ bonding = g_new0(struct bonding_req, 1);
+
+ bonding->conn = dbus_connection_ref(conn);
+ bonding->msg = dbus_message_ref(msg);
+
+ adapter_suspend_discovery(device->adapter);
+
+ return bonding;
+}
+
+static void create_bond_req_exit(DBusConnection *conn, void *user_data)
+{
+ struct btd_device *device = user_data;
+ char addr[18];
+
+ ba2str(&device->bdaddr, addr);
+ DBG("%s: requestor exited before bonding was completed", addr);
+
+ if (device->authr)
+ device_cancel_authentication(device, FALSE);
+
+ if (device->bonding) {
+ device->bonding->listener_id = 0;
+ device_request_disconnect(device, NULL);
+ }
+}
+
+DBusMessage *device_create_bonding(struct btd_device *device,
+ DBusConnection *conn,
+ DBusMessage *msg,
+ const char *agent_path,
+ uint8_t capability)
+{
+ char filename[PATH_MAX + 1];
+ char *str, srcaddr[18], dstaddr[18];
+ struct btd_adapter *adapter = device->adapter;
+ struct bonding_req *bonding;
+ bdaddr_t src;
+ int err;
+
+ adapter_get_address(adapter, &src);
+ ba2str(&src, srcaddr);
+ ba2str(&device->bdaddr, dstaddr);
+
+ if (device->bonding)
+ return btd_error_in_progress(msg);
+
+ /* check if a link key already exists */
+ create_name(filename, PATH_MAX, STORAGEDIR, srcaddr,
+ "linkkeys");
+
+ str = textfile_caseget(filename, dstaddr);
+ if (str) {
+ free(str);
+ return btd_error_already_exists(msg);
+ }
+
+ err = adapter_create_bonding(adapter, &device->bdaddr, capability);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+ bonding = bonding_request_new(conn, msg, device, agent_path,
+ capability);
+ if (!bonding) {
+ adapter_cancel_bonding(adapter, &device->bdaddr);
+ return NULL;
+ }
+
+ bonding->listener_id = g_dbus_add_disconnect_watch(conn,
+ dbus_message_get_sender(msg),
+ create_bond_req_exit, device,
+ NULL);
+
+ device->bonding = bonding;
+ bonding->device = device;
+
+ return NULL;
+}
+
+void device_simple_pairing_complete(struct btd_device *device, uint8_t status)
+{
+ struct authentication_req *auth = device->authr;
+
+ if (auth && auth->type == AUTH_TYPE_NOTIFY && auth->agent)
+ agent_cancel(auth->agent);
+}
+
+static void device_auth_req_free(struct btd_device *device)
+{
+ g_free(device->authr);
+ device->authr = NULL;
+}
+
+void device_bonding_complete(struct btd_device *device, uint8_t status)
+{
+ struct bonding_req *bonding = device->bonding;
+ struct authentication_req *auth = device->authr;
+
+ DBG("bonding %p status 0x%02x", bonding, status);
+
+ if (auth && auth->type == AUTH_TYPE_NOTIFY && auth->agent)
+ agent_cancel(auth->agent);
+
+ if (status) {
+ device_cancel_authentication(device, TRUE);
+ device_cancel_bonding(device, status);
+ return;
+ }
+
+ device_auth_req_free(device);
+
+ /* If we're already paired nothing more is needed */
+ if (device->paired)
+ return;
+
+ device_set_paired(device, TRUE);
+
+ /* If we were initiators start service discovery immediately.
+ * However if the other end was the initator wait a few seconds
+ * before SDP. This is due to potential IOP issues if the other
+ * end starts doing SDP at the same time as us */
+ if (bonding) {
+ DBG("Proceeding with service discovery");
+ /* If we are initiators remove any discovery timer and just
+ * start discovering services directly */
+ if (device->discov_timer) {
+ g_source_remove(device->discov_timer);
+ device->discov_timer = 0;
+ }
+
+ device_browse_sdp(device, bonding->conn, bonding->msg,
+ NULL, FALSE);
+
+ bonding_request_free(bonding);
+ } else {
+ if (!device->browse && !device->discov_timer &&
+ main_opts.reverse_sdp) {
+ /* If we are not initiators and there is no currently
+ * active discovery or discovery timer, set discovery
+ * timer */
+ DBG("setting timer for reverse service discovery");
+ device->discov_timer = g_timeout_add_seconds(
+ DISCOVERY_TIMER,
+ start_discovery,
+ device);
+ }
+ }
+}
+
+gboolean device_is_creating(struct btd_device *device, const char *sender)
+{
+ DBusMessage *msg;
+
+ if (device->bonding && device->bonding->msg)
+ msg = device->bonding->msg;
+ else if (device->browse && device->browse->msg)
+ msg = device->browse->msg;
+ else
+ return FALSE;
+
+ if (!dbus_message_is_method_call(msg, ADAPTER_INTERFACE,
+ "CreatePairedDevice") &&
+ !dbus_message_is_method_call(msg, ADAPTER_INTERFACE,
+ "CreateDevice"))
+ return FALSE;
+
+ if (sender == NULL)
+ return TRUE;
+
+ return g_str_equal(sender, dbus_message_get_sender(msg));
+}
+
+gboolean device_is_bonding(struct btd_device *device, const char *sender)
+{
+ struct bonding_req *bonding = device->bonding;
+
+ if (!device->bonding)
+ return FALSE;
+
+ if (!sender)
+ return TRUE;
+
+ return g_str_equal(sender, dbus_message_get_sender(bonding->msg));
+}
+
+void device_cancel_bonding(struct btd_device *device, uint8_t status)
+{
+ struct bonding_req *bonding = device->bonding;
+ DBusMessage *reply;
+ char addr[18];
+
+ if (!bonding)
+ return;
+
+ ba2str(&device->bdaddr, addr);
+ DBG("Canceling bonding request for %s", addr);
+
+ if (device->authr)
+ device_cancel_authentication(device, FALSE);
+
+ reply = new_authentication_return(bonding->msg, status);
+ g_dbus_send_message(bonding->conn, reply);
+
+ bonding_request_cancel(bonding);
+ bonding_request_free(bonding);
+}
+
+static void pincode_cb(struct agent *agent, DBusError *err,
+ const char *pincode, void *data)
+{
+ struct authentication_req *auth = data;
+ struct btd_device *device = auth->device;
+
+ /* No need to reply anything if the authentication already failed */
+ if (auth->cb == NULL)
+ return;
+
+ ((agent_pincode_cb) auth->cb)(agent, err, pincode, device);
+
+ device->authr->cb = NULL;
+ device->authr->agent = NULL;
+}
+
+static void confirm_cb(struct agent *agent, DBusError *err, void *data)
+{
+ struct authentication_req *auth = data;
+ struct btd_device *device = auth->device;
+
+ /* No need to reply anything if the authentication already failed */
+ if (auth->cb == NULL)
+ return;
+
+ ((agent_cb) auth->cb)(agent, err, device);
+
+ device->authr->cb = NULL;
+ device->authr->agent = NULL;
+}
+
+static void passkey_cb(struct agent *agent, DBusError *err,
+ uint32_t passkey, void *data)
+{
+ struct authentication_req *auth = data;
+ struct btd_device *device = auth->device;
+
+ /* No need to reply anything if the authentication already failed */
+ if (auth->cb == NULL)
+ return;
+
+ ((agent_passkey_cb) auth->cb)(agent, err, passkey, device);
+
+ device->authr->cb = NULL;
+ device->authr->agent = NULL;
+}
+
+int device_request_authentication(struct btd_device *device, auth_type_t type,
+ uint32_t passkey, void *cb)
+{
+ struct authentication_req *auth;
+ struct agent *agent;
+ char addr[18];
+ int err;
+
+ ba2str(&device->bdaddr, addr);
+ DBG("Requesting agent authentication for %s", addr);
+
+ if (device->authr) {
+ error("Authentication already requested for %s", addr);
+ return -EALREADY;
+ }
+
+ agent = device_get_agent(device);
+ if (!agent) {
+ error("No agent available for request type %d", type);
+ return -EPERM;
+ }
+
+ auth = g_new0(struct authentication_req, 1);
+ auth->agent = agent;
+ auth->device = device;
+ auth->cb = cb;
+ auth->type = type;
+ device->authr = auth;
+
+ switch (type) {
+ case AUTH_TYPE_PINCODE:
+ err = agent_request_pincode(agent, device, pincode_cb,
+ auth, NULL);
+ break;
+ case AUTH_TYPE_PASSKEY:
+ err = agent_request_passkey(agent, device, passkey_cb,
+ auth, NULL);
+ break;
+ case AUTH_TYPE_CONFIRM:
+ err = agent_request_confirmation(agent, device, passkey,
+ confirm_cb, auth, NULL);
+ break;
+ case AUTH_TYPE_NOTIFY:
+ err = agent_display_passkey(agent, device, passkey);
+ break;
+ default:
+ err = -EINVAL;
+ }
+
+ if (err < 0) {
+ error("Failed requesting authentication");
+ device_auth_req_free(device);
+ }
+
+ return err;
+}
+
+static void cancel_authentication(struct authentication_req *auth)
+{
+ struct btd_device *device;
+ struct agent *agent;
+ DBusError err;
+
+ if (!auth || !auth->cb)
+ return;
+
+ device = auth->device;
+ agent = auth->agent;
+
+ dbus_error_init(&err);
+ dbus_set_error_const(&err, "org.bluez.Error.Canceled", NULL);
+
+ switch (auth->type) {
+ case AUTH_TYPE_PINCODE:
+ ((agent_pincode_cb) auth->cb)(agent, &err, NULL, device);
+ break;
+ case AUTH_TYPE_CONFIRM:
+ ((agent_cb) auth->cb)(agent, &err, device);
+ break;
+ case AUTH_TYPE_PASSKEY:
+ ((agent_passkey_cb) auth->cb)(agent, &err, 0, device);
+ break;
+ case AUTH_TYPE_NOTIFY:
+ /* User Notify doesn't require any reply */
+ break;
+ }
+
+ dbus_error_free(&err);
+ auth->cb = NULL;
+}
+
+void device_cancel_authentication(struct btd_device *device, gboolean aborted)
+{
+ struct authentication_req *auth = device->authr;
+ char addr[18];
+
+ if (!auth)
+ return;
+
+ ba2str(&device->bdaddr, addr);
+ DBG("Canceling authentication request for %s", addr);
+
+ if (auth->agent)
+ agent_cancel(auth->agent);
+
+ if (!aborted)
+ cancel_authentication(auth);
+
+ device_auth_req_free(device);
+}
+
+gboolean device_is_authenticating(struct btd_device *device)
+{
+ return (device->authr != NULL);
+}
+
+gboolean device_is_authorizing(struct btd_device *device)
+{
+ return device->authorizing;
+}
+
+void device_set_authorizing(struct btd_device *device, gboolean auth)
+{
+ device->authorizing = auth;
+}
+
+void btd_device_add_service(struct btd_device *device, const char *path)
+{
+ if (g_slist_find_custom(device->services, path, (GCompareFunc) strcmp))
+ return;
+
+ device->services = g_slist_append(device->services, g_strdup(path));
+}
+
+void device_add_primary(struct btd_device *device, struct att_primary *prim)
+{
+ device->primaries = g_slist_append(device->primaries, prim);
+}
+
+GSList *btd_device_get_primaries(struct btd_device *device)
+{
+ return device->primaries;
+}
+
+void btd_device_add_uuid(struct btd_device *device, const char *uuid)
+{
+ GSList *uuid_list;
+ char *new_uuid;
+
+ if (g_slist_find_custom(device->uuids, uuid,
+ (GCompareFunc) strcasecmp))
+ return;
+
+ new_uuid = g_strdup(uuid);
+ uuid_list = g_slist_append(NULL, new_uuid);
+
+ device_probe_drivers(device, uuid_list);
+
+ g_free(new_uuid);
+ g_slist_free(uuid_list);
+
+ store_profiles(device);
+ services_changed(device);
+}
+
+const sdp_record_t *btd_device_get_record(struct btd_device *device,
+ const char *uuid)
+{
+ bdaddr_t src;
+
+ if (device->tmp_records) {
+ const sdp_record_t *record;
+
+ record = find_record_in_list(device->tmp_records, uuid);
+ if (record != NULL)
+ return record;
+ }
+
+ adapter_get_address(device->adapter, &src);
+
+ device->tmp_records = read_records(&src, &device->bdaddr);
+ if (!device->tmp_records)
+ return NULL;
+
+ return find_record_in_list(device->tmp_records, uuid);
+}
+
+int btd_register_device_driver(struct btd_device_driver *driver)
+{
+ device_drivers = g_slist_append(device_drivers, driver);
+
+ return 0;
+}
+
+void btd_unregister_device_driver(struct btd_device_driver *driver)
+{
+ device_drivers = g_slist_remove(device_drivers, driver);
+}
+
+struct btd_device *btd_device_ref(struct btd_device *device)
+{
+ device->ref++;
+
+ DBG("%p: ref=%d", device, device->ref);
+
+ return device;
+}
+
+void btd_device_unref(struct btd_device *device)
+{
+ DBusConnection *conn = get_dbus_connection();
+ gchar *path;
+
+ device->ref--;
+
+ DBG("%p: ref=%d", device, device->ref);
+
+ if (device->ref > 0)
+ return;
+
+ path = g_strdup(device->path);
+
+ g_dbus_unregister_interface(conn, path, DEVICE_INTERFACE);
+
+ g_free(path);
+}
diff --git a/src/device.h b/src/device.h
new file mode 100644
index 0000000..3ce212b
--- /dev/null
+++ b/src/device.h
@@ -0,0 +1,119 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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
+ *
+ */
+
+#define DEVICE_INTERFACE "org.bluez.Device"
+
+struct btd_device;
+struct att_primary;
+
+typedef enum {
+ AUTH_TYPE_PINCODE,
+ AUTH_TYPE_PASSKEY,
+ AUTH_TYPE_CONFIRM,
+ AUTH_TYPE_NOTIFY,
+} auth_type_t;
+
+typedef enum {
+ DEVICE_TYPE_UNKNOWN,
+ DEVICE_TYPE_BREDR,
+ DEVICE_TYPE_LE,
+ DEVICE_TYPE_DUALMODE
+} device_type_t;
+
+struct btd_device *device_create(DBusConnection *conn,
+ struct btd_adapter *adapter,
+ const gchar *address, device_type_t type);
+void device_set_name(struct btd_device *device, const char *name);
+void device_get_name(struct btd_device *device, char *name, size_t len);
+device_type_t device_get_type(struct btd_device *device);
+void device_remove(struct btd_device *device, gboolean remove_stored);
+gint device_address_cmp(struct btd_device *device, const gchar *address);
+int device_browse_primary(struct btd_device *device, DBusConnection *conn,
+ DBusMessage *msg, gboolean secure);
+int device_browse_sdp(struct btd_device *device, DBusConnection *conn,
+ DBusMessage *msg, uuid_t *search, gboolean reverse);
+void device_probe_drivers(struct btd_device *device, GSList *profiles);
+const sdp_record_t *btd_device_get_record(struct btd_device *device,
+ const char *uuid);
+GSList *btd_device_get_primaries(struct btd_device *device);
+void btd_device_add_service(struct btd_device *device, const char *path);
+void device_add_primary(struct btd_device *device, struct att_primary *prim);
+void btd_device_add_uuid(struct btd_device *device, const char *uuid);
+struct btd_adapter *device_get_adapter(struct btd_device *device);
+void device_get_address(struct btd_device *device, bdaddr_t *bdaddr);
+const gchar *device_get_path(struct btd_device *device);
+struct agent *device_get_agent(struct btd_device *device);
+gboolean device_is_busy(struct btd_device *device);
+gboolean device_is_temporary(struct btd_device *device);
+gboolean device_is_paired(struct btd_device *device);
+gboolean device_is_trusted(struct btd_device *device);
+void device_set_paired(struct btd_device *device, gboolean paired);
+void device_set_temporary(struct btd_device *device, gboolean temporary);
+void device_set_cap(struct btd_device *device, uint8_t cap);
+void device_set_type(struct btd_device *device, device_type_t type);
+uint8_t device_get_cap(struct btd_device *device);
+void device_set_auth(struct btd_device *device, uint8_t auth);
+uint8_t device_get_auth(struct btd_device *device);
+gboolean device_is_connected(struct btd_device *device);
+DBusMessage *device_create_bonding(struct btd_device *device,
+ DBusConnection *conn, DBusMessage *msg,
+ const char *agent_path, uint8_t capability);
+void device_remove_bonding(struct btd_device *device);
+void device_bonding_complete(struct btd_device *device, uint8_t status);
+void device_simple_pairing_complete(struct btd_device *device, uint8_t status);
+gboolean device_is_creating(struct btd_device *device, const char *sender);
+gboolean device_is_bonding(struct btd_device *device, const char *sender);
+void device_cancel_bonding(struct btd_device *device, uint8_t status);
+int device_request_authentication(struct btd_device *device, auth_type_t type,
+ uint32_t passkey, void *cb);
+void device_cancel_authentication(struct btd_device *device, gboolean aborted);
+gboolean device_is_authenticating(struct btd_device *device);
+gboolean device_is_authorizing(struct btd_device *device);
+void device_set_authorizing(struct btd_device *device, gboolean auth);
+void device_add_connection(struct btd_device *device, DBusConnection *conn);
+void device_remove_connection(struct btd_device *device, DBusConnection *conn);
+void device_request_disconnect(struct btd_device *device, DBusMessage *msg);
+
+typedef void (*disconnect_watch) (struct btd_device *device, gboolean removal,
+ void *user_data);
+
+guint device_add_disconnect_watch(struct btd_device *device,
+ disconnect_watch watch, void *user_data,
+ GDestroyNotify destroy);
+void device_remove_disconnect_watch(struct btd_device *device, guint id);
+
+#define BTD_UUIDS(args...) ((const char *[]) { args, NULL } )
+
+struct btd_device_driver {
+ const char *name;
+ const char **uuids;
+ int (*probe) (struct btd_device *device, GSList *uuids);
+ void (*remove) (struct btd_device *device);
+};
+
+int btd_register_device_driver(struct btd_device_driver *driver);
+void btd_unregister_device_driver(struct btd_device_driver *driver);
+
+struct btd_device *btd_device_ref(struct btd_device *device);
+void btd_device_unref(struct btd_device *device);
diff --git a/src/error.c b/src/error.c
new file mode 100644
index 0000000..c2d9baa
--- /dev/null
+++ b/src/error.c
@@ -0,0 +1,116 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2007-2008 Fabien Chevalier <fabchevalier@free.fr>
+ *
+ *
+ * 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 <gdbus.h>
+
+#include "error.h"
+
+DBusMessage *btd_error_invalid_args(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
+}
+
+DBusMessage *btd_error_busy(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".InProgress",
+ "Operation already in progress");
+}
+
+DBusMessage *btd_error_already_exists(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".AlreadyExists",
+ "Already Exists");
+}
+
+DBusMessage *btd_error_not_supported(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".NotSupported",
+ "Operation is not supported");
+}
+
+DBusMessage *btd_error_not_connected(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".NotConnected",
+ "Not Connected");
+}
+
+DBusMessage *btd_error_already_connected(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".AlreadyConnected",
+ "Already Connected");
+}
+
+DBusMessage *btd_error_in_progress(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".InProgress",
+ "In Progress");
+}
+
+DBusMessage *btd_error_not_available(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".NotAvailable",
+ "Operation currently not available");
+}
+
+DBusMessage *btd_error_does_not_exist(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".DoesNotExist",
+ "Does Not Exist");
+}
+
+DBusMessage *btd_error_not_authorized(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".NotAuthorized",
+ "Operation Not Authorized");
+}
+
+DBusMessage *btd_error_no_such_adapter(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".NoSuchAdapter",
+ "No such adapter");
+}
+
+DBusMessage *btd_error_agent_not_available(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".AgentNotAvailable",
+ "Agent Not Available");
+}
+
+DBusMessage *btd_error_not_ready(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".NotReady",
+ "Resource Not Ready");
+}
+
+DBusMessage *btd_error_failed(DBusMessage *msg, const char *str)
+{
+ return g_dbus_create_error(msg, ERROR_INTERFACE
+ ".Failed", "%s", str);
+}
diff --git a/src/error.h b/src/error.h
new file mode 100644
index 0000000..cdb8919
--- /dev/null
+++ b/src/error.h
@@ -0,0 +1,43 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2007-2008 Fabien Chevalier <fabchevalier@free.fr>
+ *
+ *
+ * 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
+ *
+ */
+
+#include <dbus/dbus.h>
+
+#define ERROR_INTERFACE "org.bluez.Error"
+
+DBusMessage *btd_error_invalid_args(DBusMessage *msg);
+DBusMessage *btd_error_busy(DBusMessage *msg);
+DBusMessage *btd_error_already_exists(DBusMessage *msg);
+DBusMessage *btd_error_not_supported(DBusMessage *msg);
+DBusMessage *btd_error_not_connected(DBusMessage *msg);
+DBusMessage *btd_error_already_connected(DBusMessage *msg);
+DBusMessage *btd_error_not_available(DBusMessage *msg);
+DBusMessage *btd_error_in_progress(DBusMessage *msg);
+DBusMessage *btd_error_does_not_exist(DBusMessage *msg);
+DBusMessage *btd_error_not_authorized(DBusMessage *msg);
+DBusMessage *btd_error_no_such_adapter(DBusMessage *msg);
+DBusMessage *btd_error_agent_not_available(DBusMessage *msg);
+DBusMessage *btd_error_not_ready(DBusMessage *msg);
+DBusMessage *btd_error_failed(DBusMessage *msg, const char *str);
diff --git a/src/event.c b/src/event.c
new file mode 100644
index 0000000..4ca1be5
--- /dev/null
+++ b/src/event.c
@@ -0,0 +1,731 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+#include "textfile.h"
+
+#include "hcid.h"
+#include "adapter.h"
+#include "manager.h"
+#include "device.h"
+#include "error.h"
+#include "glib-helper.h"
+#include "dbus-common.h"
+#include "agent.h"
+#include "storage.h"
+#include "event.h"
+#include "sdpd.h"
+
+struct eir_data {
+ GSList *services;
+ int flags;
+ char *name;
+ gboolean name_complete;
+};
+
+static gboolean get_adapter_and_device(bdaddr_t *src, bdaddr_t *dst,
+ struct btd_adapter **adapter,
+ struct btd_device **device,
+ gboolean create)
+{
+ DBusConnection *conn = get_dbus_connection();
+ char peer_addr[18];
+
+ *adapter = manager_find_adapter(src);
+ if (!*adapter) {
+ error("Unable to find matching adapter");
+ return FALSE;
+ }
+
+ ba2str(dst, peer_addr);
+
+ if (create)
+ *device = adapter_get_device(conn, *adapter, peer_addr);
+ else
+ *device = adapter_find_device(*adapter, peer_addr);
+
+ if (create && !*device) {
+ error("Unable to get device object!");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*****************************************************************
+ *
+ * Section reserved to HCI commands confirmation handling and low
+ * level events(eg: device attached/dettached.
+ *
+ *****************************************************************/
+
+static void pincode_cb(struct agent *agent, DBusError *derr,
+ const char *pincode, struct btd_device *device)
+{
+ struct btd_adapter *adapter = device_get_adapter(device);
+ bdaddr_t sba, dba;
+ int err;
+
+ device_get_address(device, &dba);
+
+ if (derr) {
+ err = btd_adapter_pincode_reply(adapter, &dba, NULL);
+ if (err < 0)
+ goto fail;
+ return;
+ }
+
+ err = btd_adapter_pincode_reply(adapter, &dba, pincode);
+ if (err < 0)
+ goto fail;
+
+ adapter_get_address(adapter, &sba);
+
+ return;
+
+fail:
+ error("Sending PIN code reply failed: %s (%d)", strerror(-err), -err);
+}
+
+int btd_event_request_pin(bdaddr_t *sba, bdaddr_t *dba)
+{
+ struct btd_adapter *adapter;
+ struct btd_device *device;
+ char pin[17];
+ int pinlen;
+
+ if (!get_adapter_and_device(sba, dba, &adapter, &device, TRUE))
+ return -ENODEV;
+
+ memset(pin, 0, sizeof(pin));
+ pinlen = read_pin_code(sba, dba, pin);
+ if (pinlen > 0) {
+ btd_adapter_pincode_reply(adapter, dba, pin);
+ return 0;
+ }
+
+ return device_request_authentication(device, AUTH_TYPE_PINCODE, 0,
+ pincode_cb);
+}
+
+static int confirm_reply(struct btd_adapter *adapter,
+ struct btd_device *device, gboolean success)
+{
+ bdaddr_t bdaddr;
+
+ device_get_address(device, &bdaddr);
+
+ return btd_adapter_confirm_reply(adapter, &bdaddr, success);
+}
+
+static void confirm_cb(struct agent *agent, DBusError *err, void *user_data)
+{
+ struct btd_device *device = user_data;
+ struct btd_adapter *adapter = device_get_adapter(device);
+ gboolean success = (err == NULL) ? TRUE : FALSE;
+
+ confirm_reply(adapter, device, success);
+}
+
+static void passkey_cb(struct agent *agent, DBusError *err, uint32_t passkey,
+ void *user_data)
+{
+ struct btd_device *device = user_data;
+ struct btd_adapter *adapter = device_get_adapter(device);
+ bdaddr_t bdaddr;
+
+ device_get_address(device, &bdaddr);
+
+ if (err)
+ passkey = INVALID_PASSKEY;
+
+ btd_adapter_passkey_reply(adapter, &bdaddr, passkey);
+}
+
+int btd_event_user_confirm(bdaddr_t *sba, bdaddr_t *dba, uint32_t passkey)
+{
+ struct btd_adapter *adapter;
+ struct btd_device *device;
+
+ if (!get_adapter_and_device(sba, dba, &adapter, &device, TRUE))
+ return -ENODEV;
+
+ return device_request_authentication(device, AUTH_TYPE_CONFIRM,
+ passkey, confirm_cb);
+}
+
+int btd_event_user_passkey(bdaddr_t *sba, bdaddr_t *dba)
+{
+ struct btd_adapter *adapter;
+ struct btd_device *device;
+
+ if (!get_adapter_and_device(sba, dba, &adapter, &device, TRUE))
+ return -ENODEV;
+
+ return device_request_authentication(device, AUTH_TYPE_PASSKEY, 0,
+ passkey_cb);
+}
+
+int btd_event_user_notify(bdaddr_t *sba, bdaddr_t *dba, uint32_t passkey)
+{
+ struct btd_adapter *adapter;
+ struct btd_device *device;
+
+ if (!get_adapter_and_device(sba, dba, &adapter, &device, TRUE))
+ return -ENODEV;
+
+ return device_request_authentication(device, AUTH_TYPE_NOTIFY,
+ passkey, NULL);
+}
+
+void btd_event_bonding_complete(bdaddr_t *local, bdaddr_t *peer,
+ uint8_t status)
+{
+ struct btd_adapter *adapter;
+ struct btd_device *device;
+ gboolean create;
+
+ DBG("status 0x%02x", status);
+
+ create = status ? FALSE : TRUE;
+
+ if (!get_adapter_and_device(local, peer, &adapter, &device, create))
+ return;
+
+ if (device)
+ device_bonding_complete(device, status);
+}
+
+void btd_event_simple_pairing_complete(bdaddr_t *local, bdaddr_t *peer,
+ uint8_t status)
+{
+ struct btd_adapter *adapter;
+ struct btd_device *device;
+ gboolean create;
+
+ DBG("status=%02x", status);
+
+ create = status ? FALSE : TRUE;
+
+ if (!get_adapter_and_device(local, peer, &adapter, &device, create))
+ return;
+
+ if (!device)
+ return;
+
+ device_simple_pairing_complete(device, status);
+}
+
+static int parse_eir_data(struct eir_data *eir, uint8_t *eir_data,
+ size_t eir_length)
+{
+ uint16_t len = 0;
+ size_t total;
+ size_t uuid16_count = 0;
+ size_t uuid32_count = 0;
+ size_t uuid128_count = 0;
+ uint8_t *uuid16 = NULL;
+ uint8_t *uuid32 = NULL;
+ uint8_t *uuid128 = NULL;
+ uuid_t service;
+ char *uuid_str;
+ unsigned int i;
+
+ eir->flags = -1;
+
+ /* No EIR data to parse */
+ if (eir_data == NULL || eir_length == 0)
+ return 0;
+
+ while (len < eir_length - 1) {
+ uint8_t field_len = eir_data[0];
+
+ /* Check for the end of EIR */
+ if (field_len == 0)
+ break;
+
+ switch (eir_data[1]) {
+ case EIR_UUID16_SOME:
+ case EIR_UUID16_ALL:
+ uuid16_count = field_len / 2;
+ uuid16 = &eir_data[2];
+ break;
+ case EIR_UUID32_SOME:
+ case EIR_UUID32_ALL:
+ uuid32_count = field_len / 4;
+ uuid32 = &eir_data[2];
+ break;
+ case EIR_UUID128_SOME:
+ case EIR_UUID128_ALL:
+ uuid128_count = field_len / 16;
+ uuid128 = &eir_data[2];
+ break;
+ case EIR_FLAGS:
+ eir->flags = eir_data[2];
+ break;
+ case EIR_NAME_SHORT:
+ case EIR_NAME_COMPLETE:
+ if (g_utf8_validate((char *) &eir_data[2],
+ field_len - 1, NULL))
+ eir->name = g_strndup((char *) &eir_data[2],
+ field_len - 1);
+ else
+ eir->name = g_strdup("");
+ eir->name_complete = eir_data[1] == EIR_NAME_COMPLETE;
+ break;
+ }
+
+ len += field_len + 1;
+ eir_data += field_len + 1;
+ }
+
+ /* Bail out if got incorrect length */
+ if (len > eir_length)
+ return -EINVAL;
+
+ total = uuid16_count + uuid32_count + uuid128_count;
+
+ /* No UUIDs were parsed, so skip code below */
+ if (!total)
+ return 0;
+
+ /* Generate uuids in SDP format (EIR data is Little Endian) */
+ service.type = SDP_UUID16;
+ for (i = 0; i < uuid16_count; i++) {
+ uint16_t val16 = uuid16[1];
+
+ val16 = (val16 << 8) + uuid16[0];
+ service.value.uuid16 = val16;
+ uuid_str = bt_uuid2string(&service);
+ eir->services = g_slist_append(eir->services, uuid_str);
+ uuid16 += 2;
+ }
+
+ service.type = SDP_UUID32;
+ for (i = uuid16_count; i < uuid32_count + uuid16_count; i++) {
+ uint32_t val32 = uuid32[3];
+ int k;
+
+ for (k = 2; k >= 0; k--)
+ val32 = (val32 << 8) + uuid32[k];
+
+ service.value.uuid32 = val32;
+ uuid_str = bt_uuid2string(&service);
+ eir->services = g_slist_append(eir->services, uuid_str);
+ uuid32 += 4;
+ }
+
+ service.type = SDP_UUID128;
+ for (i = uuid32_count + uuid16_count; i < total; i++) {
+ int k;
+
+ for (k = 0; k < 16; k++)
+ service.value.uuid128.data[k] = uuid128[16 - k - 1];
+
+ uuid_str = bt_uuid2string(&service);
+ eir->services = g_slist_append(eir->services, uuid_str);
+ uuid128 += 16;
+ }
+
+ return 0;
+}
+
+static void free_eir_data(struct eir_data *eir)
+{
+ g_slist_foreach(eir->services, (GFunc) g_free, NULL);
+ g_slist_free(eir->services);
+ g_free(eir->name);
+}
+
+void btd_event_advertising_report(bdaddr_t *local, le_advertising_info *info)
+{
+ struct btd_adapter *adapter;
+ struct eir_data eir_data;
+ int8_t rssi;
+ int err;
+
+ adapter = manager_find_adapter(local);
+ if (adapter == NULL) {
+ error("No matching adapter found");
+ return;
+ }
+
+ memset(&eir_data, 0, sizeof(eir_data));
+ err = parse_eir_data(&eir_data, info->data, info->length);
+ if (err < 0)
+ error("Error parsing advertising data: %s (%d)",
+ strerror(-err), -err);
+
+ rssi = *(info->data + info->length);
+
+ adapter_update_device_from_info(adapter, info->bdaddr, rssi,
+ info->evt_type, eir_data.name,
+ eir_data.services, eir_data.flags);
+
+ free_eir_data(&eir_data);
+}
+
+static void update_lastseen(bdaddr_t *sba, bdaddr_t *dba)
+{
+ time_t t;
+ struct tm *tm;
+
+ t = time(NULL);
+ tm = gmtime(&t);
+
+ write_lastseen_info(sba, dba, tm);
+}
+
+static void update_lastused(bdaddr_t *sba, bdaddr_t *dba)
+{
+ time_t t;
+ struct tm *tm;
+
+ t = time(NULL);
+ tm = gmtime(&t);
+
+ write_lastused_info(sba, dba, tm);
+}
+
+void btd_event_device_found(bdaddr_t *local, bdaddr_t *peer, uint32_t class,
+ int8_t rssi, uint8_t *data)
+{
+ char filename[PATH_MAX + 1];
+ struct btd_adapter *adapter;
+ char local_addr[18], peer_addr[18], *alias, *name;
+ name_status_t name_status;
+ struct eir_data eir_data;
+ int state, err;
+ dbus_bool_t legacy;
+ unsigned char features[8];
+ const char *dev_name;
+
+ ba2str(local, local_addr);
+ ba2str(peer, peer_addr);
+
+ adapter = manager_find_adapter(local);
+ if (!adapter) {
+ error("No matching adapter found");
+ return;
+ }
+
+ update_lastseen(local, peer);
+ write_remote_class(local, peer, class);
+
+ if (data)
+ write_remote_eir(local, peer, data);
+
+ /*
+ * Workaround to identify periodic inquiry: inquiry complete event is
+ * sent after each window, however there isn't an event to indicate the
+ * beginning of a new periodic inquiry window.
+ */
+ state = adapter_get_state(adapter);
+ if (!(state & (STATE_STDINQ | STATE_LE_SCAN | STATE_PINQ))) {
+ state |= STATE_PINQ;
+ adapter_set_state(adapter, state);
+ }
+
+ /* the inquiry result can be triggered by NON D-Bus client */
+ if (adapter_get_discover_type(adapter) & DISC_RESOLVNAME &&
+ adapter_has_discov_sessions(adapter))
+ name_status = NAME_REQUIRED;
+ else
+ name_status = NAME_NOT_REQUIRED;
+
+ create_name(filename, PATH_MAX, STORAGEDIR, local_addr, "aliases");
+ alias = textfile_get(filename, peer_addr);
+
+ create_name(filename, PATH_MAX, STORAGEDIR, local_addr, "names");
+ name = textfile_get(filename, peer_addr);
+
+ if (data)
+ legacy = FALSE;
+ else if (name == NULL)
+ legacy = TRUE;
+ else if (read_remote_features(local, peer, NULL, features) == 0) {
+ if (features[0] & 0x01)
+ legacy = FALSE;
+ else
+ legacy = TRUE;
+ } else
+ legacy = TRUE;
+
+ memset(&eir_data, 0, sizeof(eir_data));
+ err = parse_eir_data(&eir_data, data, EIR_DATA_LENGTH);
+ if (err < 0)
+ error("Error parsing EIR data: %s (%d)", strerror(-err), -err);
+
+ /* Complete EIR names are always used. Shortened EIR names are only
+ * used if there is no name already in storage. */
+ dev_name = name;
+ if (eir_data.name != NULL) {
+ if (eir_data.name_complete) {
+ write_device_name(local, peer, eir_data.name);
+ name_status = NAME_NOT_REQUIRED;
+ dev_name = eir_data.name;
+ } else if (name == NULL)
+ dev_name = eir_data.name;
+ }
+
+ adapter_update_found_devices(adapter, peer, rssi, class, dev_name,
+ alias, legacy, eir_data.services,
+ name_status);
+
+ free_eir_data(&eir_data);
+ free(name);
+ free(alias);
+}
+
+void btd_event_set_legacy_pairing(bdaddr_t *local, bdaddr_t *peer,
+ gboolean legacy)
+{
+ struct btd_adapter *adapter;
+ struct remote_dev_info *dev, match;
+
+ adapter = manager_find_adapter(local);
+ if (!adapter) {
+ error("No matching adapter found");
+ return;
+ }
+
+ memset(&match, 0, sizeof(struct remote_dev_info));
+ bacpy(&match.bdaddr, peer);
+ match.name_status = NAME_ANY;
+
+ dev = adapter_search_found_devices(adapter, &match);
+ if (dev)
+ dev->legacy = legacy;
+}
+
+void btd_event_remote_class(bdaddr_t *local, bdaddr_t *peer, uint32_t class)
+{
+ uint32_t old_class = 0;
+ struct btd_adapter *adapter;
+ struct btd_device *device;
+ const gchar *dev_path;
+ DBusConnection *conn = get_dbus_connection();
+
+ read_remote_class(local, peer, &old_class);
+
+ if (old_class == class)
+ return;
+
+ write_remote_class(local, peer, class);
+
+ if (!get_adapter_and_device(local, peer, &adapter, &device, FALSE))
+ return;
+
+ if (!device)
+ return;
+
+ dev_path = device_get_path(device);
+
+ emit_property_changed(conn, dev_path, DEVICE_INTERFACE, "Class",
+ DBUS_TYPE_UINT32, &class);
+}
+
+void btd_event_remote_name(bdaddr_t *local, bdaddr_t *peer, uint8_t status,
+ char *name)
+{
+ struct btd_adapter *adapter;
+ char srcaddr[18], dstaddr[18];
+ int state;
+ struct btd_device *device;
+ struct remote_dev_info match, *dev_info;
+
+ if (status == 0) {
+ char *end;
+
+ /* It's ok to cast end between const and non-const since
+ * we know it points to inside of name which is non-const */
+ if (!g_utf8_validate(name, -1, (const char **) &end))
+ *end = '\0';
+
+ write_device_name(local, peer, name);
+ }
+
+ if (!get_adapter_and_device(local, peer, &adapter, &device, FALSE))
+ return;
+
+ ba2str(local, srcaddr);
+ ba2str(peer, dstaddr);
+
+ if (status != 0)
+ goto proceed;
+
+ bacpy(&match.bdaddr, peer);
+ match.name_status = NAME_ANY;
+
+ dev_info = adapter_search_found_devices(adapter, &match);
+ if (dev_info) {
+ g_free(dev_info->name);
+ dev_info->name = g_strdup(name);
+ adapter_emit_device_found(adapter, dev_info);
+ }
+
+ if (device)
+ device_set_name(device, name);
+
+proceed:
+ /* remove from remote name request list */
+ adapter_remove_found_device(adapter, peer);
+
+ /* check if there is more devices to request names */
+ if (adapter_resolve_names(adapter) == 0)
+ return;
+
+ state = adapter_get_state(adapter);
+ state &= ~STATE_RESOLVNAME;
+ adapter_set_state(adapter, state);
+}
+
+int btd_event_link_key_notify(bdaddr_t *local, bdaddr_t *peer,
+ uint8_t *key, uint8_t key_type,
+ uint8_t pin_length)
+{
+ struct btd_adapter *adapter;
+ struct btd_device *device;
+ int ret;
+
+ if (!get_adapter_and_device(local, peer, &adapter, &device, TRUE))
+ return -ENODEV;
+
+ DBG("storing link key of type 0x%02x", key_type);
+
+ ret = write_link_key(local, peer, key, key_type, pin_length);
+
+ if (ret == 0 && device_is_temporary(device))
+ device_set_temporary(device, FALSE);
+
+ return ret;
+}
+
+void btd_event_conn_complete(bdaddr_t *local, bdaddr_t *peer)
+{
+ struct btd_adapter *adapter;
+ struct btd_device *device;
+
+ if (!get_adapter_and_device(local, peer, &adapter, &device, TRUE))
+ return;
+
+ update_lastused(local, peer);
+
+ adapter_add_connection(adapter, device);
+}
+
+void btd_event_conn_failed(bdaddr_t *local, bdaddr_t *peer, uint8_t status)
+{
+ struct btd_adapter *adapter;
+ struct btd_device *device;
+ DBusConnection *conn = get_dbus_connection();
+
+ DBG("status 0x%02x", status);
+
+ if (!get_adapter_and_device(local, peer, &adapter, &device, FALSE))
+ return;
+
+ if (!device)
+ return;
+
+ if (device_is_temporary(device))
+ adapter_remove_device(conn, adapter, device, TRUE);
+}
+
+void btd_event_disconn_complete(bdaddr_t *local, bdaddr_t *peer)
+{
+ struct btd_adapter *adapter;
+ struct btd_device *device;
+
+ DBG("");
+
+ if (!get_adapter_and_device(local, peer, &adapter, &device, FALSE))
+ return;
+
+ if (!device)
+ return;
+
+ adapter_remove_connection(adapter, device);
+}
+
+/* Section reserved to device HCI callbacks */
+
+void btd_event_le_set_scan_enable_complete(bdaddr_t *local, uint8_t status)
+{
+ struct btd_adapter *adapter;
+ int state;
+
+ adapter = manager_find_adapter(local);
+ if (!adapter) {
+ error("No matching adapter found");
+ return;
+ }
+
+ if (status) {
+ error("Can't enable/disable LE scan");
+ return;
+ }
+
+ state = adapter_get_state(adapter);
+
+ /* Enabling or disabling ? */
+ if (state & STATE_LE_SCAN)
+ state &= ~STATE_LE_SCAN;
+ else
+ state |= STATE_LE_SCAN;
+
+ adapter_set_state(adapter, state);
+}
+
+void btd_event_returned_link_key(bdaddr_t *local, bdaddr_t *peer)
+{
+ struct btd_adapter *adapter;
+ struct btd_device *device;
+
+ if (!get_adapter_and_device(local, peer, &adapter, &device, TRUE))
+ return;
+
+ device_set_paired(device, TRUE);
+}
diff --git a/src/event.h b/src/event.h
new file mode 100644
index 0000000..765390a
--- /dev/null
+++ b/src/event.h
@@ -0,0 +1,44 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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
+ *
+ */
+
+int btd_event_request_pin(bdaddr_t *sba, bdaddr_t *dba);
+void btd_event_advertising_report(bdaddr_t *local, le_advertising_info *info);
+void btd_event_device_found(bdaddr_t *local, bdaddr_t *peer, uint32_t class,
+ int8_t rssi, uint8_t *data);
+void btd_event_set_legacy_pairing(bdaddr_t *local, bdaddr_t *peer, gboolean legacy);
+void btd_event_remote_class(bdaddr_t *local, bdaddr_t *peer, uint32_t class);
+void btd_event_remote_name(bdaddr_t *local, bdaddr_t *peer, uint8_t status, char *name);
+void btd_event_conn_complete(bdaddr_t *local, bdaddr_t *peer);
+void btd_event_conn_failed(bdaddr_t *local, bdaddr_t *peer, uint8_t status);
+void btd_event_disconn_complete(bdaddr_t *local, bdaddr_t *peer);
+void btd_event_bonding_complete(bdaddr_t *local, bdaddr_t *peer,
+ uint8_t status);
+void btd_event_simple_pairing_complete(bdaddr_t *local, bdaddr_t *peer, uint8_t status);
+void btd_event_le_set_scan_enable_complete(bdaddr_t *local, uint8_t status);
+void btd_event_returned_link_key(bdaddr_t *local, bdaddr_t *peer);
+int btd_event_user_confirm(bdaddr_t *sba, bdaddr_t *dba, uint32_t passkey);
+int btd_event_user_passkey(bdaddr_t *sba, bdaddr_t *dba);
+int btd_event_user_notify(bdaddr_t *sba, bdaddr_t *dba, uint32_t passkey);
+int btd_event_link_key_notify(bdaddr_t *local, bdaddr_t *peer, uint8_t *key,
+ uint8_t key_type, uint8_t pin_length);
diff --git a/src/genbuiltin b/src/genbuiltin
new file mode 100755
index 0000000..8b6f047
--- /dev/null
+++ b/src/genbuiltin
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+for i in $*
+do
+ echo "extern struct bluetooth_plugin_desc __bluetooth_builtin_$i;"
+done
+
+echo
+echo "static struct bluetooth_plugin_desc *__bluetooth_builtin[] = {"
+
+for i in $*
+do
+ echo " &__bluetooth_builtin_$i,"
+done
+
+echo " NULL"
+echo "};"
diff --git a/src/glib-helper.c b/src/glib-helper.c
new file mode 100644
index 0000000..22c14e7
--- /dev/null
+++ b/src/glib-helper.c
@@ -0,0 +1,587 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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 <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.h>
+
+#include "btio.h"
+#include "sdpd.h"
+#include "glib-helper.h"
+
+/* Number of seconds to keep a sdp_session_t in the cache */
+#define CACHE_TIMEOUT 2
+
+struct cached_sdp_session {
+ bdaddr_t src;
+ bdaddr_t dst;
+ sdp_session_t *session;
+ guint timer;
+};
+
+static GSList *cached_sdp_sessions = NULL;
+
+static gboolean cached_session_expired(gpointer user_data)
+{
+ struct cached_sdp_session *cached = user_data;
+
+ cached_sdp_sessions = g_slist_remove(cached_sdp_sessions, cached);
+
+ sdp_close(cached->session);
+
+ g_free(cached);
+
+ return FALSE;
+}
+
+static sdp_session_t *get_sdp_session(const bdaddr_t *src, const bdaddr_t *dst)
+{
+ GSList *l;
+
+ for (l = cached_sdp_sessions; l != NULL; l = l->next) {
+ struct cached_sdp_session *c = l->data;
+ sdp_session_t *session;
+
+ if (bacmp(&c->src, src) || bacmp(&c->dst, dst))
+ continue;
+
+ g_source_remove(c->timer);
+
+ session = c->session;
+
+ cached_sdp_sessions = g_slist_remove(cached_sdp_sessions, c);
+ g_free(c);
+
+ return session;
+ }
+
+ return sdp_connect(src, dst, SDP_NON_BLOCKING);
+}
+
+static void cache_sdp_session(bdaddr_t *src, bdaddr_t *dst,
+ sdp_session_t *session)
+{
+ struct cached_sdp_session *cached;
+
+ cached = g_new0(struct cached_sdp_session, 1);
+
+ bacpy(&cached->src, src);
+ bacpy(&cached->dst, dst);
+
+ cached->session = session;
+
+ cached_sdp_sessions = g_slist_append(cached_sdp_sessions, cached);
+
+ cached->timer = g_timeout_add_seconds(CACHE_TIMEOUT,
+ cached_session_expired,
+ cached);
+}
+
+struct search_context {
+ bdaddr_t src;
+ bdaddr_t dst;
+ sdp_session_t *session;
+ bt_callback_t cb;
+ bt_destroy_t destroy;
+ gpointer user_data;
+ uuid_t uuid;
+ guint io_id;
+};
+
+static GSList *context_list = NULL;
+
+static void search_context_cleanup(struct search_context *ctxt)
+{
+ context_list = g_slist_remove(context_list, ctxt);
+
+ if (ctxt->destroy)
+ ctxt->destroy(ctxt->user_data);
+
+ g_free(ctxt);
+}
+
+static void search_completed_cb(uint8_t type, uint16_t status,
+ uint8_t *rsp, size_t size, void *user_data)
+{
+ struct search_context *ctxt = user_data;
+ sdp_list_t *recs = NULL;
+ int scanned, seqlen = 0, bytesleft = size;
+ uint8_t dataType;
+ int err = 0;
+
+ if (status || type != SDP_SVC_SEARCH_ATTR_RSP) {
+ err = -EPROTO;
+ goto done;
+ }
+
+ scanned = sdp_extract_seqtype(rsp, bytesleft, &dataType, &seqlen);
+ if (!scanned || !seqlen)
+ goto done;
+
+ rsp += scanned;
+ bytesleft -= scanned;
+ do {
+ sdp_record_t *rec;
+ int recsize;
+
+ recsize = 0;
+ rec = sdp_extract_pdu(rsp, bytesleft, &recsize);
+ if (!rec)
+ break;
+
+ if (!recsize) {
+ sdp_record_free(rec);
+ break;
+ }
+
+ scanned += recsize;
+ rsp += recsize;
+ bytesleft -= recsize;
+
+ recs = sdp_list_append(recs, rec);
+ } while (scanned < (ssize_t) size && bytesleft > 0);
+
+done:
+ cache_sdp_session(&ctxt->src, &ctxt->dst, ctxt->session);
+
+ if (ctxt->cb)
+ ctxt->cb(recs, err, ctxt->user_data);
+
+ if (recs)
+ sdp_list_free(recs, (sdp_free_func_t) sdp_record_free);
+
+ search_context_cleanup(ctxt);
+}
+
+static gboolean search_process_cb(GIOChannel *chan, GIOCondition cond,
+ gpointer user_data)
+{
+ struct search_context *ctxt = user_data;
+ int err = 0;
+
+ if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
+ err = EIO;
+ goto failed;
+ }
+
+ if (sdp_process(ctxt->session) < 0)
+ goto failed;
+
+ return TRUE;
+
+failed:
+ if (err) {
+ sdp_close(ctxt->session);
+ ctxt->session = NULL;
+
+ if (ctxt->cb)
+ ctxt->cb(NULL, err, ctxt->user_data);
+
+ search_context_cleanup(ctxt);
+ }
+
+ return FALSE;
+}
+
+static gboolean connect_watch(GIOChannel *chan, GIOCondition cond,
+ gpointer user_data)
+{
+ struct search_context *ctxt = user_data;
+ sdp_list_t *search, *attrids;
+ uint32_t range = 0x0000ffff;
+ socklen_t len;
+ int sk, err = 0;
+
+ sk = g_io_channel_unix_get_fd(chan);
+ ctxt->io_id = 0;
+
+ len = sizeof(err);
+ if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
+ err = errno;
+ goto failed;
+ }
+
+ if (err != 0)
+ goto failed;
+
+ if (sdp_set_notify(ctxt->session, search_completed_cb, ctxt) < 0) {
+ err = EIO;
+ goto failed;
+ }
+
+ search = sdp_list_append(NULL, &ctxt->uuid);
+ attrids = sdp_list_append(NULL, &range);
+ if (sdp_service_search_attr_async(ctxt->session,
+ search, SDP_ATTR_REQ_RANGE, attrids) < 0) {
+ sdp_list_free(attrids, NULL);
+ sdp_list_free(search, NULL);
+ err = EIO;
+ goto failed;
+ }
+
+ sdp_list_free(attrids, NULL);
+ sdp_list_free(search, NULL);
+
+ /* Set callback responsible for update the internal SDP transaction */
+ ctxt->io_id = g_io_add_watch(chan,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ search_process_cb, ctxt);
+ return FALSE;
+
+failed:
+ sdp_close(ctxt->session);
+ ctxt->session = NULL;
+
+ if (ctxt->cb)
+ ctxt->cb(NULL, -err, ctxt->user_data);
+
+ search_context_cleanup(ctxt);
+
+ return FALSE;
+}
+
+static int create_search_context(struct search_context **ctxt,
+ const bdaddr_t *src,
+ const bdaddr_t *dst,
+ uuid_t *uuid)
+{
+ sdp_session_t *s;
+ GIOChannel *chan;
+
+ if (!ctxt)
+ return -EINVAL;
+
+ s = get_sdp_session(src, dst);
+ if (!s)
+ return -errno;
+
+ *ctxt = g_try_malloc0(sizeof(struct search_context));
+ if (!*ctxt) {
+ sdp_close(s);
+ return -ENOMEM;
+ }
+
+ bacpy(&(*ctxt)->src, src);
+ bacpy(&(*ctxt)->dst, dst);
+ (*ctxt)->session = s;
+ (*ctxt)->uuid = *uuid;
+
+ chan = g_io_channel_unix_new(sdp_get_socket(s));
+ (*ctxt)->io_id = g_io_add_watch(chan,
+ G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ connect_watch, *ctxt);
+ g_io_channel_unref(chan);
+
+ return 0;
+}
+
+int bt_search_service(const bdaddr_t *src, const bdaddr_t *dst,
+ uuid_t *uuid, bt_callback_t cb, void *user_data,
+ bt_destroy_t destroy)
+{
+ struct search_context *ctxt = NULL;
+ int err;
+
+ if (!cb)
+ return -EINVAL;
+
+ err = create_search_context(&ctxt, src, dst, uuid);
+ if (err < 0)
+ return err;
+
+ ctxt->cb = cb;
+ ctxt->destroy = destroy;
+ ctxt->user_data = user_data;
+
+ context_list = g_slist_append(context_list, ctxt);
+
+ return 0;
+}
+
+static gint find_by_bdaddr(gconstpointer data, gconstpointer user_data)
+{
+ const struct search_context *ctxt = data, *search = user_data;
+
+ return (bacmp(&ctxt->dst, &search->dst) &&
+ bacmp(&ctxt->src, &search->src));
+}
+
+int bt_cancel_discovery(const bdaddr_t *src, const bdaddr_t *dst)
+{
+ struct search_context match, *ctxt;
+ GSList *l;
+
+ memset(&match, 0, sizeof(match));
+ bacpy(&match.src, src);
+ bacpy(&match.dst, dst);
+
+ /* Ongoing SDP Discovery */
+ l = g_slist_find_custom(context_list, &match, find_by_bdaddr);
+ if (l == NULL)
+ return -ENOENT;
+
+ ctxt = l->data;
+
+ if (!ctxt->session)
+ return -ENOTCONN;
+
+ if (ctxt->io_id)
+ g_source_remove(ctxt->io_id);
+
+ if (ctxt->session)
+ sdp_close(ctxt->session);
+
+ search_context_cleanup(ctxt);
+
+ return 0;
+}
+
+char *bt_uuid2string(uuid_t *uuid)
+{
+ gchar *str;
+ uuid_t uuid128;
+ unsigned int data0;
+ unsigned short data1;
+ unsigned short data2;
+ unsigned short data3;
+ unsigned int data4;
+ unsigned short data5;
+
+ if (!uuid)
+ return NULL;
+
+ switch (uuid->type) {
+ case SDP_UUID16:
+ sdp_uuid16_to_uuid128(&uuid128, uuid);
+ break;
+ case SDP_UUID32:
+ sdp_uuid32_to_uuid128(&uuid128, uuid);
+ break;
+ case SDP_UUID128:
+ memcpy(&uuid128, uuid, sizeof(uuid_t));
+ break;
+ default:
+ /* Type of UUID unknown */
+ return NULL;
+ }
+
+ memcpy(&data0, &uuid128.value.uuid128.data[0], 4);
+ memcpy(&data1, &uuid128.value.uuid128.data[4], 2);
+ memcpy(&data2, &uuid128.value.uuid128.data[6], 2);
+ memcpy(&data3, &uuid128.value.uuid128.data[8], 2);
+ memcpy(&data4, &uuid128.value.uuid128.data[10], 4);
+ memcpy(&data5, &uuid128.value.uuid128.data[14], 2);
+
+ str = g_try_malloc0(MAX_LEN_UUID_STR);
+ if (!str)
+ return NULL;
+
+ sprintf(str, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x",
+ g_ntohl(data0), g_ntohs(data1),
+ g_ntohs(data2), g_ntohs(data3),
+ g_ntohl(data4), g_ntohs(data5));
+
+ return str;
+}
+
+static struct {
+ const char *name;
+ uint16_t class;
+} bt_services[] = {
+ { "vcp", VIDEO_CONF_SVCLASS_ID },
+ { "pbap", PBAP_SVCLASS_ID },
+ { "sap", SAP_SVCLASS_ID },
+ { "ftp", OBEX_FILETRANS_SVCLASS_ID },
+ { "bpp", BASIC_PRINTING_SVCLASS_ID },
+ { "bip", IMAGING_SVCLASS_ID },
+ { "synch", IRMC_SYNC_SVCLASS_ID },
+ { "dun", DIALUP_NET_SVCLASS_ID },
+ { "opp", OBEX_OBJPUSH_SVCLASS_ID },
+ { "fax", FAX_SVCLASS_ID },
+ { "spp", SERIAL_PORT_SVCLASS_ID },
+ { "hsp", HEADSET_SVCLASS_ID },
+ { "hfp", HANDSFREE_SVCLASS_ID },
+ { }
+};
+
+static uint16_t name2class(const char *pattern)
+{
+ int i;
+
+ for (i = 0; bt_services[i].name; i++) {
+ if (strcasecmp(bt_services[i].name, pattern) == 0)
+ return bt_services[i].class;
+ }
+
+ return 0;
+}
+
+static inline gboolean is_uuid128(const char *string)
+{
+ return (strlen(string) == 36 &&
+ string[8] == '-' &&
+ string[13] == '-' &&
+ string[18] == '-' &&
+ string[23] == '-');
+}
+
+static int string2uuid16(uuid_t *uuid, const char *string)
+{
+ int length = strlen(string);
+ char *endptr = NULL;
+ uint16_t u16;
+
+ if (length != 4 && length != 6)
+ return -EINVAL;
+
+ u16 = strtol(string, &endptr, 16);
+ if (endptr && *endptr == '\0') {
+ sdp_uuid16_create(uuid, u16);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+char *bt_name2string(const char *pattern)
+{
+ uuid_t uuid;
+ uint16_t uuid16;
+ int i;
+
+ /* UUID 128 string format */
+ if (is_uuid128(pattern))
+ return g_strdup(pattern);
+
+ /* Friendly service name format */
+ uuid16 = name2class(pattern);
+ if (uuid16)
+ goto proceed;
+
+ /* HEX format */
+ uuid16 = strtol(pattern, NULL, 16);
+ for (i = 0; bt_services[i].class; i++) {
+ if (bt_services[i].class == uuid16)
+ goto proceed;
+ }
+
+ return NULL;
+
+proceed:
+ sdp_uuid16_create(&uuid, uuid16);
+
+ return bt_uuid2string(&uuid);
+}
+
+int bt_string2uuid(uuid_t *uuid, const char *string)
+{
+ uint32_t data0, data4;
+ uint16_t data1, data2, data3, data5;
+
+ if (is_uuid128(string) &&
+ sscanf(string, "%08x-%04hx-%04hx-%04hx-%08x%04hx",
+ &data0, &data1, &data2, &data3, &data4, &data5) == 6) {
+ uint8_t val[16];
+
+ data0 = g_htonl(data0);
+ data1 = g_htons(data1);
+ data2 = g_htons(data2);
+ data3 = g_htons(data3);
+ data4 = g_htonl(data4);
+ data5 = g_htons(data5);
+
+ memcpy(&val[0], &data0, 4);
+ memcpy(&val[4], &data1, 2);
+ memcpy(&val[6], &data2, 2);
+ memcpy(&val[8], &data3, 2);
+ memcpy(&val[10], &data4, 4);
+ memcpy(&val[14], &data5, 2);
+
+ sdp_uuid128_create(uuid, val);
+
+ return 0;
+ } else {
+ uint16_t class = name2class(string);
+ if (class) {
+ sdp_uuid16_create(uuid, class);
+ return 0;
+ }
+
+ return string2uuid16(uuid, string);
+ }
+}
+
+gchar *bt_list2string(GSList *list)
+{
+ GSList *l;
+ gchar *str, *tmp;
+
+ if (!list)
+ return NULL;
+
+ str = g_strdup((const gchar *) list->data);
+
+ for (l = list->next; l; l = l->next) {
+ tmp = g_strconcat(str, " " , (const gchar *) l->data, NULL);
+ g_free(str);
+ str = tmp;
+ }
+
+ return str;
+}
+
+GSList *bt_string2list(const gchar *str)
+{
+ GSList *l = NULL;
+ gchar **uuids;
+ int i = 0;
+
+ if (!str)
+ return NULL;
+
+ /* FIXME: eglib doesn't support g_strsplit */
+ uuids = g_strsplit(str, " ", 0);
+ if (!uuids)
+ return NULL;
+
+ while (uuids[i]) {
+ l = g_slist_append(l, uuids[i]);
+ i++;
+ }
+
+ g_free(uuids);
+
+ return l;
+}
diff --git a/src/glib-helper.h b/src/glib-helper.h
new file mode 100644
index 0000000..c83f5e2
--- /dev/null
+++ b/src/glib-helper.h
@@ -0,0 +1,36 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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
+ *
+ */
+
+typedef void (*bt_callback_t) (sdp_list_t *recs, int err, gpointer user_data);
+typedef void (*bt_destroy_t) (gpointer user_data);
+
+int bt_search_service(const bdaddr_t *src, const bdaddr_t *dst,
+ uuid_t *uuid, bt_callback_t cb, void *user_data,
+ bt_destroy_t destroy);
+int bt_cancel_discovery(const bdaddr_t *src, const bdaddr_t *dst);
+
+gchar *bt_uuid2string(uuid_t *uuid);
+char *bt_name2string(const char *string);
+int bt_string2uuid(uuid_t *uuid, const char *string);
+gchar *bt_list2string(GSList *list);
+GSList *bt_string2list(const gchar *str);
diff --git a/src/hcid.h b/src/hcid.h
new file mode 100644
index 0000000..856723b
--- /dev/null
+++ b/src/hcid.h
@@ -0,0 +1,66 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2000-2001 Qualcomm Incorporated
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 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
+ *
+ */
+
+struct main_opts {
+ char host_name[40];
+ unsigned long flags;
+ char *name;
+ uint32_t class;
+ uint16_t pageto;
+ uint32_t discovto;
+ uint32_t pairto;
+ uint16_t link_mode;
+ uint16_t link_policy;
+ gboolean remember_powered;
+ gboolean reverse_sdp;
+ gboolean name_resolv;
+ gboolean debug_keys;
+ gboolean attrib_server;
+ gboolean le;
+
+ uint8_t scan;
+ uint8_t mode;
+ uint8_t discov_interval;
+ char deviceid[15]; /* FIXME: */
+};
+
+enum {
+ HCID_SET_NAME,
+ HCID_SET_CLASS,
+ HCID_SET_PAGETO,
+ HCID_SET_DISCOVTO,
+};
+
+extern struct main_opts main_opts;
+
+void btd_start_exit_timer(void);
+void btd_stop_exit_timer(void);
+
+gboolean plugin_init(GKeyFile *config, const char *enable,
+ const char *disable);
+void plugin_cleanup(void);
+
+void rfkill_init(void);
+void rfkill_exit(void);
diff --git a/src/log.c b/src/log.c
new file mode 100644
index 0000000..4ec4633
--- /dev/null
+++ b/src/log.c
@@ -0,0 +1,136 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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 <stdarg.h>
+#include <syslog.h>
+
+#include <glib.h>
+
+#include "log.h"
+
+void info(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+
+ vsyslog(LOG_INFO, format, ap);
+
+ va_end(ap);
+}
+
+void error(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+
+ vsyslog(LOG_ERR, format, ap);
+
+ va_end(ap);
+}
+
+void btd_debug(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+
+ vsyslog(LOG_DEBUG, format, ap);
+
+ va_end(ap);
+}
+
+extern struct btd_debug_desc __start___debug[];
+extern struct btd_debug_desc __stop___debug[];
+
+static gchar **enabled = NULL;
+
+static gboolean is_enabled(struct btd_debug_desc *desc)
+{
+ int i;
+
+ if (enabled == NULL)
+ return 0;
+
+ for (i = 0; enabled[i] != NULL; i++) {
+ if (desc->name != NULL && g_pattern_match_simple(enabled[i],
+ desc->name) == TRUE)
+ return 1;
+ if (desc->file != NULL && g_pattern_match_simple(enabled[i],
+ desc->file) == TRUE)
+ return 1;
+ }
+
+ return 0;
+}
+
+void __btd_toggle_debug()
+{
+ struct btd_debug_desc *desc;
+
+ for (desc = __start___debug; desc < __stop___debug; desc++)
+ desc->flags |= BTD_DEBUG_FLAG_PRINT;
+}
+
+void __btd_log_init(const char *debug, int detach)
+{
+ int option = LOG_NDELAY | LOG_PID;
+ struct btd_debug_desc *desc;
+ const char *name = NULL, *file = NULL;
+
+ if (debug != NULL)
+ enabled = g_strsplit_set(debug, ":, ", 0);
+
+ for (desc = __start___debug; desc < __stop___debug; desc++) {
+ if (file != NULL || name != NULL) {
+ if (g_strcmp0(desc->file, file) == 0) {
+ if (desc->name == NULL)
+ desc->name = name;
+ } else
+ file = NULL;
+ }
+
+ if (is_enabled(desc))
+ desc->flags |= BTD_DEBUG_FLAG_PRINT;
+ }
+
+ if (!detach)
+ option |= LOG_PERROR;
+
+ openlog("bluetoothd", option, LOG_DAEMON);
+
+ syslog(LOG_INFO, "Bluetooth deamon %s", VERSION);
+}
+
+void __btd_log_cleanup(void)
+{
+ closelog();
+
+ g_strfreev(enabled);
+}
diff --git a/src/log.h b/src/log.h
new file mode 100644
index 0000000..cc45cbf
--- /dev/null
+++ b/src/log.h
@@ -0,0 +1,57 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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
+ *
+ */
+
+void info(const char *format, ...) __attribute__((format(printf, 1, 2)));
+void error(const char *format, ...) __attribute__((format(printf, 1, 2)));
+
+void btd_debug(const char *format, ...) __attribute__((format(printf, 1, 2)));
+
+void __btd_log_init(const char *debug, int detach);
+void __btd_log_cleanup(void);
+void __btd_toggle_debug();
+
+struct btd_debug_desc {
+ const char *name;
+ const char *file;
+#define BTD_DEBUG_FLAG_DEFAULT (0)
+#define BTD_DEBUG_FLAG_PRINT (1 << 0)
+ unsigned int flags;
+} __attribute__((aligned(8)));
+
+/**
+ * DBG:
+ * @fmt: format string
+ * @arg...: list of arguments
+ *
+ * Simple macro around btd_debug() which also include the function
+ * name it is called in.
+ */
+#define DBG(fmt, arg...) do { \
+ static struct btd_debug_desc __btd_debug_desc \
+ __attribute__((used, section("__debug"), aligned(8))) = { \
+ .file = __FILE__, .flags = BTD_DEBUG_FLAG_DEFAULT, \
+ }; \
+ if (__btd_debug_desc.flags & BTD_DEBUG_FLAG_PRINT) \
+ btd_debug("%s:%s() " fmt, __FILE__, __FUNCTION__ , ## arg); \
+} while (0)
+
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..c454327
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,509 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2000-2001 Qualcomm Incorporated
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 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 <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/uuid.h>
+
+#include <glib.h>
+
+#include <dbus/dbus.h>
+
+#include <gdbus.h>
+
+#include "log.h"
+
+#include "hcid.h"
+#include "sdpd.h"
+#include "attrib-server.h"
+#include "adapter.h"
+#include "event.h"
+#include "dbus-common.h"
+#include "agent.h"
+#include "manager.h"
+
+#ifdef HAVE_CAPNG
+#include <cap-ng.h>
+#endif
+
+#define BLUEZ_NAME "org.bluez"
+
+#define LAST_ADAPTER_EXIT_TIMEOUT 30
+
+#define DEFAULT_DISCOVERABLE_TIMEOUT 180 /* 3 minutes */
+
+struct main_opts main_opts;
+
+static GKeyFile *load_config(const char *file)
+{
+ GError *err = NULL;
+ GKeyFile *keyfile;
+
+ keyfile = g_key_file_new();
+
+ g_key_file_set_list_separator(keyfile, ',');
+
+ if (!g_key_file_load_from_file(keyfile, file, 0, &err)) {
+ error("Parsing %s failed: %s", file, err->message);
+ g_error_free(err);
+ g_key_file_free(keyfile);
+ return NULL;
+ }
+
+ return keyfile;
+}
+
+static void parse_config(GKeyFile *config)
+{
+ GError *err = NULL;
+ char *str;
+ int val;
+ gboolean boolean;
+
+ if (!config)
+ return;
+
+ DBG("parsing main.conf");
+
+ val = g_key_file_get_integer(config, "General",
+ "DiscoverableTimeout", &err);
+ if (err) {
+ DBG("%s", err->message);
+ g_clear_error(&err);
+ } else {
+ DBG("discovto=%d", val);
+ main_opts.discovto = val;
+ main_opts.flags |= 1 << HCID_SET_DISCOVTO;
+ }
+
+ val = g_key_file_get_integer(config, "General",
+ "PairableTimeout", &err);
+ if (err) {
+ DBG("%s", err->message);
+ g_clear_error(&err);
+ } else {
+ DBG("pairto=%d", val);
+ main_opts.pairto = val;
+ }
+
+ val = g_key_file_get_integer(config, "General", "PageTimeout", &err);
+ if (err) {
+ DBG("%s", err->message);
+ g_clear_error(&err);
+ } else {
+ DBG("pageto=%d", val);
+ main_opts.pageto = val;
+ main_opts.flags |= 1 << HCID_SET_PAGETO;
+ }
+
+ str = g_key_file_get_string(config, "General", "Name", &err);
+ if (err) {
+ DBG("%s", err->message);
+ g_clear_error(&err);
+ } else {
+ DBG("name=%s", str);
+ g_free(main_opts.name);
+ main_opts.name = g_strdup(str);
+ main_opts.flags |= 1 << HCID_SET_NAME;
+ g_free(str);
+ }
+
+ str = g_key_file_get_string(config, "General", "Class", &err);
+ if (err) {
+ DBG("%s", err->message);
+ g_clear_error(&err);
+ } else {
+ DBG("class=%s", str);
+ main_opts.class = strtol(str, NULL, 16);
+ main_opts.flags |= 1 << HCID_SET_CLASS;
+ g_free(str);
+ }
+
+ val = g_key_file_get_integer(config, "General",
+ "DiscoverSchedulerInterval", &err);
+ if (err) {
+ DBG("%s", err->message);
+ g_clear_error(&err);
+ } else {
+ DBG("discov_interval=%d", val);
+ main_opts.discov_interval = val;
+ }
+
+ boolean = g_key_file_get_boolean(config, "General",
+ "InitiallyPowered", &err);
+ if (err) {
+ DBG("%s", err->message);
+ g_clear_error(&err);
+ } else if (boolean == FALSE)
+ main_opts.mode = MODE_OFF;
+
+ boolean = g_key_file_get_boolean(config, "General",
+ "RememberPowered", &err);
+ if (err) {
+ DBG("%s", err->message);
+ g_clear_error(&err);
+ } else
+ main_opts.remember_powered = boolean;
+
+ str = g_key_file_get_string(config, "General", "DeviceID", &err);
+ if (err) {
+ DBG("%s", err->message);
+ g_clear_error(&err);
+ } else {
+ DBG("deviceid=%s", str);
+ strncpy(main_opts.deviceid, str,
+ sizeof(main_opts.deviceid) - 1);
+ g_free(str);
+ }
+
+ boolean = g_key_file_get_boolean(config, "General",
+ "ReverseServiceDiscovery", &err);
+ if (err) {
+ DBG("%s", err->message);
+ g_clear_error(&err);
+ } else
+ main_opts.reverse_sdp = boolean;
+
+ boolean = g_key_file_get_boolean(config, "General",
+ "NameResolving", &err);
+ if (err)
+ g_clear_error(&err);
+ else
+ main_opts.name_resolv = boolean;
+
+ boolean = g_key_file_get_boolean(config, "General",
+ "DebugKeys", &err);
+ if (err)
+ g_clear_error(&err);
+ else
+ main_opts.debug_keys = boolean;
+
+ boolean = g_key_file_get_boolean(config, "General",
+ "AttributeServer", &err);
+ if (err)
+ g_clear_error(&err);
+ else
+ main_opts.attrib_server = boolean;
+
+ boolean = g_key_file_get_boolean(config, "General",
+ "EnableLE", &err);
+ if (err)
+ g_clear_error(&err);
+ else
+ main_opts.le = boolean;
+
+ main_opts.link_mode = HCI_LM_ACCEPT;
+
+ main_opts.link_policy = HCI_LP_RSWITCH | HCI_LP_SNIFF |
+ HCI_LP_HOLD | HCI_LP_PARK;
+}
+
+static void init_defaults(void)
+{
+ /* Default HCId settings */
+ memset(&main_opts, 0, sizeof(main_opts));
+ main_opts.scan = SCAN_PAGE;
+ main_opts.mode = MODE_CONNECTABLE;
+ main_opts.name = g_strdup("BlueZ");
+ main_opts.discovto = DEFAULT_DISCOVERABLE_TIMEOUT;
+ main_opts.remember_powered = TRUE;
+ main_opts.reverse_sdp = TRUE;
+ main_opts.name_resolv = TRUE;
+
+ if (gethostname(main_opts.host_name, sizeof(main_opts.host_name) - 1) < 0)
+ strcpy(main_opts.host_name, "noname");
+}
+
+static GMainLoop *event_loop;
+
+static void sig_term(int sig)
+{
+ g_main_loop_quit(event_loop);
+}
+
+static void sig_debug(int sig)
+{
+ __btd_toggle_debug();
+}
+
+static gchar *option_debug = NULL;
+static gchar *option_plugin = NULL;
+static gchar *option_noplugin = NULL;
+static gboolean option_detach = TRUE;
+static gboolean option_version = FALSE;
+static gboolean option_udev = FALSE;
+
+static guint last_adapter_timeout = 0;
+
+static gboolean exit_timeout(gpointer data)
+{
+ g_main_loop_quit(event_loop);
+ last_adapter_timeout = 0;
+ return FALSE;
+}
+
+void btd_start_exit_timer(void)
+{
+ if (option_udev == FALSE)
+ return;
+
+ if (last_adapter_timeout > 0)
+ g_source_remove(last_adapter_timeout);
+
+ last_adapter_timeout = g_timeout_add_seconds(LAST_ADAPTER_EXIT_TIMEOUT,
+ exit_timeout, NULL);
+}
+
+void btd_stop_exit_timer(void)
+{
+ if (last_adapter_timeout == 0)
+ return;
+
+ g_source_remove(last_adapter_timeout);
+ last_adapter_timeout = 0;
+}
+
+static void disconnect_dbus(void)
+{
+ DBusConnection *conn = get_dbus_connection();
+
+ if (!conn || !dbus_connection_get_is_connected(conn))
+ return;
+
+ manager_cleanup(conn, "/");
+
+ set_dbus_connection(NULL);
+
+ dbus_connection_unref(conn);
+}
+
+static int connect_dbus(void)
+{
+ DBusConnection *conn;
+ DBusError err;
+
+ dbus_error_init(&err);
+
+ conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, BLUEZ_NAME, &err);
+ if (!conn) {
+ if (dbus_error_is_set(&err)) {
+ dbus_error_free(&err);
+ return -EIO;
+ }
+ return -EALREADY;
+ }
+
+ if (!manager_init(conn, "/"))
+ return -EIO;
+
+ set_dbus_connection(conn);
+
+ return 0;
+}
+
+static gboolean parse_debug(const char *key, const char *value,
+ gpointer user_data, GError **error)
+{
+ if (value)
+ option_debug = g_strdup(value);
+ else
+ option_debug = g_strdup("*");
+
+ return TRUE;
+}
+
+static GOptionEntry options[] = {
+ { "debug", 'd', G_OPTION_FLAG_OPTIONAL_ARG,
+ G_OPTION_ARG_CALLBACK, parse_debug,
+ "Specify debug options to enable", "DEBUG" },
+ { "plugin", 'p', 0, G_OPTION_ARG_STRING, &option_plugin,
+ "Specify plugins to load", "NAME,..," },
+ { "noplugin", 'P', 0, G_OPTION_ARG_STRING, &option_noplugin,
+ "Specify plugins not to load", "NAME,..." },
+ { "nodetach", 'n', G_OPTION_FLAG_REVERSE,
+ G_OPTION_ARG_NONE, &option_detach,
+ "Don't run as daemon in background" },
+ { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
+ "Show version information and exit" },
+ { "udev", 'u', 0, G_OPTION_ARG_NONE, &option_udev,
+ "Run from udev mode of operation" },
+ { NULL },
+};
+
+int main(int argc, char *argv[])
+{
+ GOptionContext *context;
+ GError *err = NULL;
+ struct sigaction sa;
+ uint16_t mtu = 0;
+ GKeyFile *config;
+
+ init_defaults();
+
+#ifdef HAVE_CAPNG
+ /* Drop capabilities */
+ capng_clear(CAPNG_SELECT_BOTH);
+ capng_updatev(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED,
+ CAP_NET_BIND_SERVICE, CAP_NET_ADMIN,
+ CAP_NET_RAW, CAP_IPC_LOCK, -1);
+ capng_apply(CAPNG_SELECT_BOTH);
+#endif
+
+ context = g_option_context_new(NULL);
+ g_option_context_add_main_entries(context, options, NULL);
+
+ if (g_option_context_parse(context, &argc, &argv, &err) == FALSE) {
+ if (err != NULL) {
+ g_printerr("%s\n", err->message);
+ g_error_free(err);
+ } else
+ g_printerr("An unknown error occurred\n");
+ exit(1);
+ }
+
+ g_option_context_free(context);
+
+ if (option_version == TRUE) {
+ printf("%s\n", VERSION);
+ exit(0);
+ }
+
+ if (option_udev == TRUE) {
+ int err;
+
+ option_detach = TRUE;
+ err = connect_dbus();
+ if (err < 0) {
+ if (err == -EALREADY)
+ exit(0);
+ exit(1);
+ }
+ }
+
+ if (option_detach == TRUE && option_udev == FALSE) {
+ if (daemon(0, 0)) {
+ perror("Can't start daemon");
+ exit(1);
+ }
+ }
+
+ umask(0077);
+
+ __btd_log_init(option_debug, option_detach);
+
+ 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_debug;
+ sigaction(SIGUSR2, &sa, NULL);
+
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &sa, NULL);
+
+ config = load_config(CONFIGDIR "/main.conf");
+
+ parse_config(config);
+
+ agent_init();
+
+ if (option_udev == FALSE) {
+ if (connect_dbus() < 0) {
+ error("Unable to get on D-Bus");
+ exit(1);
+ }
+ } else {
+ if (daemon(0, 0)) {
+ perror("Can't start daemon");
+ exit(1);
+ }
+ }
+
+ start_sdp_server(mtu, main_opts.deviceid, SDP_SERVER_COMPAT);
+
+ if (main_opts.attrib_server) {
+ if (attrib_server_init() < 0)
+ error("Can't initialize attribute server");
+ }
+
+ /* Loading plugins has to be done after D-Bus has been setup since
+ * the plugins might wanna expose some paths on the bus. However the
+ * best order of how to init various subsystems of the Bluetooth
+ * daemon needs to be re-worked. */
+ plugin_init(config, option_plugin, option_noplugin);
+
+ event_loop = g_main_loop_new(NULL, FALSE);
+
+ if (adapter_ops_setup() < 0) {
+ error("adapter_ops_setup failed");
+ exit(1);
+ }
+
+ rfkill_init();
+
+ DBG("Entering main loop");
+
+ g_main_loop_run(event_loop);
+
+ disconnect_dbus();
+
+ rfkill_exit();
+
+ plugin_cleanup();
+
+ if (main_opts.attrib_server)
+ attrib_server_exit();
+
+ stop_sdp_server();
+
+ agent_exit();
+
+ g_main_loop_unref(event_loop);
+
+ if (config)
+ g_key_file_free(config);
+
+ info("Exit");
+
+ __btd_log_cleanup();
+
+ return 0;
+}
diff --git a/src/main.conf b/src/main.conf
new file mode 100644
index 0000000..c03f135
--- /dev/null
+++ b/src/main.conf
@@ -0,0 +1,66 @@
+[General]
+
+# List of plugins that should not be loaded on bluetoothd startup
+#DisablePlugins = network,input
+
+# Default adaper name
+# %h - substituted for hostname
+# %d - substituted for adapter id
+Name = %h-%d
+
+# Default device class. Only the major and minor device class bits are
+# considered.
+Class = 0x000100
+
+# How long to stay in discoverable mode before going back to non-discoverable
+# The value is in seconds. Default is 180, i.e. 3 minutes.
+# 0 = disable timer, i.e. stay discoverable forever
+DiscoverableTimeout = 0
+
+# How long to stay in pairable mode before going back to non-discoverable
+# The value is in seconds. Default is 0.
+# 0 = disable timer, i.e. stay pairable forever
+PairableTimeout = 0
+
+# Use some other page timeout than the controller default one
+# which is 16384 (10 seconds).
+PageTimeout = 8192
+
+# Discover scheduler interval used in Adapter.DiscoverDevices
+# The value is in seconds. Defaults is 0 to use controller scheduler.
+DiscoverSchedulerInterval = 0
+
+# What value should be assumed for the adapter Powered property when
+# SetProperty(Powered, ...) hasn't been called yet. Defaults to true
+InitiallyPowered = true
+
+# Remember the previously stored Powered state when initializing adapters
+RememberPowered = true
+
+# Use vendor, product and version information for DID profile support.
+# The values are separated by ":" and VID, PID and version.
+#DeviceID = 1234:5678:abcd
+
+# Do reverse service discovery for previously unknown devices that connect to
+# us. This option is really only needed for qualification since the BITE tester
+# doesn't like us doing reverse SDP for some test cases (though there could in
+# theory be other useful purposes for this too). Defaults to true.
+ReverseServiceDiscovery = true
+
+# Enable name resolving after inquiry. Set it to 'false' if you don't need
+# remote devices name and want shorter discovery cycle. Defaults to 'true'.
+NameResolving = true
+
+# Enable runtime persistency of debug link keys. Default is false which
+# makes debug link keys valid only for the duration of the connection
+# that they were created for.
+DebugKeys = false
+
+# Enable Low Energy support if the dongle supports. Default is false.
+# Enable/Disable interleave discovery and attribute server over LE.
+EnableLE = false
+
+# Enable the GATT Attribute Server. Default is false, because it is only
+# useful for testing. Attribute server is not enabled over LE if EnableLE
+# is false.
+AttributeServer = false
diff --git a/src/manager.c b/src/manager.c
new file mode 100644
index 0000000..e805e0c
--- /dev/null
+++ b/src/manager.c
@@ -0,0 +1,431 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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 <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+
+#include <glib.h>
+
+#include <dbus/dbus.h>
+
+#include <gdbus.h>
+
+#include "hcid.h"
+#include "dbus-common.h"
+#include "log.h"
+#include "adapter.h"
+#include "error.h"
+#include "manager.h"
+
+static char base_path[50] = "/org/bluez";
+
+static DBusConnection *connection = NULL;
+static int default_adapter_id = -1;
+static GSList *adapters = NULL;
+
+const char *manager_get_base_path(void)
+{
+ return base_path;
+}
+
+static DBusMessage *default_adapter(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessage *reply;
+ struct btd_adapter *adapter;
+ const gchar *path;
+
+ adapter = manager_find_adapter_by_id(default_adapter_id);
+ if (!adapter)
+ return btd_error_no_such_adapter(msg);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ path = adapter_get_path(adapter);
+
+ dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID);
+
+ return reply;
+}
+
+static DBusMessage *find_adapter(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessage *reply;
+ struct btd_adapter *adapter;
+ const char *pattern;
+ int dev_id;
+ const gchar *path;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &pattern,
+ DBUS_TYPE_INVALID))
+ return NULL;
+
+ /* hci_devid() would make sense to use here, except it is
+ * restricted to devices which are up */
+ if (!strcmp(pattern, "any") || !strcmp(pattern, "00:00:00:00:00:00")) {
+ path = adapter_any_get_path();
+ if (path != NULL)
+ goto done;
+ return btd_error_no_such_adapter(msg);
+ } else if (!strncmp(pattern, "hci", 3) && strlen(pattern) >= 4) {
+ dev_id = atoi(pattern + 3);
+ adapter = manager_find_adapter_by_id(dev_id);
+ } else {
+ bdaddr_t bdaddr;
+ str2ba(pattern, &bdaddr);
+ adapter = manager_find_adapter(&bdaddr);
+ }
+
+ if (!adapter)
+ return btd_error_no_such_adapter(msg);
+
+ path = adapter_get_path(adapter);
+
+done:
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID);
+
+ return reply;
+}
+
+static DBusMessage *list_adapters(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessageIter iter;
+ DBusMessageIter array_iter;
+ DBusMessage *reply;
+ GSList *l;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_OBJECT_PATH_AS_STRING, &array_iter);
+
+ for (l = adapters; l; l = l->next) {
+ struct btd_adapter *adapter = l->data;
+ const gchar *path = adapter_get_path(adapter);
+
+ dbus_message_iter_append_basic(&array_iter,
+ DBUS_TYPE_OBJECT_PATH, &path);
+ }
+
+ dbus_message_iter_close_container(&iter, &array_iter);
+
+ return reply;
+}
+
+static DBusMessage *get_properties(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ GSList *list;
+ char **array;
+ int i;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ array = g_new0(char *, g_slist_length(adapters) + 1);
+ for (i = 0, list = adapters; list; list = list->next) {
+ struct btd_adapter *adapter = list->data;
+
+ array[i] = (char *) adapter_get_path(adapter);
+ i++;
+ }
+ dict_append_array(&dict, "Adapters", DBUS_TYPE_OBJECT_PATH, &array, i);
+ g_free(array);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static GDBusMethodTable manager_methods[] = {
+ { "GetProperties", "", "a{sv}",get_properties },
+ { "DefaultAdapter", "", "o", default_adapter },
+ { "FindAdapter", "s", "o", find_adapter },
+ { "ListAdapters", "", "ao", list_adapters,
+ G_DBUS_METHOD_FLAG_DEPRECATED},
+ { }
+};
+
+static GDBusSignalTable manager_signals[] = {
+ { "PropertyChanged", "sv" },
+ { "AdapterAdded", "o" },
+ { "AdapterRemoved", "o" },
+ { "DefaultAdapterChanged", "o" },
+ { }
+};
+
+dbus_bool_t manager_init(DBusConnection *conn, const char *path)
+{
+ connection = conn;
+
+ snprintf(base_path, sizeof(base_path), "/org/bluez/%d", getpid());
+
+ return g_dbus_register_interface(conn, "/", MANAGER_INTERFACE,
+ manager_methods, manager_signals,
+ NULL, NULL, NULL);
+}
+
+static void manager_update_adapters(void)
+{
+ GSList *list;
+ char **array;
+ int i;
+
+ array = g_new0(char *, g_slist_length(adapters) + 1);
+ for (i = 0, list = adapters; list; list = list->next) {
+ struct btd_adapter *adapter = list->data;
+
+ array[i] = (char *) adapter_get_path(adapter);
+ i++;
+ }
+
+ emit_array_property_changed(connection, "/",
+ MANAGER_INTERFACE, "Adapters",
+ DBUS_TYPE_OBJECT_PATH, &array, i);
+
+ g_free(array);
+}
+
+static void manager_set_default_adapter(int id)
+{
+ struct btd_adapter *adapter;
+ const gchar *path;
+
+ default_adapter_id = id;
+
+ adapter = manager_find_adapter_by_id(id);
+ if (!adapter)
+ return;
+
+ path = adapter_get_path(adapter);
+
+ g_dbus_emit_signal(connection, "/",
+ MANAGER_INTERFACE,
+ "DefaultAdapterChanged",
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID);
+}
+
+static void manager_remove_adapter(struct btd_adapter *adapter)
+{
+ uint16_t dev_id = adapter_get_dev_id(adapter);
+ const gchar *path = adapter_get_path(adapter);
+
+ adapters = g_slist_remove(adapters, adapter);
+
+ manager_update_adapters();
+
+ if (default_adapter_id == dev_id || default_adapter_id < 0) {
+ int new_default = hci_get_route(NULL);
+
+ manager_set_default_adapter(new_default);
+ }
+
+ g_dbus_emit_signal(connection, "/",
+ MANAGER_INTERFACE, "AdapterRemoved",
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID);
+
+ adapter_remove(adapter);
+
+ if (adapters == NULL)
+ btd_start_exit_timer();
+}
+
+void manager_cleanup(DBusConnection *conn, const char *path)
+{
+ g_slist_foreach(adapters, (GFunc) manager_remove_adapter, NULL);
+ g_slist_free(adapters);
+
+ g_dbus_unregister_interface(conn, "/", MANAGER_INTERFACE);
+}
+
+static gint adapter_id_cmp(gconstpointer a, gconstpointer b)
+{
+ struct btd_adapter *adapter = (struct btd_adapter *) a;
+ uint16_t id = GPOINTER_TO_UINT(b);
+ uint16_t dev_id = adapter_get_dev_id(adapter);
+
+ return dev_id == id ? 0 : -1;
+}
+
+static gint adapter_cmp(gconstpointer a, gconstpointer b)
+{
+ struct btd_adapter *adapter = (struct btd_adapter *) a;
+ const bdaddr_t *bdaddr = b;
+ bdaddr_t src;
+
+ adapter_get_address(adapter, &src);
+
+ return bacmp(&src, bdaddr);
+}
+
+struct btd_adapter *manager_find_adapter(const bdaddr_t *sba)
+{
+ GSList *match;
+
+ match = g_slist_find_custom(adapters, sba, adapter_cmp);
+ if (!match)
+ return NULL;
+
+ return match->data;
+}
+
+struct btd_adapter *manager_find_adapter_by_id(int id)
+{
+ GSList *match;
+
+ match = g_slist_find_custom(adapters, GINT_TO_POINTER(id),
+ adapter_id_cmp);
+ if (!match)
+ return NULL;
+
+ return match->data;
+}
+
+void manager_foreach_adapter(adapter_cb func, gpointer user_data)
+{
+ g_slist_foreach(adapters, (GFunc) func, user_data);
+}
+
+GSList *manager_get_adapters(void)
+{
+ return adapters;
+}
+
+void manager_add_adapter(const char *path)
+{
+ g_dbus_emit_signal(connection, "/",
+ MANAGER_INTERFACE, "AdapterAdded",
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID);
+
+ manager_update_adapters();
+
+ btd_stop_exit_timer();
+}
+
+struct btd_adapter *btd_manager_register_adapter(int id)
+{
+ struct btd_adapter *adapter;
+ const char *path;
+
+ adapter = manager_find_adapter_by_id(id);
+ if (adapter) {
+ error("Unable to register adapter: hci%d already exist", id);
+ return NULL;
+ }
+
+ adapter = adapter_create(connection, id);
+ if (!adapter)
+ return NULL;
+
+ adapters = g_slist_append(adapters, adapter);
+
+ if (!adapter_init(adapter)) {
+ btd_adapter_unref(adapter);
+ return NULL;
+ }
+
+ path = adapter_get_path(adapter);
+ g_dbus_emit_signal(connection, "/",
+ MANAGER_INTERFACE, "AdapterAdded",
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID);
+
+ manager_update_adapters();
+
+ btd_stop_exit_timer();
+
+ if (default_adapter_id < 0)
+ manager_set_default_adapter(id);
+
+ DBG("Adapter %s registered", path);
+
+ return btd_adapter_ref(adapter);
+}
+
+int btd_manager_unregister_adapter(int id)
+{
+ struct btd_adapter *adapter;
+ const gchar *path;
+
+ adapter = manager_find_adapter_by_id(id);
+ if (!adapter)
+ return -1;
+
+ path = adapter_get_path(adapter);
+
+ info("Unregister path: %s", path);
+
+ manager_remove_adapter(adapter);
+
+ return 0;
+}
+
+void btd_manager_set_did(uint16_t vendor, uint16_t product, uint16_t version)
+{
+ GSList *l;
+
+ for (l = adapters; l != NULL; l = g_slist_next(l)) {
+ struct btd_adapter *adapter = l->data;
+
+ btd_adapter_set_did(adapter, vendor, product, version);
+ }
+}
diff --git a/src/manager.h b/src/manager.h
new file mode 100644
index 0000000..05c38b3
--- /dev/null
+++ b/src/manager.h
@@ -0,0 +1,43 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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
+ *
+ */
+
+#include <bluetooth/bluetooth.h>
+#include <dbus/dbus.h>
+
+#define MANAGER_INTERFACE "org.bluez.Manager"
+
+typedef void (*adapter_cb) (struct btd_adapter *adapter, gpointer user_data);
+
+dbus_bool_t manager_init(DBusConnection *conn, const char *path);
+void manager_cleanup(DBusConnection *conn, const char *path);
+
+const char *manager_get_base_path(void);
+struct btd_adapter *manager_find_adapter(const bdaddr_t *sba);
+struct btd_adapter *manager_find_adapter_by_id(int id);
+void manager_foreach_adapter(adapter_cb func, gpointer user_data);
+GSList *manager_get_adapters(void);
+struct btd_adapter *btd_manager_register_adapter(int id);
+int btd_manager_unregister_adapter(int id);
+void manager_add_adapter(const char *path);
+void btd_manager_set_did(uint16_t vendor, uint16_t product, uint16_t version);
diff --git a/src/oob.c b/src/oob.c
new file mode 100644
index 0000000..75798fb
--- /dev/null
+++ b/src/oob.c
@@ -0,0 +1,41 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 ST-Ericsson SA
+ *
+ * Author: Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson
+ *
+ *
+ * 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
+ *
+ */
+
+#include "adapter.h"
+#include "oob.h"
+
+static oob_read_cb_t local_oob_read_cb = NULL;
+
+void oob_register_cb(oob_read_cb_t cb)
+{
+ local_oob_read_cb = cb;
+}
+
+void oob_read_local_data_complete(struct btd_adapter *adapter, uint8_t *hash,
+ uint8_t *randomizer)
+{
+ if (local_oob_read_cb)
+ local_oob_read_cb(adapter, hash, randomizer);
+}
diff --git a/src/oob.h b/src/oob.h
new file mode 100644
index 0000000..5805082
--- /dev/null
+++ b/src/oob.h
@@ -0,0 +1,32 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 ST-Ericsson SA
+ *
+ * Author: Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson
+ *
+ *
+ * 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
+ *
+ */
+
+typedef void (*oob_read_cb_t) (struct btd_adapter *adapter, uint8_t *hash,
+ uint8_t *randomizer);
+
+void oob_register_cb(oob_read_cb_t cb);
+
+void oob_read_local_data_complete(struct btd_adapter *adapter, uint8_t *hash,
+ uint8_t *randomizer);
diff --git a/src/oui.c b/src/oui.c
new file mode 100644
index 0000000..80bb3d3
--- /dev/null
+++ b/src/oui.c
@@ -0,0 +1,101 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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 <sys/stat.h>
+#include <sys/mman.h>
+
+#include "oui.h"
+
+/* http://standards.ieee.org/regauth/oui/oui.txt */
+
+char *ouitocomp(const char *oui)
+{
+ struct stat st;
+ char *str, *map, *off, *end;
+ int fd;
+
+ fd = open(OUIFILE, O_RDONLY);
+ if (fd < 0)
+ return NULL;
+
+ if (fstat(fd, &st) < 0) {
+ close(fd);
+ return NULL;
+ }
+
+ str = malloc(128);
+ if (!str) {
+ close(fd);
+ return NULL;
+ }
+
+ memset(str, 0, 128);
+
+ map = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (!map || map == MAP_FAILED) {
+ free(str);
+ close(fd);
+ return NULL;
+ }
+
+ off = strstr(map, oui);
+ if (off) {
+ off += 18;
+ end = strpbrk(off, "\r\n");
+ strncpy(str, off, end - off);
+ } else {
+ free(str);
+ str = NULL;
+ }
+
+ munmap(map, st.st_size);
+
+ close(fd);
+
+ return str;
+}
+
+int oui2comp(const char *oui, char *comp, size_t size)
+{
+ char *tmp;
+
+ tmp = ouitocomp(oui);
+ if (!tmp)
+ return -1;
+
+ snprintf(comp, size, "%s", tmp);
+
+ free(tmp);
+
+ return 0;
+}
diff --git a/src/oui.h b/src/oui.h
new file mode 100644
index 0000000..ad66e56
--- /dev/null
+++ b/src/oui.h
@@ -0,0 +1,25 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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
+ *
+ */
+
+char *ouitocomp(const char *oui);
+int oui2comp(const char *oui, char *comp, size_t size);
diff --git a/src/plugin.c b/src/plugin.c
new file mode 100644
index 0000000..3506553
--- /dev/null
+++ b/src/plugin.c
@@ -0,0 +1,249 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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 <errno.h>
+#include <dlfcn.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <bluetooth/bluetooth.h>
+
+#include <glib.h>
+
+#include "plugin.h"
+#include "log.h"
+#include "hcid.h"
+#include "btio.h"
+
+static GSList *plugins = NULL;
+
+struct bluetooth_plugin {
+ void *handle;
+ gboolean active;
+ struct bluetooth_plugin_desc *desc;
+};
+
+static gint compare_priority(gconstpointer a, gconstpointer b)
+{
+ const struct bluetooth_plugin *plugin1 = a;
+ const struct bluetooth_plugin *plugin2 = b;
+
+ return plugin2->desc->priority - plugin1->desc->priority;
+}
+
+static gboolean add_plugin(void *handle, struct bluetooth_plugin_desc *desc)
+{
+ struct bluetooth_plugin *plugin;
+
+ if (desc->init == NULL)
+ return FALSE;
+
+ if (g_str_equal(desc->version, VERSION) == FALSE) {
+ error("Version mismatch for %s", desc->name);
+ return FALSE;
+ }
+
+ DBG("Loading %s plugin", desc->name);
+
+ plugin = g_try_new0(struct bluetooth_plugin, 1);
+ if (plugin == NULL)
+ return FALSE;
+
+ plugin->handle = handle;
+ plugin->active = FALSE;
+ plugin->desc = desc;
+
+ plugins = g_slist_insert_sorted(plugins, plugin, compare_priority);
+
+ return TRUE;
+}
+
+static gboolean enable_plugin(const char *name, char **conf_disable,
+ char **cli_enable, char **cli_disable)
+{
+ if (conf_disable) {
+ for (; *conf_disable; conf_disable++)
+ if (g_pattern_match_simple(*conf_disable, name))
+ break;
+ if (*conf_disable) {
+ info("Excluding (conf) %s", name);
+ return FALSE;
+ }
+ }
+
+ if (cli_disable) {
+ for (; *cli_disable; cli_disable++)
+ if (g_pattern_match_simple(*cli_disable, name))
+ break;
+ if (*cli_disable) {
+ info("Excluding (cli) %s", name);
+ return FALSE;
+ }
+ }
+
+ if (cli_enable) {
+ for (; *cli_enable; cli_enable++)
+ if (g_pattern_match_simple(*cli_enable, name))
+ break;
+ if (!*cli_enable) {
+ info("Ignoring (cli) %s", name);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+#include "builtin.h"
+
+gboolean plugin_init(GKeyFile *config, const char *enable, const char *disable)
+{
+ GSList *list;
+ GDir *dir;
+ const gchar *file;
+ char **conf_disabled, **cli_disabled, **cli_enabled;
+ unsigned int i;
+
+ /* Make a call to BtIO API so its symbols got resolved before the
+ * plugins are loaded. */
+ bt_io_error_quark();
+
+ if (config)
+ conf_disabled = g_key_file_get_string_list(config, "General",
+ "DisablePlugins",
+ NULL, NULL);
+ else
+ conf_disabled = NULL;
+
+ if (enable)
+ cli_enabled = g_strsplit_set(enable, ", ", -1);
+ else
+ cli_enabled = NULL;
+
+ if (disable)
+ cli_disabled = g_strsplit_set(disable, ", ", -1);
+ else
+ cli_disabled = NULL;
+
+ DBG("Loading builtin plugins");
+
+ for (i = 0; __bluetooth_builtin[i]; i++) {
+ if (!enable_plugin(__bluetooth_builtin[i]->name, conf_disabled,
+ cli_enabled, cli_disabled))
+ continue;
+
+ add_plugin(NULL, __bluetooth_builtin[i]);
+ }
+
+ if (strlen(PLUGINDIR) == 0)
+ goto start;
+
+ DBG("Loading plugins %s", PLUGINDIR);
+
+ dir = g_dir_open(PLUGINDIR, 0, NULL);
+ if (!dir)
+ goto start;
+
+ while ((file = g_dir_read_name(dir)) != NULL) {
+ struct bluetooth_plugin_desc *desc;
+ void *handle;
+ gchar *filename;
+
+ if (g_str_has_prefix(file, "lib") == TRUE ||
+ g_str_has_suffix(file, ".so") == FALSE)
+ continue;
+
+ filename = g_build_filename(PLUGINDIR, file, NULL);
+
+ handle = dlopen(filename, RTLD_NOW);
+ if (handle == NULL) {
+ error("Can't load plugin %s: %s", filename,
+ dlerror());
+ g_free(filename);
+ continue;
+ }
+
+ g_free(filename);
+
+ desc = dlsym(handle, "bluetooth_plugin_desc");
+ if (desc == NULL) {
+ error("Can't load plugin description: %s", dlerror());
+ dlclose(handle);
+ continue;
+ }
+
+ if (!enable_plugin(desc->name, conf_disabled,
+ cli_enabled, cli_disabled)) {
+ dlclose(handle);
+ continue;
+ }
+
+ if (add_plugin(handle, desc) == FALSE)
+ dlclose(handle);
+ }
+
+ g_dir_close(dir);
+
+start:
+ for (list = plugins; list; list = list->next) {
+ struct bluetooth_plugin *plugin = list->data;
+
+ if (plugin->desc->init() < 0) {
+ error("Failed to init %s plugin", plugin->desc->name);
+ continue;
+ }
+
+ plugin->active = TRUE;
+ }
+
+ g_strfreev(conf_disabled);
+ g_strfreev(cli_enabled);
+ g_strfreev(cli_disabled);
+
+ return TRUE;
+}
+
+void plugin_cleanup(void)
+{
+ GSList *list;
+
+ DBG("Cleanup plugins");
+
+ for (list = plugins; list; list = list->next) {
+ struct bluetooth_plugin *plugin = list->data;
+
+ if (plugin->active == TRUE && plugin->desc->exit)
+ plugin->desc->exit();
+
+ if (plugin->handle != NULL)
+ dlclose(plugin->handle);
+
+ g_free(plugin);
+ }
+
+ g_slist_free(plugins);
+}
diff --git a/src/plugin.h b/src/plugin.h
new file mode 100644
index 0000000..30bd415
--- /dev/null
+++ b/src/plugin.h
@@ -0,0 +1,47 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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
+ *
+ */
+#define BLUETOOTH_PLUGIN_PRIORITY_LOW -100
+#define BLUETOOTH_PLUGIN_PRIORITY_DEFAULT 0
+#define BLUETOOTH_PLUGIN_PRIORITY_HIGH 100
+
+struct bluetooth_plugin_desc {
+ const char *name;
+ const char *version;
+ int priority;
+ int (*init) (void);
+ void (*exit) (void);
+};
+
+#ifdef BLUETOOTH_PLUGIN_BUILTIN
+#define BLUETOOTH_PLUGIN_DEFINE(name, version, priority, init, exit) \
+ struct bluetooth_plugin_desc __bluetooth_builtin_ ## name = { \
+ #name, version, priority, init, exit \
+ };
+#else
+#define BLUETOOTH_PLUGIN_DEFINE(name, version, priority, init, exit) \
+ extern struct bluetooth_plugin_desc bluetooth_plugin_desc \
+ __attribute__ ((visibility("default"))); \
+ struct bluetooth_plugin_desc bluetooth_plugin_desc = { \
+ #name, version, priority, init, exit \
+ };
+#endif
diff --git a/src/ppoll.h b/src/ppoll.h
new file mode 100644
index 0000000..7d09d44
--- /dev/null
+++ b/src/ppoll.h
@@ -0,0 +1,16 @@
+#ifdef ppoll
+#undef ppoll
+#endif
+
+#define ppoll compat_ppoll
+
+static inline int compat_ppoll(struct pollfd *fds, nfds_t nfds,
+ const struct timespec *timeout, const sigset_t *sigmask)
+{
+ if (timeout == NULL)
+ return poll(fds, nfds, -1);
+ else if (timeout->tv_sec == 0)
+ return poll(fds, nfds, 500);
+ else
+ return poll(fds, nfds, timeout->tv_sec * 1000);
+}
diff --git a/src/rfkill.c b/src/rfkill.c
new file mode 100644
index 0000000..b40c6e7
--- /dev/null
+++ b/src/rfkill.c
@@ -0,0 +1,173 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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 <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include "log.h"
+#include "adapter.h"
+#include "manager.h"
+#include "hcid.h"
+
+enum rfkill_type {
+ RFKILL_TYPE_ALL = 0,
+ RFKILL_TYPE_WLAN,
+ RFKILL_TYPE_BLUETOOTH,
+ RFKILL_TYPE_UWB,
+ RFKILL_TYPE_WIMAX,
+ RFKILL_TYPE_WWAN,
+};
+
+enum rfkill_operation {
+ RFKILL_OP_ADD = 0,
+ RFKILL_OP_DEL,
+ RFKILL_OP_CHANGE,
+ RFKILL_OP_CHANGE_ALL,
+};
+
+struct rfkill_event {
+ uint32_t idx;
+ uint8_t type;
+ uint8_t op;
+ uint8_t soft;
+ uint8_t hard;
+};
+
+static gboolean rfkill_event(GIOChannel *chan,
+ GIOCondition cond, gpointer data)
+{
+ unsigned char buf[32];
+ struct rfkill_event *event = (void *) buf;
+ struct btd_adapter *adapter;
+ char sysname[PATH_MAX];
+ ssize_t len;
+ int fd, id;
+
+ if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
+ return FALSE;
+
+ fd = g_io_channel_unix_get_fd(chan);
+
+ memset(buf, 0, sizeof(buf));
+
+ len = read(fd, buf, sizeof(buf));
+ if (len < 0) {
+ if (errno == EAGAIN)
+ return TRUE;
+ return FALSE;
+ }
+
+ if (len != sizeof(struct rfkill_event))
+ return TRUE;
+
+ DBG("RFKILL event idx %u type %u op %u soft %u hard %u",
+ event->idx, event->type, event->op,
+ event->soft, event->hard);
+
+ if (event->soft || event->hard)
+ return TRUE;
+
+ if (event->op != RFKILL_OP_CHANGE)
+ return TRUE;
+
+ if (event->type != RFKILL_TYPE_BLUETOOTH &&
+ event->type != RFKILL_TYPE_ALL)
+ return TRUE;
+
+ snprintf(sysname, sizeof(sysname) - 1,
+ "/sys/class/rfkill/rfkill%u/name", event->idx);
+
+ fd = open(sysname, O_RDONLY);
+ if (fd < 0)
+ return TRUE;
+
+ memset(sysname, 0, sizeof(sysname));
+
+ if (read(fd, sysname, sizeof(sysname)) < 4) {
+ close(fd);
+ return TRUE;
+ }
+
+ close(fd);
+
+ if (g_str_has_prefix(sysname, "hci") == FALSE)
+ return TRUE;
+
+ id = atoi(sysname + 3);
+ if (id < 0)
+ return TRUE;
+
+ adapter = manager_find_adapter_by_id(id);
+ if (!adapter)
+ return TRUE;
+
+ DBG("RFKILL unblock for hci%d", id);
+
+ btd_adapter_restore_powered(adapter);
+
+ return TRUE;
+}
+
+static GIOChannel *channel = NULL;
+
+void rfkill_init(void)
+{
+ int fd;
+
+ if (!main_opts.remember_powered)
+ return;
+
+ fd = open("/dev/rfkill", O_RDWR);
+ if (fd < 0) {
+ error("Failed to open RFKILL control device");
+ return;
+ }
+
+ channel = g_io_channel_unix_new(fd);
+ g_io_channel_set_close_on_unref(channel, TRUE);
+
+ g_io_add_watch(channel, G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
+ rfkill_event, NULL);
+}
+
+void rfkill_exit(void)
+{
+ if (!channel)
+ return;
+
+ g_io_channel_shutdown(channel, TRUE, NULL);
+ g_io_channel_unref(channel);
+
+ channel = NULL;
+}
diff --git a/src/sdp-xml.c b/src/sdp-xml.c
new file mode 100644
index 0000000..3aa9df0
--- /dev/null
+++ b/src/sdp-xml.c
@@ -0,0 +1,789 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2005-2010 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
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <string.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include "sdp-xml.h"
+
+#define STRBUFSIZE 1024
+#define MAXINDENT 64
+
+static void convert_raw_data_to_xml(sdp_data_t *value, int indent_level,
+ void *data, void (*appender)(void *, const char *))
+{
+ int i, hex;
+ char buf[STRBUFSIZE];
+ char indent[MAXINDENT];
+ char next_indent[MAXINDENT];
+
+ if (!value)
+ return;
+
+ if (indent_level >= MAXINDENT)
+ indent_level = MAXINDENT - 2;
+
+ for (i = 0; i < indent_level; i++) {
+ indent[i] = '\t';
+ next_indent[i] = '\t';
+ }
+
+ indent[i] = '\0';
+ next_indent[i] = '\t';
+ next_indent[i + 1] = '\0';
+
+ buf[STRBUFSIZE - 1] = '\0';
+
+ switch (value->dtd) {
+ case SDP_DATA_NIL:
+ appender(data, indent);
+ appender(data, "<nil/>\n");
+ break;
+
+ case SDP_BOOL:
+ appender(data, indent);
+ appender(data, "<boolean value=\"");
+ appender(data, value->val.uint8 ? "true" : "false");
+ appender(data, "\" />\n");
+ break;
+
+ case SDP_UINT8:
+ appender(data, indent);
+ appender(data, "<uint8 value=\"");
+ snprintf(buf, STRBUFSIZE - 1, "0x%02x", value->val.uint8);
+ appender(data, buf);
+ appender(data, "\" />\n");
+ break;
+
+ case SDP_UINT16:
+ appender(data, indent);
+ appender(data, "<uint16 value=\"");
+ snprintf(buf, STRBUFSIZE - 1, "0x%04x", value->val.uint16);
+ appender(data, buf);
+ appender(data, "\" />\n");
+ break;
+
+ case SDP_UINT32:
+ appender(data, indent);
+ appender(data, "<uint32 value=\"");
+ snprintf(buf, STRBUFSIZE - 1, "0x%08x", value->val.uint32);
+ appender(data, buf);
+ appender(data, "\" />\n");
+ break;
+
+ case SDP_UINT64:
+ appender(data, indent);
+ appender(data, "<uint64 value=\"");
+ snprintf(buf, STRBUFSIZE - 1, "0x%016jx", value->val.uint64);
+ appender(data, buf);
+ appender(data, "\" />\n");
+ break;
+
+ case SDP_UINT128:
+ appender(data, indent);
+ appender(data, "<uint128 value=\"");
+
+ for (i = 0; i < 16; i++) {
+ sprintf(&buf[i * 2], "%02x",
+ (unsigned char) value->val.uint128.data[i]);
+ }
+
+ appender(data, buf);
+ appender(data, "\" />\n");
+ break;
+
+ case SDP_INT8:
+ appender(data, indent);
+ appender(data, "<int8 value=\"");
+ snprintf(buf, STRBUFSIZE - 1, "%d", value->val.int8);
+ appender(data, buf);
+ appender(data, "\" />\n");
+ break;
+
+ case SDP_INT16:
+ appender(data, indent);
+ appender(data, "<int16 value=\"");
+ snprintf(buf, STRBUFSIZE - 1, "%d", value->val.int16);
+ appender(data, buf);
+ appender(data, "\" />\n");
+ break;
+
+ case SDP_INT32:
+ appender(data, indent);
+ appender(data, "<int32 value=\"");
+ snprintf(buf, STRBUFSIZE - 1, "%d", value->val.int32);
+ appender(data, buf);
+ appender(data, "\" />\n");
+ break;
+
+ case SDP_INT64:
+ appender(data, indent);
+ appender(data, "<int64 value=\"");
+ snprintf(buf, STRBUFSIZE - 1, "%jd", value->val.int64);
+ appender(data, buf);
+ appender(data, "\" />\n");
+ break;
+
+ case SDP_INT128:
+ appender(data, indent);
+ appender(data, "<int128 value=\"");
+
+ for (i = 0; i < 16; i++) {
+ sprintf(&buf[i * 2], "%02x",
+ (unsigned char) value->val.int128.data[i]);
+ }
+ appender(data, buf);
+
+ appender(data, "\" />\n");
+ break;
+
+ case SDP_UUID16:
+ appender(data, indent);
+ appender(data, "<uuid value=\"");
+ snprintf(buf, STRBUFSIZE - 1, "0x%04x", value->val.uuid.value.uuid16);
+ appender(data, buf);
+ appender(data, "\" />\n");
+ break;
+
+ case SDP_UUID32:
+ appender(data, indent);
+ appender(data, "<uuid value=\"");
+ snprintf(buf, STRBUFSIZE - 1, "0x%08x", value->val.uuid.value.uuid32);
+ appender(data, buf);
+ appender(data, "\" />\n");
+ break;
+
+ case SDP_UUID128:
+ appender(data, indent);
+ appender(data, "<uuid value=\"");
+
+ snprintf(buf, STRBUFSIZE - 1,
+ "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ (unsigned char) value->val.uuid.value.
+ uuid128.data[0],
+ (unsigned char) value->val.uuid.value.
+ uuid128.data[1],
+ (unsigned char) value->val.uuid.value.
+ uuid128.data[2],
+ (unsigned char) value->val.uuid.value.
+ uuid128.data[3],
+ (unsigned char) value->val.uuid.value.
+ uuid128.data[4],
+ (unsigned char) value->val.uuid.value.
+ uuid128.data[5],
+ (unsigned char) value->val.uuid.value.
+ uuid128.data[6],
+ (unsigned char) value->val.uuid.value.
+ uuid128.data[7],
+ (unsigned char) value->val.uuid.value.
+ uuid128.data[8],
+ (unsigned char) value->val.uuid.value.
+ uuid128.data[9],
+ (unsigned char) value->val.uuid.value.
+ uuid128.data[10],
+ (unsigned char) value->val.uuid.value.
+ uuid128.data[11],
+ (unsigned char) value->val.uuid.value.
+ uuid128.data[12],
+ (unsigned char) value->val.uuid.value.
+ uuid128.data[13],
+ (unsigned char) value->val.uuid.value.
+ uuid128.data[14],
+ (unsigned char) value->val.uuid.value.
+ uuid128.data[15]);
+
+ appender(data, buf);
+ appender(data, "\" />\n");
+ break;
+
+ case SDP_TEXT_STR8:
+ case SDP_TEXT_STR16:
+ case SDP_TEXT_STR32:
+ {
+ int num_chars_to_escape = 0;
+ int length = value->unitSize - 1;
+ char *strBuf = 0;
+
+ hex = 0;
+
+ for (i = 0; i < length; i++) {
+ if (!isprint(value->val.str[i]) &&
+ value->val.str[i] != '\0') {
+ hex = 1;
+ break;
+ }
+
+ /* XML is evil, must do this... */
+ if ((value->val.str[i] == '<') ||
+ (value->val.str[i] == '>') ||
+ (value->val.str[i] == '"') ||
+ (value->val.str[i] == '&'))
+ num_chars_to_escape++;
+ }
+
+ appender(data, indent);
+
+ appender(data, "<text ");
+
+ if (hex) {
+ appender(data, "encoding=\"hex\" ");
+ strBuf = malloc(sizeof(char)
+ * ((value->unitSize-1) * 2 + 1));
+
+ /* Unit Size seems to include the size for dtd
+ It is thus off by 1
+ This is safe for Normal strings, but not
+ hex encoded data */
+ for (i = 0; i < (value->unitSize-1); i++)
+ sprintf(&strBuf[i*sizeof(char)*2],
+ "%02x",
+ (unsigned char) value->val.str[i]);
+
+ strBuf[(value->unitSize-1) * 2] = '\0';
+ }
+ else {
+ int j;
+ /* escape the XML disallowed chars */
+ strBuf = malloc(sizeof(char) *
+ (value->unitSize + 1 + num_chars_to_escape * 4));
+ for (i = 0, j = 0; i < length; i++) {
+ if (value->val.str[i] == '&') {
+ strBuf[j++] = '&';
+ strBuf[j++] = 'a';
+ strBuf[j++] = 'm';
+ strBuf[j++] = 'p';
+ }
+ else if (value->val.str[i] == '<') {
+ strBuf[j++] = '&';
+ strBuf[j++] = 'l';
+ strBuf[j++] = 't';
+ }
+ else if (value->val.str[i] == '>') {
+ strBuf[j++] = '&';
+ strBuf[j++] = 'g';
+ strBuf[j++] = 't';
+ }
+ else if (value->val.str[i] == '"') {
+ strBuf[j++] = '&';
+ strBuf[j++] = 'q';
+ strBuf[j++] = 'u';
+ strBuf[j++] = 'o';
+ strBuf[j++] = 't';
+ }
+ else if (value->val.str[i] == '\0') {
+ strBuf[j++] = ' ';
+ } else {
+ strBuf[j++] = value->val.str[i];
+ }
+ }
+
+ strBuf[j] = '\0';
+ }
+
+ appender(data, "value=\"");
+ appender(data, strBuf);
+ appender(data, "\" />\n");
+ free(strBuf);
+ break;
+ }
+
+ case SDP_URL_STR8:
+ case SDP_URL_STR16:
+ case SDP_URL_STR32:
+ {
+ char *strBuf;
+
+ appender(data, indent);
+ appender(data, "<url value=\"");
+ strBuf = strndup(value->val.str, value->unitSize - 1);
+ appender(data, strBuf);
+ free(strBuf);
+ appender(data, "\" />\n");
+ break;
+ }
+
+ case SDP_SEQ8:
+ case SDP_SEQ16:
+ case SDP_SEQ32:
+ appender(data, indent);
+ appender(data, "<sequence>\n");
+
+ convert_raw_data_to_xml(value->val.dataseq,
+ indent_level + 1, data, appender);
+
+ appender(data, indent);
+ appender(data, "</sequence>\n");
+
+ break;
+
+ case SDP_ALT8:
+ case SDP_ALT16:
+ case SDP_ALT32:
+ appender(data, indent);
+
+ appender(data, "<alternate>\n");
+
+ convert_raw_data_to_xml(value->val.dataseq,
+ indent_level + 1, data, appender);
+ appender(data, indent);
+
+ appender(data, "</alternate>\n");
+
+ break;
+ }
+
+ convert_raw_data_to_xml(value->next, indent_level, data, appender);
+}
+
+struct conversion_data {
+ void *data;
+ void (*appender)(void *data, const char *);
+};
+
+static void convert_raw_attr_to_xml_func(void *val, void *data)
+{
+ struct conversion_data *cd = data;
+ sdp_data_t *value = val;
+ char buf[STRBUFSIZE];
+
+ buf[STRBUFSIZE - 1] = '\0';
+ snprintf(buf, STRBUFSIZE - 1, "\t<attribute id=\"0x%04x\">\n",
+ value->attrId);
+ cd->appender(cd->data, buf);
+
+ if (data)
+ convert_raw_data_to_xml(value, 2, cd->data, cd->appender);
+ else
+ cd->appender(cd->data, "\t\tNULL\n");
+
+ cd->appender(cd->data, "\t</attribute>\n");
+}
+
+/*
+ * Will convert the sdp record to XML. The appender and data can be used
+ * to control where to output the record (e.g. file or a data buffer). The
+ * appender will be called repeatedly with data and the character buffer
+ * (containing parts of the generated XML) to append.
+ */
+void convert_sdp_record_to_xml(sdp_record_t *rec,
+ void *data, void (*appender)(void *, const char *))
+{
+ struct conversion_data cd;
+
+ cd.data = data;
+ cd.appender = appender;
+
+ if (rec && rec->attrlist) {
+ appender(data, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n\n");
+ appender(data, "<record>\n");
+ sdp_list_foreach(rec->attrlist,
+ convert_raw_attr_to_xml_func, &cd);
+ appender(data, "</record>\n");
+ }
+}
+
+static sdp_data_t *sdp_xml_parse_uuid128(const char *data)
+{
+ uint128_t val;
+ unsigned int i, j;
+
+ char buf[3];
+
+ memset(&val, 0, sizeof(val));
+
+ buf[2] = '\0';
+
+ for (j = 0, i = 0; i < strlen(data);) {
+ if (data[i] == '-') {
+ i++;
+ continue;
+ }
+
+ buf[0] = data[i];
+ buf[1] = data[i + 1];
+
+ val.data[j++] = strtoul(buf, 0, 16);
+ i += 2;
+ }
+
+ return sdp_data_alloc(SDP_UUID128, &val);
+}
+
+sdp_data_t *sdp_xml_parse_uuid(const char *data, sdp_record_t *record)
+{
+ sdp_data_t *ret;
+ char *endptr;
+ uint32_t val;
+ uint16_t val2;
+ int len;
+
+ len = strlen(data);
+
+ if (len == 36) {
+ ret = sdp_xml_parse_uuid128(data);
+ goto result;
+ }
+
+ val = strtoll(data, &endptr, 16);
+
+ /* Couldn't parse */
+ if (*endptr != '\0')
+ return NULL;
+
+ if (val > USHRT_MAX) {
+ ret = sdp_data_alloc(SDP_UUID32, &val);
+ goto result;
+ }
+
+ val2 = val;
+
+ ret = sdp_data_alloc(SDP_UUID16, &val2);
+
+result:
+ if (record && ret)
+ sdp_pattern_add_uuid(record, &ret->val.uuid);
+
+ return ret;
+}
+
+sdp_data_t *sdp_xml_parse_int(const char * data, uint8_t dtd)
+{
+ char *endptr;
+ sdp_data_t *ret = NULL;
+
+ switch (dtd) {
+ case SDP_BOOL:
+ {
+ uint8_t val = 0;
+
+ if (!strcmp("true", data)) {
+ val = 1;
+ }
+
+ else if (!strcmp("false", data)) {
+ val = 0;
+ }
+ else {
+ return NULL;
+ }
+
+ ret = sdp_data_alloc(dtd, &val);
+ break;
+ }
+
+ case SDP_INT8:
+ {
+ int8_t val = strtoul(data, &endptr, 0);
+
+ /* Failed to parse */
+ if ((endptr != data) && (*endptr != '\0'))
+ return NULL;
+
+ ret = sdp_data_alloc(dtd, &val);
+ break;
+ }
+
+ case SDP_UINT8:
+ {
+ uint8_t val = strtoul(data, &endptr, 0);
+
+ /* Failed to parse */
+ if ((endptr != data) && (*endptr != '\0'))
+ return NULL;
+
+ ret = sdp_data_alloc(dtd, &val);
+ break;
+ }
+
+ case SDP_INT16:
+ {
+ int16_t val = strtoul(data, &endptr, 0);
+
+ /* Failed to parse */
+ if ((endptr != data) && (*endptr != '\0'))
+ return NULL;
+
+ ret = sdp_data_alloc(dtd, &val);
+ break;
+ }
+
+ case SDP_UINT16:
+ {
+ uint16_t val = strtoul(data, &endptr, 0);
+
+ /* Failed to parse */
+ if ((endptr != data) && (*endptr != '\0'))
+ return NULL;
+
+ ret = sdp_data_alloc(dtd, &val);
+ break;
+ }
+
+ case SDP_INT32:
+ {
+ int32_t val = strtoul(data, &endptr, 0);
+
+ /* Failed to parse */
+ if ((endptr != data) && (*endptr != '\0'))
+ return NULL;
+
+ ret = sdp_data_alloc(dtd, &val);
+ break;
+ }
+
+ case SDP_UINT32:
+ {
+ uint32_t val = strtoul(data, &endptr, 0);
+
+ /* Failed to parse */
+ if ((endptr != data) && (*endptr != '\0'))
+ return NULL;
+
+ ret = sdp_data_alloc(dtd, &val);
+ break;
+ }
+
+ case SDP_INT64:
+ {
+ int64_t val = strtoull(data, &endptr, 0);
+
+ /* Failed to parse */
+ if ((endptr != data) && (*endptr != '\0'))
+ return NULL;
+
+ ret = sdp_data_alloc(dtd, &val);
+ break;
+ }
+
+ case SDP_UINT64:
+ {
+ uint64_t val = strtoull(data, &endptr, 0);
+
+ /* Failed to parse */
+ if ((endptr != data) && (*endptr != '\0'))
+ return NULL;
+
+ ret = sdp_data_alloc(dtd, &val);
+ break;
+ }
+
+ case SDP_INT128:
+ case SDP_UINT128:
+ {
+ uint128_t val;
+ int i = 0;
+ char buf[3];
+
+ buf[2] = '\0';
+
+ for (; i < 32; i += 2) {
+ buf[0] = data[i];
+ buf[1] = data[i + 1];
+
+ val.data[i >> 1] = strtoul(buf, 0, 16);
+ }
+
+ ret = sdp_data_alloc(dtd, &val);
+ break;
+ }
+
+ };
+
+ return ret;
+}
+
+static char *sdp_xml_parse_string_decode(const char *data, char encoding, uint32_t *length)
+{
+ int len = strlen(data);
+ char *text;
+
+ if (encoding == SDP_XML_ENCODING_NORMAL) {
+ text = strdup(data);
+ *length = len;
+ } else {
+ char buf[3], *decoded;
+ int i;
+
+ decoded = malloc((len >> 1) + 1);
+
+ /* Ensure the string is a power of 2 */
+ len = (len >> 1) << 1;
+
+ buf[2] = '\0';
+
+ for (i = 0; i < len; i += 2) {
+ buf[0] = data[i];
+ buf[1] = data[i + 1];
+
+ decoded[i >> 1] = strtoul(buf, 0, 16);
+ }
+
+ decoded[len >> 1] = '\0';
+ text = decoded;
+ *length = len >> 1;
+ }
+
+ return text;
+}
+
+sdp_data_t *sdp_xml_parse_url(const char *data)
+{
+ uint8_t dtd = SDP_URL_STR8;
+ char *url;
+ uint32_t length;
+ sdp_data_t *ret;
+
+ url = sdp_xml_parse_string_decode(data,
+ SDP_XML_ENCODING_NORMAL, &length);
+
+ if (length > UCHAR_MAX)
+ dtd = SDP_URL_STR16;
+
+ ret = sdp_data_alloc_with_length(dtd, url, length);
+
+ free(url);
+
+ return ret;
+}
+
+sdp_data_t *sdp_xml_parse_text(const char *data, char encoding)
+{
+ uint8_t dtd = SDP_TEXT_STR8;
+ char *text;
+ uint32_t length;
+ sdp_data_t *ret;
+
+ text = sdp_xml_parse_string_decode(data, encoding, &length);
+
+ if (length > UCHAR_MAX)
+ dtd = SDP_TEXT_STR16;
+
+ ret = sdp_data_alloc_with_length(dtd, text, length);
+
+ free(text);
+
+ return ret;
+}
+
+sdp_data_t *sdp_xml_parse_nil(const char *data)
+{
+ return sdp_data_alloc(SDP_DATA_NIL, 0);
+}
+
+#define DEFAULT_XML_DATA_SIZE 1024
+
+struct sdp_xml_data *sdp_xml_data_alloc()
+{
+ struct sdp_xml_data *elem;
+
+ elem = malloc(sizeof(struct sdp_xml_data));
+ if (!elem)
+ return NULL;
+
+ memset(elem, 0, sizeof(struct sdp_xml_data));
+
+ /* Null terminate the text */
+ elem->size = DEFAULT_XML_DATA_SIZE;
+ elem->text = malloc(DEFAULT_XML_DATA_SIZE);
+ elem->text[0] = '\0';
+
+ return elem;
+}
+
+void sdp_xml_data_free(struct sdp_xml_data *elem)
+{
+ if (elem->data)
+ sdp_data_free(elem->data);
+
+ free(elem->name);
+ free(elem->text);
+ free(elem);
+}
+
+struct sdp_xml_data *sdp_xml_data_expand(struct sdp_xml_data *elem)
+{
+ char *newbuf;
+
+ newbuf = malloc(elem->size * 2);
+ if (!newbuf)
+ return NULL;
+
+ memcpy(newbuf, elem->text, elem->size);
+ elem->size *= 2;
+ free(elem->text);
+
+ elem->text = newbuf;
+
+ return elem;
+}
+
+sdp_data_t *sdp_xml_parse_datatype(const char *el, struct sdp_xml_data *elem,
+ sdp_record_t *record)
+{
+ const char *data = elem->text;
+
+ if (!strcmp(el, "boolean"))
+ return sdp_xml_parse_int(data, SDP_BOOL);
+ else if (!strcmp(el, "uint8"))
+ return sdp_xml_parse_int(data, SDP_UINT8);
+ else if (!strcmp(el, "uint16"))
+ return sdp_xml_parse_int(data, SDP_UINT16);
+ else if (!strcmp(el, "uint32"))
+ return sdp_xml_parse_int(data, SDP_UINT32);
+ else if (!strcmp(el, "uint64"))
+ return sdp_xml_parse_int(data, SDP_UINT64);
+ else if (!strcmp(el, "uint128"))
+ return sdp_xml_parse_int(data, SDP_UINT128);
+ else if (!strcmp(el, "int8"))
+ return sdp_xml_parse_int(data, SDP_INT8);
+ else if (!strcmp(el, "int16"))
+ return sdp_xml_parse_int(data, SDP_INT16);
+ else if (!strcmp(el, "int32"))
+ return sdp_xml_parse_int(data, SDP_INT32);
+ else if (!strcmp(el, "int64"))
+ return sdp_xml_parse_int(data, SDP_INT64);
+ else if (!strcmp(el, "int128"))
+ return sdp_xml_parse_int(data, SDP_INT128);
+ else if (!strcmp(el, "uuid"))
+ return sdp_xml_parse_uuid(data, record);
+ else if (!strcmp(el, "url"))
+ return sdp_xml_parse_url(data);
+ else if (!strcmp(el, "text"))
+ return sdp_xml_parse_text(data, elem->type);
+ else if (!strcmp(el, "nil"))
+ return sdp_xml_parse_nil(data);
+
+ return NULL;
+}
diff --git a/src/sdp-xml.h b/src/sdp-xml.h
new file mode 100644
index 0000000..7031276
--- /dev/null
+++ b/src/sdp-xml.h
@@ -0,0 +1,59 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2005-2010 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
+ *
+ */
+
+
+#ifndef __SDP_XML_H
+#define __SDP_XML_H
+
+#include <bluetooth/sdp.h>
+
+#define SDP_XML_ENCODING_NORMAL 0
+#define SDP_XML_ENCODING_HEX 1
+
+void convert_sdp_record_to_xml(sdp_record_t *rec,
+ void *user_data, void (*append_func) (void *, const char *));
+
+sdp_data_t *sdp_xml_parse_nil(const char *data);
+sdp_data_t *sdp_xml_parse_text(const char *data, char encoding);
+sdp_data_t *sdp_xml_parse_url(const char *data);
+sdp_data_t *sdp_xml_parse_int(const char *data, uint8_t dtd);
+sdp_data_t *sdp_xml_parse_uuid(const char *data, sdp_record_t *record);
+
+struct sdp_xml_data {
+ char *text; /* Pointer to the current buffer */
+ int size; /* Size of the current buffer */
+ sdp_data_t *data; /* The current item being built */
+ struct sdp_xml_data *next; /* Next item on the stack */
+ char type; /* 0 = Text or Hexadecimal */
+ char *name; /* Name, optional in the dtd */
+ /* TODO: What is it used for? */
+};
+
+struct sdp_xml_data *sdp_xml_data_alloc();
+void sdp_xml_data_free(struct sdp_xml_data *elem);
+struct sdp_xml_data *sdp_xml_data_expand(struct sdp_xml_data *elem);
+
+sdp_data_t *sdp_xml_parse_datatype(const char *el, struct sdp_xml_data *elem,
+ sdp_record_t *record);
+
+#endif /* __SDP_XML_H */
diff --git a/src/sdpd-database.c b/src/sdpd-database.c
new file mode 100644
index 0000000..08f542f
--- /dev/null
+++ b/src/sdpd-database.c
@@ -0,0 +1,346 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2001-2002 Nokia Corporation
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2002-2003 Stephen Crane <steve.crane@rococosoft.com>
+ *
+ *
+ * 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 <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include "sdpd.h"
+#include "log.h"
+#include "adapter.h"
+#include "manager.h"
+
+static sdp_list_t *service_db;
+static sdp_list_t *access_db;
+
+typedef struct {
+ uint32_t handle;
+ bdaddr_t device;
+} sdp_access_t;
+
+/*
+ * Ordering function called when inserting a service record.
+ * The service repository is a linked list in sorted order
+ * and the service record handle is the sort key
+ */
+int record_sort(const void *r1, const void *r2)
+{
+ const sdp_record_t *rec1 = r1;
+ const sdp_record_t *rec2 = r2;
+
+ if (!rec1 || !rec2) {
+ error("NULL RECORD LIST FATAL");
+ return -1;
+ }
+
+ return rec1->handle - rec2->handle;
+}
+
+static int access_sort(const void *r1, const void *r2)
+{
+ const sdp_access_t *rec1 = r1;
+ const sdp_access_t *rec2 = r2;
+
+ if (!rec1 || !rec2) {
+ error("NULL RECORD LIST FATAL");
+ return -1;
+ }
+
+ return rec1->handle - rec2->handle;
+}
+
+static void access_free(void *p)
+{
+ free(p);
+}
+
+/*
+ * Reset the service repository by deleting its contents
+ */
+void sdp_svcdb_reset()
+{
+ sdp_list_free(service_db, (sdp_free_func_t) sdp_record_free);
+ sdp_list_free(access_db, access_free);
+}
+
+typedef struct _indexed {
+ int sock;
+ sdp_record_t *record;
+} sdp_indexed_t;
+
+static sdp_list_t *socket_index;
+
+/*
+ * collect all services registered over this socket
+ */
+void sdp_svcdb_collect_all(int sock)
+{
+ sdp_list_t *p, *q;
+
+ for (p = socket_index, q = 0; p; ) {
+ sdp_indexed_t *item = p->data;
+ if (item->sock == sock) {
+ sdp_list_t *next = p->next;
+ sdp_record_remove(item->record->handle);
+ sdp_record_free(item->record);
+ free(item);
+ if (q)
+ q->next = next;
+ else
+ socket_index = next;
+ free(p);
+ p = next;
+ } else if (item->sock > sock)
+ return;
+ else {
+ q = p;
+ p = p->next;
+ }
+ }
+}
+
+void sdp_svcdb_collect(sdp_record_t *rec)
+{
+ sdp_list_t *p, *q;
+
+ for (p = socket_index, q = 0; p; q = p, p = p->next) {
+ sdp_indexed_t *item = p->data;
+ if (rec == item->record) {
+ free(item);
+ if (q)
+ q->next = p->next;
+ else
+ socket_index = p->next;
+ free(p);
+ return;
+ }
+ }
+}
+
+static int compare_indices(const void *i1, const void *i2)
+{
+ const sdp_indexed_t *s1 = i1;
+ const sdp_indexed_t *s2 = i2;
+ return s1->sock - s2->sock;
+}
+
+void sdp_svcdb_set_collectable(sdp_record_t *record, int sock)
+{
+ sdp_indexed_t *item = malloc(sizeof(sdp_indexed_t));
+ item->sock = sock;
+ item->record = record;
+ socket_index = sdp_list_insert_sorted(socket_index, item, compare_indices);
+}
+
+/*
+ * Add a service record to the repository
+ */
+void sdp_record_add(const bdaddr_t *device, sdp_record_t *rec)
+{
+ struct btd_adapter *adapter;
+ sdp_access_t *dev;
+
+ SDPDBG("Adding rec : 0x%lx", (long) rec);
+ SDPDBG("with handle : 0x%x", rec->handle);
+
+ service_db = sdp_list_insert_sorted(service_db, rec, record_sort);
+
+ dev = malloc(sizeof(*dev));
+ if (!dev)
+ return;
+
+ bacpy(&dev->device, device);
+ dev->handle = rec->handle;
+
+ access_db = sdp_list_insert_sorted(access_db, dev, access_sort);
+
+ if (bacmp(device, BDADDR_ANY) == 0) {
+ manager_foreach_adapter(adapter_service_insert, rec);
+ return;
+ }
+
+ adapter = manager_find_adapter(device);
+ if (adapter)
+ adapter_service_insert(adapter, rec);
+}
+
+static sdp_list_t *record_locate(uint32_t handle)
+{
+ if (service_db) {
+ sdp_list_t *p;
+ sdp_record_t r;
+
+ r.handle = handle;
+ p = sdp_list_find(service_db, &r, record_sort);
+ return p;
+ }
+
+ SDPDBG("Could not find svcRec for : 0x%x", handle);
+ return NULL;
+}
+
+static sdp_list_t *access_locate(uint32_t handle)
+{
+ if (access_db) {
+ sdp_list_t *p;
+ sdp_access_t a;
+
+ a.handle = handle;
+ p = sdp_list_find(access_db, &a, access_sort);
+ return p;
+ }
+
+ SDPDBG("Could not find access data for : 0x%x", handle);
+ return NULL;
+}
+
+/*
+ * Given a service record handle, find the record associated with it.
+ */
+sdp_record_t *sdp_record_find(uint32_t handle)
+{
+ sdp_list_t *p = record_locate(handle);
+
+ if (!p) {
+ SDPDBG("Couldn't find record for : 0x%x", handle);
+ return 0;
+ }
+
+ return p->data;
+}
+
+/*
+ * Given a service record handle, remove its record from the repository
+ */
+int sdp_record_remove(uint32_t handle)
+{
+ sdp_list_t *p = record_locate(handle);
+ sdp_record_t *r;
+ sdp_access_t *a;
+
+ if (!p) {
+ error("Remove : Couldn't find record for : 0x%x", handle);
+ return -1;
+ }
+
+ r = p->data;
+ if (r)
+ service_db = sdp_list_remove(service_db, r);
+
+ p = access_locate(handle);
+ if (p == NULL || p->data == NULL)
+ return 0;
+
+ a = p->data;
+
+ if (bacmp(&a->device, BDADDR_ANY) != 0) {
+ struct btd_adapter *adapter = manager_find_adapter(&a->device);
+ if (adapter)
+ adapter_service_remove(adapter, r);
+ } else
+ manager_foreach_adapter(adapter_service_remove, r);
+
+ access_db = sdp_list_remove(access_db, a);
+ access_free(a);
+
+ return 0;
+}
+
+/*
+ * Return a pointer to the linked list containing the records in sorted order
+ */
+sdp_list_t *sdp_get_record_list(void)
+{
+ return service_db;
+}
+
+sdp_list_t *sdp_get_access_list(void)
+{
+ return access_db;
+}
+
+int sdp_check_access(uint32_t handle, bdaddr_t *device)
+{
+ sdp_list_t *p = access_locate(handle);
+ sdp_access_t *a;
+
+ if (!p)
+ return 1;
+
+ a = p->data;
+ if (!a)
+ return 1;
+
+ if (bacmp(&a->device, device) &&
+ bacmp(&a->device, BDADDR_ANY) &&
+ bacmp(device, BDADDR_ANY))
+ return 0;
+
+ return 1;
+}
+
+uint32_t sdp_next_handle(void)
+{
+ uint32_t handle = 0x10000;
+
+ while (sdp_record_find(handle))
+ handle++;
+
+ return handle;
+}
+
+void sdp_init_services_list(bdaddr_t *device)
+{
+ sdp_list_t *p;
+
+ DBG("");
+
+ for (p = access_db; p != NULL; p = p->next) {
+ sdp_access_t *access = p->data;
+ sdp_record_t *rec;
+
+ if (bacmp(BDADDR_ANY, &access->device))
+ continue;
+
+ rec = sdp_record_find(access->handle);
+ if (rec == NULL)
+ continue;
+
+ SDPDBG("adding record with handle %x", access->handle);
+
+ manager_foreach_adapter(adapter_service_insert, rec);
+ }
+}
diff --git a/src/sdpd-request.c b/src/sdpd-request.c
new file mode 100644
index 0000000..1722f78
--- /dev/null
+++ b/src/sdpd-request.c
@@ -0,0 +1,1094 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2001-2002 Nokia Corporation
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2002-2003 Stephen Crane <steve.crane@rococosoft.com>
+ *
+ *
+ * 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 <string.h>
+#include <limits.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <netinet/in.h>
+
+#include "sdpd.h"
+#include "log.h"
+
+typedef struct {
+ uint32_t timestamp;
+ union {
+ uint16_t maxBytesSent;
+ uint16_t lastIndexSent;
+ } cStateValue;
+} sdp_cont_state_t;
+
+#define SDP_CONT_STATE_SIZE (sizeof(uint8_t) + sizeof(sdp_cont_state_t))
+
+#define MIN(x, y) ((x) < (y)) ? (x): (y)
+
+typedef struct _sdp_cstate_list sdp_cstate_list_t;
+
+struct _sdp_cstate_list {
+ sdp_cstate_list_t *next;
+ uint32_t timestamp;
+ sdp_buf_t buf;
+};
+
+static sdp_cstate_list_t *cstates;
+
+// FIXME: should probably remove it when it's found
+static sdp_buf_t *sdp_get_cached_rsp(sdp_cont_state_t *cstate)
+{
+ sdp_cstate_list_t *p;
+
+ for (p = cstates; p; p = p->next)
+ if (p->timestamp == cstate->timestamp)
+ return &p->buf;
+ return 0;
+}
+
+static uint32_t sdp_cstate_alloc_buf(sdp_buf_t *buf)
+{
+ sdp_cstate_list_t *cstate = malloc(sizeof(sdp_cstate_list_t));
+ uint8_t *data = malloc(buf->data_size);
+
+ memcpy(data, buf->data, buf->data_size);
+ memset((char *)cstate, 0, sizeof(sdp_cstate_list_t));
+ cstate->buf.data = data;
+ cstate->buf.data_size = buf->data_size;
+ cstate->buf.buf_size = buf->data_size;
+ cstate->timestamp = sdp_get_time();
+ cstate->next = cstates;
+ cstates = cstate;
+ return cstate->timestamp;
+}
+
+/* Additional values for checking datatype (not in spec) */
+#define SDP_TYPE_UUID 0xfe
+#define SDP_TYPE_ATTRID 0xff
+
+struct attrid {
+ uint8_t dtd;
+ union {
+ uint16_t uint16;
+ uint32_t uint32;
+ };
+};
+
+/*
+ * Generic data element sequence extractor. Builds
+ * a list whose elements are those found in the
+ * sequence. The data type of elements found in the
+ * sequence is returned in the reference pDataType
+ */
+static int extract_des(uint8_t *buf, int len, sdp_list_t **svcReqSeq, uint8_t *pDataType, uint8_t expectedType)
+{
+ uint8_t seqType;
+ int scanned, data_size = 0;
+ short numberOfElements = 0;
+ int seqlen = 0;
+ sdp_list_t *pSeq = NULL;
+ uint8_t dataType;
+ int status = 0;
+ const uint8_t *p;
+ size_t bufsize;
+
+ scanned = sdp_extract_seqtype(buf, len, &seqType, &data_size);
+
+ SDPDBG("Seq type : %d", seqType);
+ if (!scanned || (seqType != SDP_SEQ8 && seqType != SDP_SEQ16)) {
+ error("Unknown seq type");
+ return -1;
+ }
+ p = buf + scanned;
+ bufsize = len - scanned;
+
+ SDPDBG("Data size : %d", data_size);
+
+ for (;;) {
+ char *pElem = NULL;
+ int localSeqLength = 0;
+
+ if (bufsize < sizeof(uint8_t)) {
+ SDPDBG("->Unexpected end of buffer");
+ goto failed;
+ }
+
+ dataType = *p;
+
+ SDPDBG("Data type: 0x%02x", dataType);
+
+ if (expectedType == SDP_TYPE_UUID) {
+ if (dataType != SDP_UUID16 && dataType != SDP_UUID32 && dataType != SDP_UUID128) {
+ SDPDBG("->Unexpected Data type (expected UUID_ANY)");
+ goto failed;
+ }
+ } else if (expectedType == SDP_TYPE_ATTRID &&
+ (dataType != SDP_UINT16 && dataType != SDP_UINT32)) {
+ SDPDBG("->Unexpected Data type (expected 0x%02x or 0x%02x)",
+ SDP_UINT16, SDP_UINT32);
+ goto failed;
+ } else if (expectedType != SDP_TYPE_ATTRID && dataType != expectedType) {
+ SDPDBG("->Unexpected Data type (expected 0x%02x)", expectedType);
+ goto failed;
+ }
+
+ switch (dataType) {
+ case SDP_UINT16:
+ p += sizeof(uint8_t);
+ seqlen += sizeof(uint8_t);
+ bufsize -= sizeof(uint8_t);
+ if (bufsize < sizeof(uint16_t)) {
+ SDPDBG("->Unexpected end of buffer");
+ goto failed;
+ }
+
+ if (expectedType == SDP_TYPE_ATTRID) {
+ struct attrid *aid;
+ aid = malloc(sizeof(struct attrid));
+ aid->dtd = dataType;
+ bt_put_unaligned(ntohs(bt_get_unaligned((uint16_t *)p)), (uint16_t *)&aid->uint16);
+ pElem = (char *) aid;
+ } else {
+ pElem = malloc(sizeof(uint16_t));
+ bt_put_unaligned(ntohs(bt_get_unaligned((uint16_t *)p)), (uint16_t *)pElem);
+ }
+ p += sizeof(uint16_t);
+ seqlen += sizeof(uint16_t);
+ bufsize -= sizeof(uint16_t);
+ break;
+ case SDP_UINT32:
+ p += sizeof(uint8_t);
+ seqlen += sizeof(uint8_t);
+ bufsize -= sizeof(uint8_t);
+ if (bufsize < (int)sizeof(uint32_t)) {
+ SDPDBG("->Unexpected end of buffer");
+ goto failed;
+ }
+
+ if (expectedType == SDP_TYPE_ATTRID) {
+ struct attrid *aid;
+ aid = malloc(sizeof(struct attrid));
+ aid->dtd = dataType;
+ bt_put_unaligned(ntohl(bt_get_unaligned((uint32_t *)p)), (uint32_t *)&aid->uint32);
+ pElem = (char *) aid;
+ } else {
+ pElem = malloc(sizeof(uint32_t));
+ bt_put_unaligned(ntohl(bt_get_unaligned((uint32_t *)p)), (uint32_t *)pElem);
+ }
+ p += sizeof(uint32_t);
+ seqlen += sizeof(uint32_t);
+ bufsize -= sizeof(uint32_t);
+ break;
+ case SDP_UUID16:
+ case SDP_UUID32:
+ case SDP_UUID128:
+ pElem = malloc(sizeof(uuid_t));
+ status = sdp_uuid_extract(p, bufsize, (uuid_t *) pElem, &localSeqLength);
+ if (status < 0) {
+ free(pElem);
+ goto failed;
+ }
+ seqlen += localSeqLength;
+ p += localSeqLength;
+ bufsize -= localSeqLength;
+ break;
+ default:
+ return -1;
+ }
+ if (status == 0) {
+ pSeq = sdp_list_append(pSeq, pElem);
+ numberOfElements++;
+ SDPDBG("No of elements : %d", numberOfElements);
+
+ if (seqlen == data_size)
+ break;
+ else if (seqlen > data_size || seqlen > len)
+ goto failed;
+ } else
+ free(pElem);
+ }
+ *svcReqSeq = pSeq;
+ scanned += seqlen;
+ *pDataType = dataType;
+ return scanned;
+
+failed:
+ sdp_list_free(pSeq, free);
+ return -1;
+}
+
+static int sdp_set_cstate_pdu(sdp_buf_t *buf, sdp_cont_state_t *cstate)
+{
+ uint8_t *pdata = buf->data + buf->data_size;
+ int length = 0;
+
+ if (cstate) {
+ SDPDBG("Non null sdp_cstate_t id : 0x%x", cstate->timestamp);
+ *(uint8_t *)pdata = sizeof(sdp_cont_state_t);
+ pdata += sizeof(uint8_t);
+ length += sizeof(uint8_t);
+ memcpy(pdata, cstate, sizeof(sdp_cont_state_t));
+ length += sizeof(sdp_cont_state_t);
+ } else {
+ // set "null" continuation state
+ *(uint8_t *)pdata = 0;
+ pdata += sizeof(uint8_t);
+ length += sizeof(uint8_t);
+ }
+ buf->data_size += length;
+ return length;
+}
+
+static int sdp_cstate_get(uint8_t *buffer, size_t len,
+ sdp_cont_state_t **cstate)
+{
+ uint8_t cStateSize = *buffer;
+
+ SDPDBG("Continuation State size : %d", cStateSize);
+
+ if (cStateSize == 0) {
+ *cstate = NULL;
+ return 0;
+ }
+
+ buffer++;
+ len--;
+
+ if (len < sizeof(sdp_cont_state_t))
+ return -EINVAL;
+
+ /*
+ * Check if continuation state exists, if yes attempt
+ * to get response remainder from cache, else send error
+ */
+
+ *cstate = malloc(sizeof(sdp_cont_state_t));
+ if (!(*cstate))
+ return -ENOMEM;
+
+ memcpy(*cstate, buffer, sizeof(sdp_cont_state_t));
+
+ SDPDBG("Cstate TS : 0x%x", (*cstate)->timestamp);
+ SDPDBG("Bytes sent : %d", (*cstate)->cStateValue.maxBytesSent);
+
+ return 0;
+}
+
+/*
+ * The matching process is defined as "each and every UUID
+ * specified in the "search pattern" must be present in the
+ * "target pattern". Here "search pattern" is the set of UUIDs
+ * specified by the service discovery client and "target pattern"
+ * is the set of UUIDs present in a service record.
+ *
+ * Return 1 if each and every UUID in the search
+ * pattern exists in the target pattern, 0 if the
+ * match succeeds and -1 on error.
+ */
+static int sdp_match_uuid(sdp_list_t *search, sdp_list_t *pattern)
+{
+ /*
+ * The target is a sorted list, so we need not look
+ * at all elements to confirm existence of an element
+ * from the search pattern
+ */
+ int patlen = sdp_list_len(pattern);
+
+ if (patlen < sdp_list_len(search))
+ return -1;
+ for (; search; search = search->next) {
+ uuid_t *uuid128;
+ void *data = search->data;
+ sdp_list_t *list;
+ if (data == NULL)
+ return -1;
+
+ // create 128-bit form of the search UUID
+ uuid128 = sdp_uuid_to_uuid128((uuid_t *)data);
+ list = sdp_list_find(pattern, uuid128, sdp_uuid128_cmp);
+ bt_free(uuid128);
+ if (!list)
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Service search request PDU. This method extracts the search pattern
+ * (a sequence of UUIDs) and calls the matching function
+ * to find matching services
+ */
+static int service_search_req(sdp_req_t *req, sdp_buf_t *buf)
+{
+ int status = 0, i, plen, mlen, mtu, scanned;
+ sdp_list_t *pattern = NULL;
+ uint16_t expected, actual, rsp_count = 0;
+ uint8_t dtd;
+ sdp_cont_state_t *cstate = NULL;
+ uint8_t *pCacheBuffer = NULL;
+ int handleSize = 0;
+ uint32_t cStateId = 0;
+ short *pTotalRecordCount, *pCurrentRecordCount;
+ uint8_t *pdata = req->buf + sizeof(sdp_pdu_hdr_t);
+ size_t data_left = req->len - sizeof(sdp_pdu_hdr_t);
+
+ scanned = extract_des(pdata, data_left, &pattern, &dtd, SDP_TYPE_UUID);
+
+ if (scanned == -1) {
+ status = SDP_INVALID_SYNTAX;
+ goto done;
+ }
+ pdata += scanned;
+ data_left -= scanned;
+
+ plen = ntohs(((sdp_pdu_hdr_t *)(req->buf))->plen);
+ mlen = scanned + sizeof(uint16_t) + 1;
+ // ensure we don't read past buffer
+ if (plen < mlen || plen != mlen + *(uint8_t *)(pdata+sizeof(uint16_t))) {
+ status = SDP_INVALID_SYNTAX;
+ goto done;
+ }
+
+ if (data_left < sizeof(uint16_t)) {
+ status = SDP_INVALID_SYNTAX;
+ goto done;
+ }
+
+ expected = ntohs(bt_get_unaligned((uint16_t *)pdata));
+
+ SDPDBG("Expected count: %d", expected);
+ SDPDBG("Bytes scanned : %d", scanned);
+
+ pdata += sizeof(uint16_t);
+ data_left -= sizeof(uint16_t);
+
+ /*
+ * Check if continuation state exists, if yes attempt
+ * to get rsp remainder from cache, else send error
+ */
+ if (sdp_cstate_get(pdata, data_left, &cstate) < 0) {
+ status = SDP_INVALID_SYNTAX;
+ goto done;
+ }
+
+ mtu = req->mtu - sizeof(sdp_pdu_hdr_t) - sizeof(uint16_t) - sizeof(uint16_t) - SDP_CONT_STATE_SIZE;
+ actual = MIN(expected, mtu >> 2);
+
+ /* make space in the rsp buffer for total and current record counts */
+ pdata = buf->data;
+
+ /* total service record count = 0 */
+ pTotalRecordCount = (short *)pdata;
+ bt_put_unaligned(0, (uint16_t *)pdata);
+ pdata += sizeof(uint16_t);
+ buf->data_size += sizeof(uint16_t);
+
+ /* current service record count = 0 */
+ pCurrentRecordCount = (short *)pdata;
+ bt_put_unaligned(0, (uint16_t *)pdata);
+ pdata += sizeof(uint16_t);
+ buf->data_size += sizeof(uint16_t);
+
+ if (cstate == NULL) {
+ /* for every record in the DB, do a pattern search */
+ sdp_list_t *list = sdp_get_record_list();
+
+ handleSize = 0;
+ for (; list && rsp_count < expected; list = list->next) {
+ sdp_record_t *rec = list->data;
+
+ SDPDBG("Checking svcRec : 0x%x", rec->handle);
+
+ if (sdp_match_uuid(pattern, rec->pattern) > 0 &&
+ sdp_check_access(rec->handle, &req->device)) {
+ rsp_count++;
+ bt_put_unaligned(htonl(rec->handle), (uint32_t *)pdata);
+ pdata += sizeof(uint32_t);
+ handleSize += sizeof(uint32_t);
+ }
+ }
+
+ SDPDBG("Match count: %d", rsp_count);
+
+ buf->data_size += handleSize;
+ bt_put_unaligned(htons(rsp_count), (uint16_t *)pTotalRecordCount);
+ bt_put_unaligned(htons(rsp_count), (uint16_t *)pCurrentRecordCount);
+
+ if (rsp_count > actual) {
+ /* cache the rsp and generate a continuation state */
+ cStateId = sdp_cstate_alloc_buf(buf);
+ /*
+ * subtract handleSize since we now send only
+ * a subset of handles
+ */
+ buf->data_size -= handleSize;
+ } else {
+ /* NULL continuation state */
+ sdp_set_cstate_pdu(buf, NULL);
+ }
+ }
+
+ /* under both the conditions below, the rsp buffer is not built yet */
+ if (cstate || cStateId > 0) {
+ short lastIndex = 0;
+
+ if (cstate) {
+ /*
+ * Get the previous sdp_cont_state_t and obtain
+ * the cached rsp
+ */
+ sdp_buf_t *pCache = sdp_get_cached_rsp(cstate);
+ if (pCache) {
+ pCacheBuffer = pCache->data;
+ /* get the rsp_count from the cached buffer */
+ rsp_count = ntohs(bt_get_unaligned((uint16_t *)pCacheBuffer));
+
+ /* get index of the last sdp_record_t sent */
+ lastIndex = cstate->cStateValue.lastIndexSent;
+ } else {
+ status = SDP_INVALID_CSTATE;
+ goto done;
+ }
+ } else {
+ pCacheBuffer = buf->data;
+ lastIndex = 0;
+ }
+
+ /*
+ * Set the local buffer pointer to after the
+ * current record count and increment the cached
+ * buffer pointer to beyond the counters
+ */
+ pdata = (uint8_t *) pCurrentRecordCount + sizeof(uint16_t);
+
+ /* increment beyond the totalCount and the currentCount */
+ pCacheBuffer += 2 * sizeof(uint16_t);
+
+ if (cstate) {
+ handleSize = 0;
+ for (i = lastIndex; (i - lastIndex) < actual && i < rsp_count; i++) {
+ bt_put_unaligned(bt_get_unaligned((uint32_t *)(pCacheBuffer + i * sizeof(uint32_t))), (uint32_t *)pdata);
+ pdata += sizeof(uint32_t);
+ handleSize += sizeof(uint32_t);
+ }
+ } else {
+ handleSize = actual << 2;
+ i = actual;
+ }
+
+ buf->data_size += handleSize;
+ bt_put_unaligned(htons(rsp_count), (uint16_t *)pTotalRecordCount);
+ bt_put_unaligned(htons(i - lastIndex), (uint16_t *)pCurrentRecordCount);
+
+ if (i == rsp_count) {
+ /* set "null" continuationState */
+ sdp_set_cstate_pdu(buf, NULL);
+ } else {
+ /*
+ * there's more: set lastIndexSent to
+ * the new value and move on
+ */
+ sdp_cont_state_t newState;
+
+ SDPDBG("Setting non-NULL sdp_cstate_t");
+
+ if (cstate)
+ memcpy(&newState, cstate, sizeof(sdp_cont_state_t));
+ else {
+ memset(&newState, 0, sizeof(sdp_cont_state_t));
+ newState.timestamp = cStateId;
+ }
+ newState.cStateValue.lastIndexSent = i;
+ sdp_set_cstate_pdu(buf, &newState);
+ }
+ }
+
+done:
+ free(cstate);
+ if (pattern)
+ sdp_list_free(pattern, free);
+
+ return status;
+}
+
+/*
+ * Extract attribute identifiers from the request PDU.
+ * Clients could request a subset of attributes (by id)
+ * from a service record, instead of the whole set. The
+ * requested identifiers are present in the PDU form of
+ * the request
+ */
+static int extract_attrs(sdp_record_t *rec, sdp_list_t *seq, sdp_buf_t *buf)
+{
+ sdp_buf_t pdu;
+
+ if (!rec)
+ return SDP_INVALID_RECORD_HANDLE;
+
+ if (seq == NULL) {
+ SDPDBG("Attribute sequence is NULL");
+ return 0;
+ }
+
+ SDPDBG("Entries in attr seq : %d", sdp_list_len(seq));
+
+ sdp_gen_record_pdu(rec, &pdu);
+
+ for (; seq; seq = seq->next) {
+ struct attrid *aid = seq->data;
+
+ SDPDBG("AttrDataType : %d", aid->dtd);
+
+ if (aid->dtd == SDP_UINT16) {
+ uint16_t attr = bt_get_unaligned((uint16_t *)&aid->uint16);
+ sdp_data_t *a = sdp_data_get(rec, attr);
+ if (a)
+ sdp_append_to_pdu(buf, a);
+ } else if (aid->dtd == SDP_UINT32) {
+ uint32_t range = bt_get_unaligned((uint32_t *)&aid->uint32);
+ uint16_t attr;
+ uint16_t low = (0xffff0000 & range) >> 16;
+ uint16_t high = 0x0000ffff & range;
+ sdp_data_t *data;
+
+ SDPDBG("attr range : 0x%x", range);
+ SDPDBG("Low id : 0x%x", low);
+ SDPDBG("High id : 0x%x", high);
+
+ if (low == 0x0000 && high == 0xffff && pdu.data_size <= buf->buf_size) {
+ /* copy it */
+ memcpy(buf->data, pdu.data, pdu.data_size);
+ buf->data_size = pdu.data_size;
+ break;
+ }
+ /* (else) sub-range of attributes */
+ for (attr = low; attr < high; attr++) {
+ data = sdp_data_get(rec, attr);
+ if (data)
+ sdp_append_to_pdu(buf, data);
+ }
+ data = sdp_data_get(rec, high);
+ if (data)
+ sdp_append_to_pdu(buf, data);
+ } else {
+ error("Unexpected data type : 0x%x", aid->dtd);
+ error("Expect uint16_t or uint32_t");
+ free(pdu.data);
+ return SDP_INVALID_SYNTAX;
+ }
+ }
+
+ free(pdu.data);
+
+ return 0;
+}
+
+/*
+ * A request for the attributes of a service record.
+ * First check if the service record (specified by
+ * service record handle) exists, then call the attribute
+ * streaming function
+ */
+static int service_attr_req(sdp_req_t *req, sdp_buf_t *buf)
+{
+ sdp_cont_state_t *cstate = NULL;
+ uint8_t *pResponse = NULL;
+ short cstate_size = 0;
+ sdp_list_t *seq = NULL;
+ uint8_t dtd = 0;
+ int scanned = 0;
+ unsigned int max_rsp_size;
+ int status = 0, plen, mlen;
+ uint8_t *pdata = req->buf + sizeof(sdp_pdu_hdr_t);
+ size_t data_left = req->len - sizeof(sdp_pdu_hdr_t);
+ uint32_t handle;
+
+ if (data_left < sizeof(uint32_t)) {
+ status = SDP_INVALID_SYNTAX;
+ goto done;
+ }
+
+ handle = ntohl(bt_get_unaligned((uint32_t *)pdata));
+
+ pdata += sizeof(uint32_t);
+ data_left -= sizeof(uint32_t);
+
+ if (data_left < sizeof(uint16_t)) {
+ status = SDP_INVALID_SYNTAX;
+ goto done;
+ }
+
+ max_rsp_size = ntohs(bt_get_unaligned((uint16_t *)pdata));
+
+ pdata += sizeof(uint16_t);
+ data_left -= sizeof(uint16_t);
+
+ if (data_left < sizeof(sdp_pdu_hdr_t)) {
+ status = SDP_INVALID_SYNTAX;
+ goto done;
+ }
+
+ /* extract the attribute list */
+ scanned = extract_des(pdata, data_left, &seq, &dtd, SDP_TYPE_ATTRID);
+ if (scanned == -1) {
+ status = SDP_INVALID_SYNTAX;
+ goto done;
+ }
+ pdata += scanned;
+ data_left -= scanned;
+
+ plen = ntohs(((sdp_pdu_hdr_t *)(req->buf))->plen);
+ mlen = scanned + sizeof(uint32_t) + sizeof(uint16_t) + 1;
+ // ensure we don't read past buffer
+ if (plen < mlen || plen != mlen + *(uint8_t *)pdata) {
+ status = SDP_INVALID_PDU_SIZE;
+ goto done;
+ }
+
+ /*
+ * if continuation state exists, attempt
+ * to get rsp remainder from cache, else send error
+ */
+ if (sdp_cstate_get(pdata, data_left, &cstate) < 0) {
+ status = SDP_INVALID_SYNTAX;
+ goto done;
+ }
+
+ SDPDBG("SvcRecHandle : 0x%x", handle);
+ SDPDBG("max_rsp_size : %d", max_rsp_size);
+
+ /*
+ * Check that max_rsp_size is within valid range
+ * a minimum size of 0x0007 has to be used for data field
+ */
+ if (max_rsp_size < 0x0007) {
+ status = SDP_INVALID_SYNTAX;
+ goto done;
+ }
+
+ /*
+ * Calculate Attribute size acording to MTU
+ * We can send only (MTU - sizeof(sdp_pdu_hdr_t) - sizeof(sdp_cont_state_t))
+ */
+ max_rsp_size = MIN(max_rsp_size, req->mtu - sizeof(sdp_pdu_hdr_t) -
+ sizeof(uint32_t) - SDP_CONT_STATE_SIZE - sizeof(uint16_t));
+
+ /* pull header for AttributeList byte count */
+ buf->data += sizeof(uint16_t);
+ buf->buf_size -= sizeof(uint16_t);
+
+ if (cstate) {
+ sdp_buf_t *pCache = sdp_get_cached_rsp(cstate);
+
+ SDPDBG("Obtained cached rsp : %p", pCache);
+
+ if (pCache) {
+ short sent = MIN(max_rsp_size, pCache->data_size - cstate->cStateValue.maxBytesSent);
+ pResponse = pCache->data;
+ memcpy(buf->data, pResponse + cstate->cStateValue.maxBytesSent, sent);
+ buf->data_size += sent;
+ cstate->cStateValue.maxBytesSent += sent;
+
+ SDPDBG("Response size : %d sending now : %d bytes sent so far : %d",
+ pCache->data_size, sent, cstate->cStateValue.maxBytesSent);
+ if (cstate->cStateValue.maxBytesSent == pCache->data_size)
+ cstate_size = sdp_set_cstate_pdu(buf, NULL);
+ else
+ cstate_size = sdp_set_cstate_pdu(buf, cstate);
+ } else {
+ status = SDP_INVALID_CSTATE;
+ error("NULL cache buffer and non-NULL continuation state");
+ }
+ } else {
+ sdp_record_t *rec = sdp_record_find(handle);
+ status = extract_attrs(rec, seq, buf);
+ if (buf->data_size > max_rsp_size) {
+ sdp_cont_state_t newState;
+
+ memset((char *)&newState, 0, sizeof(sdp_cont_state_t));
+ newState.timestamp = sdp_cstate_alloc_buf(buf);
+ /*
+ * Reset the buffer size to the maximum expected and
+ * set the sdp_cont_state_t
+ */
+ SDPDBG("Creating continuation state of size : %d", buf->data_size);
+ buf->data_size = max_rsp_size;
+ newState.cStateValue.maxBytesSent = max_rsp_size;
+ cstate_size = sdp_set_cstate_pdu(buf, &newState);
+ } else {
+ if (buf->data_size == 0)
+ sdp_append_to_buf(buf, 0, 0);
+ cstate_size = sdp_set_cstate_pdu(buf, NULL);
+ }
+ }
+
+ // push header
+ buf->data -= sizeof(uint16_t);
+ buf->buf_size += sizeof(uint16_t);
+
+done:
+ free(cstate);
+ if (seq)
+ sdp_list_free(seq, free);
+ if (status)
+ return status;
+
+ /* set attribute list byte count */
+ bt_put_unaligned(htons(buf->data_size - cstate_size), (uint16_t *)buf->data);
+ buf->data_size += sizeof(uint16_t);
+ return 0;
+}
+
+/*
+ * combined service search and attribute extraction
+ */
+static int service_search_attr_req(sdp_req_t *req, sdp_buf_t *buf)
+{
+ int status = 0, plen, totscanned;
+ uint8_t *pdata, *pResponse = NULL;
+ unsigned int max;
+ int scanned, rsp_count = 0;
+ sdp_list_t *pattern = NULL, *seq = NULL, *svcList;
+ sdp_cont_state_t *cstate = NULL;
+ short cstate_size = 0;
+ uint8_t dtd = 0;
+ sdp_buf_t tmpbuf;
+ size_t data_left = req->len;
+
+ tmpbuf.data = NULL;
+ pdata = req->buf + sizeof(sdp_pdu_hdr_t);
+ data_left = req->len - sizeof(sdp_pdu_hdr_t);
+ scanned = extract_des(pdata, data_left, &pattern, &dtd, SDP_TYPE_UUID);
+ if (scanned == -1) {
+ status = SDP_INVALID_SYNTAX;
+ goto done;
+ }
+ totscanned = scanned;
+
+ SDPDBG("Bytes scanned: %d", scanned);
+
+ pdata += scanned;
+ data_left -= scanned;
+
+ if (data_left < sizeof(uint16_t)) {
+ status = SDP_INVALID_SYNTAX;
+ goto done;
+ }
+
+ max = ntohs(bt_get_unaligned((uint16_t *)pdata));
+
+ pdata += sizeof(uint16_t);
+ data_left -= sizeof(uint16_t);
+
+ SDPDBG("Max Attr expected: %d", max);
+
+ if (data_left < sizeof(sdp_pdu_hdr_t)) {
+ status = SDP_INVALID_SYNTAX;
+ goto done;
+ }
+
+ /* extract the attribute list */
+ scanned = extract_des(pdata, data_left, &seq, &dtd, SDP_TYPE_ATTRID);
+ if (scanned == -1) {
+ status = SDP_INVALID_SYNTAX;
+ goto done;
+ }
+
+ pdata += scanned;
+ data_left -= scanned;
+
+ totscanned += scanned + sizeof(uint16_t) + 1;
+
+ plen = ntohs(((sdp_pdu_hdr_t *)(req->buf))->plen);
+ if (plen < totscanned || plen != totscanned + *(uint8_t *)pdata) {
+ status = SDP_INVALID_PDU_SIZE;
+ goto done;
+ }
+
+ /*
+ * if continuation state exists attempt
+ * to get rsp remainder from cache, else send error
+ */
+ if (sdp_cstate_get(pdata, data_left, &cstate) < 0) {
+ status = SDP_INVALID_SYNTAX;
+ goto done;
+ }
+
+ svcList = sdp_get_record_list();
+
+ tmpbuf.data = malloc(USHRT_MAX);
+ tmpbuf.data_size = 0;
+ tmpbuf.buf_size = USHRT_MAX;
+ memset(tmpbuf.data, 0, USHRT_MAX);
+
+ /*
+ * Calculate Attribute size acording to MTU
+ * We can send only (MTU - sizeof(sdp_pdu_hdr_t) - sizeof(sdp_cont_state_t))
+ */
+ max = MIN(max, req->mtu - sizeof(sdp_pdu_hdr_t) - SDP_CONT_STATE_SIZE - sizeof(uint16_t));
+
+ /* pull header for AttributeList byte count */
+ buf->data += sizeof(uint16_t);
+ buf->buf_size -= sizeof(uint16_t);
+
+ if (cstate == NULL) {
+ /* no continuation state -> create new response */
+ sdp_list_t *p;
+ for (p = svcList; p; p = p->next) {
+ sdp_record_t *rec = p->data;
+ if (sdp_match_uuid(pattern, rec->pattern) > 0 &&
+ sdp_check_access(rec->handle, &req->device)) {
+ rsp_count++;
+ status = extract_attrs(rec, seq, &tmpbuf);
+
+ SDPDBG("Response count : %d", rsp_count);
+ SDPDBG("Local PDU size : %d", tmpbuf.data_size);
+ if (status) {
+ SDPDBG("Extract attr from record returns err");
+ break;
+ }
+ if (buf->data_size + tmpbuf.data_size < buf->buf_size) {
+ // to be sure no relocations
+ sdp_append_to_buf(buf, tmpbuf.data, tmpbuf.data_size);
+ tmpbuf.data_size = 0;
+ memset(tmpbuf.data, 0, USHRT_MAX);
+ } else {
+ error("Relocation needed");
+ break;
+ }
+ SDPDBG("Net PDU size : %d", buf->data_size);
+ }
+ }
+ if (buf->data_size > max) {
+ sdp_cont_state_t newState;
+
+ memset((char *)&newState, 0, sizeof(sdp_cont_state_t));
+ newState.timestamp = sdp_cstate_alloc_buf(buf);
+ /*
+ * Reset the buffer size to the maximum expected and
+ * set the sdp_cont_state_t
+ */
+ buf->data_size = max;
+ newState.cStateValue.maxBytesSent = max;
+ cstate_size = sdp_set_cstate_pdu(buf, &newState);
+ } else
+ cstate_size = sdp_set_cstate_pdu(buf, NULL);
+ } else {
+ /* continuation State exists -> get from cache */
+ sdp_buf_t *pCache = sdp_get_cached_rsp(cstate);
+ if (pCache) {
+ uint16_t sent = MIN(max, pCache->data_size - cstate->cStateValue.maxBytesSent);
+ pResponse = pCache->data;
+ memcpy(buf->data, pResponse + cstate->cStateValue.maxBytesSent, sent);
+ buf->data_size += sent;
+ cstate->cStateValue.maxBytesSent += sent;
+ if (cstate->cStateValue.maxBytesSent == pCache->data_size)
+ cstate_size = sdp_set_cstate_pdu(buf, NULL);
+ else
+ cstate_size = sdp_set_cstate_pdu(buf, cstate);
+ } else {
+ status = SDP_INVALID_CSTATE;
+ SDPDBG("Non-null continuation state, but null cache buffer");
+ }
+ }
+
+ if (!rsp_count && !cstate) {
+ // found nothing
+ buf->data_size = 0;
+ sdp_append_to_buf(buf, tmpbuf.data, tmpbuf.data_size);
+ sdp_set_cstate_pdu(buf, NULL);
+ }
+
+ // push header
+ buf->data -= sizeof(uint16_t);
+ buf->buf_size += sizeof(uint16_t);
+
+ if (!status) {
+ /* set attribute list byte count */
+ bt_put_unaligned(htons(buf->data_size - cstate_size), (uint16_t *)buf->data);
+ buf->data_size += sizeof(uint16_t);
+ }
+
+done:
+ free(cstate);
+ free(tmpbuf.data);
+ if (pattern)
+ sdp_list_free(pattern, free);
+ if (seq)
+ sdp_list_free(seq, free);
+ return status;
+}
+
+/*
+ * Top level request processor. Calls the appropriate processing
+ * function based on request type. Handles service registration
+ * client requests also.
+ */
+static void process_request(sdp_req_t *req)
+{
+ sdp_pdu_hdr_t *reqhdr = (sdp_pdu_hdr_t *)req->buf;
+ sdp_pdu_hdr_t *rsphdr;
+ sdp_buf_t rsp;
+ uint8_t *buf = malloc(USHRT_MAX);
+ int sent = 0;
+ int status = SDP_INVALID_SYNTAX;
+
+ memset(buf, 0, USHRT_MAX);
+ rsp.data = buf + sizeof(sdp_pdu_hdr_t);
+ rsp.data_size = 0;
+ rsp.buf_size = USHRT_MAX - sizeof(sdp_pdu_hdr_t);
+ rsphdr = (sdp_pdu_hdr_t *)buf;
+
+ if (ntohs(reqhdr->plen) != req->len - sizeof(sdp_pdu_hdr_t)) {
+ status = SDP_INVALID_PDU_SIZE;
+ goto send_rsp;
+ }
+ switch (reqhdr->pdu_id) {
+ case SDP_SVC_SEARCH_REQ:
+ SDPDBG("Got a svc srch req");
+ status = service_search_req(req, &rsp);
+ rsphdr->pdu_id = SDP_SVC_SEARCH_RSP;
+ break;
+ case SDP_SVC_ATTR_REQ:
+ SDPDBG("Got a svc attr req");
+ status = service_attr_req(req, &rsp);
+ rsphdr->pdu_id = SDP_SVC_ATTR_RSP;
+ break;
+ case SDP_SVC_SEARCH_ATTR_REQ:
+ SDPDBG("Got a svc srch attr req");
+ status = service_search_attr_req(req, &rsp);
+ rsphdr->pdu_id = SDP_SVC_SEARCH_ATTR_RSP;
+ break;
+ /* Following requests are allowed only for local connections */
+ case SDP_SVC_REGISTER_REQ:
+ SDPDBG("Service register request");
+ if (req->local) {
+ status = service_register_req(req, &rsp);
+ rsphdr->pdu_id = SDP_SVC_REGISTER_RSP;
+ }
+ break;
+ case SDP_SVC_UPDATE_REQ:
+ SDPDBG("Service update request");
+ if (req->local) {
+ status = service_update_req(req, &rsp);
+ rsphdr->pdu_id = SDP_SVC_UPDATE_RSP;
+ }
+ break;
+ case SDP_SVC_REMOVE_REQ:
+ SDPDBG("Service removal request");
+ if (req->local) {
+ status = service_remove_req(req, &rsp);
+ rsphdr->pdu_id = SDP_SVC_REMOVE_RSP;
+ }
+ break;
+ default:
+ error("Unknown PDU ID : 0x%x received", reqhdr->pdu_id);
+ status = SDP_INVALID_SYNTAX;
+ break;
+ }
+
+send_rsp:
+ if (status) {
+ rsphdr->pdu_id = SDP_ERROR_RSP;
+ bt_put_unaligned(htons(status), (uint16_t *)rsp.data);
+ rsp.data_size = sizeof(uint16_t);
+ }
+
+ SDPDBG("Sending rsp. status %d", status);
+
+ rsphdr->tid = reqhdr->tid;
+ rsphdr->plen = htons(rsp.data_size);
+
+ /* point back to the real buffer start and set the real rsp length */
+ rsp.data_size += sizeof(sdp_pdu_hdr_t);
+ rsp.data = buf;
+
+ /* stream the rsp PDU */
+ sent = send(req->sock, rsp.data, rsp.data_size, 0);
+
+ SDPDBG("Bytes Sent : %d", sent);
+
+ free(rsp.data);
+ free(req->buf);
+}
+
+void handle_request(int sk, uint8_t *data, int len)
+{
+ struct sockaddr_l2 sa;
+ socklen_t size;
+ sdp_req_t req;
+
+ size = sizeof(sa);
+ if (getpeername(sk, (struct sockaddr *) &sa, &size) < 0) {
+ error("getpeername: %s", strerror(errno));
+ return;
+ }
+
+ if (sa.l2_family == AF_BLUETOOTH) {
+ struct l2cap_options lo;
+
+ memset(&lo, 0, sizeof(lo));
+ size = sizeof(lo);
+
+ if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &lo, &size) < 0) {
+ error("getsockopt: %s", strerror(errno));
+ return;
+ }
+
+ bacpy(&req.bdaddr, &sa.l2_bdaddr);
+ req.mtu = lo.omtu;
+ req.local = 0;
+ memset(&sa, 0, sizeof(sa));
+ size = sizeof(sa);
+
+ if (getsockname(sk, (struct sockaddr *) &sa, &size) < 0) {
+ error("getsockname: %s", strerror(errno));
+ return;
+ }
+
+ bacpy(&req.device, &sa.l2_bdaddr);
+ } else {
+ bacpy(&req.device, BDADDR_ANY);
+ bacpy(&req.bdaddr, BDADDR_LOCAL);
+ req.mtu = 2048;
+ req.local = 1;
+ }
+
+ req.sock = sk;
+ req.buf = data;
+ req.len = len;
+
+ process_request(&req);
+}
diff --git a/src/sdpd-server.c b/src/sdpd-server.c
new file mode 100644
index 0000000..a92ae2c
--- /dev/null
+++ b/src/sdpd-server.c
@@ -0,0 +1,292 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2001-2002 Nokia Corporation
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2002-2003 Stephen Crane <steve.crane@rococosoft.com>
+ *
+ *
+ * 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 <sys/stat.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <sys/un.h>
+#include <netinet/in.h>
+
+#include <glib.h>
+
+#include "log.h"
+#include "sdpd.h"
+
+static guint l2cap_id = 0, unix_id = 0;
+
+static int l2cap_sock, unix_sock;
+
+/*
+ * SDP server initialization on startup includes creating the
+ * l2cap and unix sockets over which discovery and registration clients
+ * access us respectively
+ */
+static int init_server(uint16_t mtu, int master, int compat)
+{
+ struct l2cap_options opts;
+ struct sockaddr_l2 l2addr;
+ struct sockaddr_un unaddr;
+ socklen_t optlen;
+
+ /* Register the public browse group root */
+ register_public_browse_group();
+
+ /* Register the SDP server's service record */
+ register_server_service();
+
+ /* Create L2CAP socket */
+ l2cap_sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+ if (l2cap_sock < 0) {
+ error("opening L2CAP socket: %s", strerror(errno));
+ return -1;
+ }
+
+ memset(&l2addr, 0, sizeof(l2addr));
+ l2addr.l2_family = AF_BLUETOOTH;
+ bacpy(&l2addr.l2_bdaddr, BDADDR_ANY);
+ l2addr.l2_psm = htobs(SDP_PSM);
+
+ if (bind(l2cap_sock, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0) {
+ error("binding L2CAP socket: %s", strerror(errno));
+ return -1;
+ }
+
+ if (master) {
+ int opt = L2CAP_LM_MASTER;
+ if (setsockopt(l2cap_sock, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0) {
+ error("setsockopt: %s", strerror(errno));
+ return -1;
+ }
+ }
+
+ if (mtu > 0) {
+ memset(&opts, 0, sizeof(opts));
+ optlen = sizeof(opts);
+
+ if (getsockopt(l2cap_sock, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen) < 0) {
+ error("getsockopt: %s", strerror(errno));
+ return -1;
+ }
+
+ opts.omtu = mtu;
+ opts.imtu = mtu;
+
+ if (setsockopt(l2cap_sock, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts)) < 0) {
+ error("setsockopt: %s", strerror(errno));
+ return -1;
+ }
+ }
+
+ if (listen(l2cap_sock, 5) < 0) {
+ error("listen: %s", strerror(errno));
+ return -1;
+ }
+
+ if (!compat) {
+ unix_sock = -1;
+ return 0;
+ }
+
+ /* Create local Unix socket */
+ unix_sock = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (unix_sock < 0) {
+ error("opening UNIX socket: %s", strerror(errno));
+ return -1;
+ }
+
+ memset(&unaddr, 0, sizeof(unaddr));
+ unaddr.sun_family = AF_UNIX;
+ strcpy(unaddr.sun_path, SDP_UNIX_PATH);
+
+ unlink(unaddr.sun_path);
+
+ if (bind(unix_sock, (struct sockaddr *) &unaddr, sizeof(unaddr)) < 0) {
+ error("binding UNIX socket: %s", strerror(errno));
+ return -1;
+ }
+
+ if (listen(unix_sock, 5) < 0) {
+ error("listen UNIX socket: %s", strerror(errno));
+ return -1;
+ }
+
+ chmod(SDP_UNIX_PATH, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
+
+ return 0;
+}
+
+static gboolean io_session_event(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+ sdp_pdu_hdr_t hdr;
+ uint8_t *buf;
+ int sk, len, size;
+
+ if (cond & G_IO_NVAL)
+ return FALSE;
+
+ sk = g_io_channel_unix_get_fd(chan);
+
+ if (cond & (G_IO_HUP | G_IO_ERR)) {
+ sdp_svcdb_collect_all(sk);
+ return FALSE;
+ }
+
+ len = recv(sk, &hdr, sizeof(sdp_pdu_hdr_t), MSG_PEEK);
+ if (len <= 0) {
+ sdp_svcdb_collect_all(sk);
+ return FALSE;
+ }
+
+ size = sizeof(sdp_pdu_hdr_t) + ntohs(hdr.plen);
+ buf = malloc(size);
+ if (!buf)
+ return TRUE;
+
+ len = recv(sk, buf, size, 0);
+ if (len <= 0) {
+ sdp_svcdb_collect_all(sk);
+ free(buf);
+ return FALSE;
+ }
+
+ handle_request(sk, buf, len);
+
+ return TRUE;
+}
+
+static gboolean io_accept_event(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+ GIOChannel *io;
+ int nsk;
+
+ if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL))
+ return FALSE;
+
+ if (data == &l2cap_sock) {
+ struct sockaddr_l2 addr;
+ socklen_t len = sizeof(addr);
+
+ nsk = accept(l2cap_sock, (struct sockaddr *) &addr, &len);
+ } else if (data == &unix_sock) {
+ struct sockaddr_un addr;
+ socklen_t len = sizeof(addr);
+
+ nsk = accept(unix_sock, (struct sockaddr *) &addr, &len);
+ } else
+ return FALSE;
+
+ if (nsk < 0) {
+ error("Can't accept connection: %s", strerror(errno));
+ return TRUE;
+ }
+
+ io = g_io_channel_unix_new(nsk);
+ g_io_channel_set_close_on_unref(io, TRUE);
+
+ g_io_add_watch(io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ io_session_event, data);
+
+ g_io_channel_unref(io);
+
+ return TRUE;
+}
+
+int start_sdp_server(uint16_t mtu, const char *did, uint32_t flags)
+{
+ int compat = flags & SDP_SERVER_COMPAT;
+ int master = flags & SDP_SERVER_MASTER;
+ GIOChannel *io;
+
+ info("Starting SDP server");
+
+ if (init_server(mtu, master, compat) < 0) {
+ error("Server initialization failed");
+ return -1;
+ }
+
+ if (did && strlen(did) > 0) {
+ const char *ptr = did;
+ uint16_t vid = 0x0000, pid = 0x0000, ver = 0x0000;
+
+ vid = (uint16_t) strtol(ptr, NULL, 16);
+ ptr = strchr(ptr, ':');
+ if (ptr) {
+ pid = (uint16_t) strtol(ptr + 1, NULL, 16);
+ ptr = strchr(ptr + 1, ':');
+ if (ptr)
+ ver = (uint16_t) strtol(ptr + 1, NULL, 16);
+ register_device_id(vid, pid, ver);
+ }
+ }
+
+ io = g_io_channel_unix_new(l2cap_sock);
+ g_io_channel_set_close_on_unref(io, TRUE);
+
+ l2cap_id = g_io_add_watch(io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ io_accept_event, &l2cap_sock);
+ g_io_channel_unref(io);
+
+ if (compat && unix_sock > fileno(stderr)) {
+ io = g_io_channel_unix_new(unix_sock);
+ g_io_channel_set_close_on_unref(io, TRUE);
+
+ unix_id = g_io_add_watch(io,
+ G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ io_accept_event, &unix_sock);
+ g_io_channel_unref(io);
+ }
+
+ return 0;
+}
+
+void stop_sdp_server(void)
+{
+ info("Stopping SDP server");
+
+ sdp_svcdb_reset();
+
+ if (unix_id > 0)
+ g_source_remove(unix_id);
+
+ if (l2cap_id > 0)
+ g_source_remove(l2cap_id);
+
+ l2cap_id = unix_id = 0;
+ l2cap_sock = unix_sock = -1;
+}
diff --git a/src/sdpd-service.c b/src/sdpd-service.c
new file mode 100644
index 0000000..0d6722a
--- /dev/null
+++ b/src/sdpd-service.c
@@ -0,0 +1,537 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2001-2002 Nokia Corporation
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2002-2003 Stephen Crane <steve.crane@rococosoft.com>
+ *
+ *
+ * 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 <assert.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <netinet/in.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "sdpd.h"
+#include "log.h"
+#include "adapter.h"
+#include "manager.h"
+
+static sdp_record_t *server = NULL;
+
+/*
+ * List of version numbers supported by the SDP server.
+ * Add to this list when newer versions are supported.
+ */
+static sdp_version_t sdpVnumArray[1] = {
+ { 1, 0 }
+};
+static const int sdpServerVnumEntries = 1;
+
+/*
+ * A simple function which returns the time of day in
+ * seconds. Used for updating the service db state
+ * attribute of the service record of the SDP server
+ */
+uint32_t sdp_get_time()
+{
+ /*
+ * To handle failure in gettimeofday, so an old
+ * value is returned and service does not fail
+ */
+ static struct timeval tm;
+
+ gettimeofday(&tm, NULL);
+ return (uint32_t) tm.tv_sec;
+}
+
+/*
+ * The service database state is an attribute of the service record
+ * of the SDP server itself. This attribute is guaranteed to
+ * change if any of the contents of the service repository
+ * changes. This function updates the timestamp of value of
+ * the svcDBState attribute
+ * Set the SDP server DB. Simply a timestamp which is the marker
+ * when the DB was modified.
+ */
+static void update_db_timestamp(void)
+{
+ uint32_t dbts = sdp_get_time();
+ sdp_data_t *d = sdp_data_alloc(SDP_UINT32, &dbts);
+ sdp_attr_replace(server, SDP_ATTR_SVCDB_STATE, d);
+}
+
+void register_public_browse_group(void)
+{
+ sdp_list_t *browselist;
+ uuid_t bgscid, pbgid;
+ sdp_data_t *sdpdata;
+ sdp_record_t *browse = sdp_record_alloc();
+
+ browse->handle = SDP_SERVER_RECORD_HANDLE + 1;
+
+ sdp_record_add(BDADDR_ANY, browse);
+ sdpdata = sdp_data_alloc(SDP_UINT32, &browse->handle);
+ sdp_attr_add(browse, SDP_ATTR_RECORD_HANDLE, sdpdata);
+
+ sdp_uuid16_create(&bgscid, BROWSE_GRP_DESC_SVCLASS_ID);
+ browselist = sdp_list_append(0, &bgscid);
+ sdp_set_service_classes(browse, browselist);
+ sdp_list_free(browselist, 0);
+
+ sdp_uuid16_create(&pbgid, PUBLIC_BROWSE_GROUP);
+ sdp_attr_add_new(browse, SDP_ATTR_GROUP_ID,
+ SDP_UUID16, &pbgid.value.uuid16);
+}
+
+/*
+ * The SDP server must present its own service record to
+ * the service repository. This can be accessed by service
+ * discovery clients. This method constructs a service record
+ * and stores it in the repository
+ */
+void register_server_service(void)
+{
+ sdp_list_t *classIDList;
+ uuid_t classID;
+ void **versions, **versionDTDs;
+ uint8_t dtd;
+ sdp_data_t *pData;
+ int i;
+
+ server = sdp_record_alloc();
+ server->pattern = NULL;
+
+ /* Force the record to be SDP_SERVER_RECORD_HANDLE */
+ server->handle = SDP_SERVER_RECORD_HANDLE;
+
+ sdp_record_add(BDADDR_ANY, server);
+ sdp_attr_add(server, SDP_ATTR_RECORD_HANDLE,
+ sdp_data_alloc(SDP_UINT32, &server->handle));
+
+ sdp_uuid16_create(&classID, SDP_SERVER_SVCLASS_ID);
+ classIDList = sdp_list_append(0, &classID);
+ sdp_set_service_classes(server, classIDList);
+ sdp_list_free(classIDList, 0);
+
+ /*
+ * Set the version numbers supported, these are passed as arguments
+ * to the server on command line. Now defaults to 1.0
+ * Build the version number sequence first
+ */
+ versions = malloc(sdpServerVnumEntries * sizeof(void *));
+ versionDTDs = malloc(sdpServerVnumEntries * sizeof(void *));
+ dtd = SDP_UINT16;
+ for (i = 0; i < sdpServerVnumEntries; i++) {
+ uint16_t *version = malloc(sizeof(uint16_t));
+ *version = sdpVnumArray[i].major;
+ *version = (*version << 8);
+ *version |= sdpVnumArray[i].minor;
+ versions[i] = version;
+ versionDTDs[i] = &dtd;
+ }
+ pData = sdp_seq_alloc(versionDTDs, versions, sdpServerVnumEntries);
+ for (i = 0; i < sdpServerVnumEntries; i++)
+ free(versions[i]);
+ free(versions);
+ free(versionDTDs);
+ sdp_attr_add(server, SDP_ATTR_VERSION_NUM_LIST, pData);
+
+ update_db_timestamp();
+}
+
+void register_device_id(const uint16_t vendor, const uint16_t product,
+ const uint16_t version)
+{
+ const uint16_t spec = 0x0102, source = 0x0002;
+ const uint8_t primary = 1;
+ sdp_list_t *class_list, *group_list, *profile_list;
+ uuid_t class_uuid, group_uuid;
+ sdp_data_t *sdp_data, *primary_data, *source_data;
+ sdp_data_t *spec_data, *vendor_data, *product_data, *version_data;
+ sdp_profile_desc_t profile;
+ sdp_record_t *record = sdp_record_alloc();
+
+ info("Adding device id record for %04x:%04x", vendor, product);
+
+ btd_manager_set_did(vendor, product, version);
+
+ record->handle = sdp_next_handle();
+
+ sdp_record_add(BDADDR_ANY, record);
+ sdp_data = sdp_data_alloc(SDP_UINT32, &record->handle);
+ sdp_attr_add(record, SDP_ATTR_RECORD_HANDLE, sdp_data);
+
+ sdp_uuid16_create(&class_uuid, PNP_INFO_SVCLASS_ID);
+ class_list = sdp_list_append(0, &class_uuid);
+ sdp_set_service_classes(record, class_list);
+ sdp_list_free(class_list, NULL);
+
+ sdp_uuid16_create(&group_uuid, PUBLIC_BROWSE_GROUP);
+ group_list = sdp_list_append(NULL, &group_uuid);
+ sdp_set_browse_groups(record, group_list);
+ sdp_list_free(group_list, NULL);
+
+ sdp_uuid16_create(&profile.uuid, PNP_INFO_PROFILE_ID);
+ profile.version = spec;
+ profile_list = sdp_list_append(NULL, &profile);
+ sdp_set_profile_descs(record, profile_list);
+ sdp_list_free(profile_list, NULL);
+
+ spec_data = sdp_data_alloc(SDP_UINT16, &spec);
+ sdp_attr_add(record, 0x0200, spec_data);
+
+ vendor_data = sdp_data_alloc(SDP_UINT16, &vendor);
+ sdp_attr_add(record, 0x0201, vendor_data);
+
+ product_data = sdp_data_alloc(SDP_UINT16, &product);
+ sdp_attr_add(record, 0x0202, product_data);
+
+ version_data = sdp_data_alloc(SDP_UINT16, &version);
+ sdp_attr_add(record, 0x0203, version_data);
+
+ primary_data = sdp_data_alloc(SDP_BOOL, &primary);
+ sdp_attr_add(record, 0x0204, primary_data);
+
+ source_data = sdp_data_alloc(SDP_UINT16, &source);
+ sdp_attr_add(record, 0x0205, source_data);
+
+ update_db_timestamp();
+}
+
+int add_record_to_server(const bdaddr_t *src, sdp_record_t *rec)
+{
+ sdp_data_t *data;
+ sdp_list_t *pattern;
+
+ if (rec->handle == 0xffffffff) {
+ rec->handle = sdp_next_handle();
+ if (rec->handle < 0x10000)
+ return -ENOSPC;
+ } else {
+ if (sdp_record_find(rec->handle))
+ return -EEXIST;
+ }
+
+ DBG("Adding record with handle 0x%05x", rec->handle);
+
+ sdp_record_add(src, rec);
+
+ data = sdp_data_alloc(SDP_UINT32, &rec->handle);
+ sdp_attr_replace(rec, SDP_ATTR_RECORD_HANDLE, data);
+
+ if (sdp_data_get(rec, SDP_ATTR_BROWSE_GRP_LIST) == NULL) {
+ uuid_t uuid;
+ sdp_uuid16_create(&uuid, PUBLIC_BROWSE_GROUP);
+ sdp_pattern_add_uuid(rec, &uuid);
+ }
+
+ for (pattern = rec->pattern; pattern; pattern = pattern->next) {
+ char uuid[32];
+
+ if (pattern->data == NULL)
+ continue;
+
+ sdp_uuid2strn((uuid_t *) pattern->data, uuid, sizeof(uuid));
+ DBG("Record pattern UUID %s", uuid);
+ }
+
+ update_db_timestamp();
+
+ return 0;
+}
+
+int remove_record_from_server(uint32_t handle)
+{
+ sdp_record_t *rec;
+
+ DBG("Removing record with handle 0x%05x", handle);
+
+ rec = sdp_record_find(handle);
+ if (!rec)
+ return -ENOENT;
+
+ if (sdp_record_remove(handle) == 0)
+ update_db_timestamp();
+
+ sdp_record_free(rec);
+
+ return 0;
+}
+
+/* FIXME: refactor for server-side */
+static sdp_record_t *extract_pdu_server(bdaddr_t *device, uint8_t *p,
+ unsigned int bufsize,
+ uint32_t handleExpected, int *scanned)
+{
+ int extractStatus = -1, localExtractedLength = 0;
+ uint8_t dtd;
+ int seqlen = 0;
+ sdp_record_t *rec = NULL;
+ uint16_t attrId, lookAheadAttrId;
+ sdp_data_t *pAttr = NULL;
+ uint32_t handle = 0xffffffff;
+
+ *scanned = sdp_extract_seqtype(p, bufsize, &dtd, &seqlen);
+ p += *scanned;
+ bufsize -= *scanned;
+
+ if (bufsize < sizeof(uint8_t) + sizeof(uint8_t)) {
+ SDPDBG("Unexpected end of packet");
+ return NULL;
+ }
+
+ lookAheadAttrId = ntohs(bt_get_unaligned((uint16_t *) (p + sizeof(uint8_t))));
+
+ SDPDBG("Look ahead attr id : %d", lookAheadAttrId);
+
+ if (lookAheadAttrId == SDP_ATTR_RECORD_HANDLE) {
+ if (bufsize < (sizeof(uint8_t) * 2) +
+ sizeof(uint16_t) + sizeof(uint32_t)) {
+ SDPDBG("Unexpected end of packet");
+ return NULL;
+ }
+ handle = ntohl(bt_get_unaligned((uint32_t *) (p +
+ sizeof(uint8_t) + sizeof(uint16_t) +
+ sizeof(uint8_t))));
+ SDPDBG("SvcRecHandle : 0x%x", handle);
+ rec = sdp_record_find(handle);
+ } else if (handleExpected != 0xffffffff)
+ rec = sdp_record_find(handleExpected);
+
+ if (!rec) {
+ rec = sdp_record_alloc();
+ rec->attrlist = NULL;
+ if (lookAheadAttrId == SDP_ATTR_RECORD_HANDLE) {
+ rec->handle = handle;
+ sdp_record_add(device, rec);
+ } else if (handleExpected != 0xffffffff) {
+ rec->handle = handleExpected;
+ sdp_record_add(device, rec);
+ }
+ } else {
+ sdp_list_free(rec->attrlist, (sdp_free_func_t) sdp_data_free);
+ rec->attrlist = NULL;
+ }
+
+ while (localExtractedLength < seqlen) {
+ int attrSize = sizeof(uint8_t);
+ int attrValueLength = 0;
+
+ if (bufsize < attrSize + sizeof(uint16_t)) {
+ SDPDBG("Unexpected end of packet: Terminating extraction of attributes");
+ break;
+ }
+
+ SDPDBG("Extract PDU, sequenceLength: %d localExtractedLength: %d",
+ seqlen, localExtractedLength);
+ dtd = *(uint8_t *) p;
+
+ attrId = ntohs(bt_get_unaligned((uint16_t *) (p + attrSize)));
+ attrSize += sizeof(uint16_t);
+
+ SDPDBG("DTD of attrId : %d Attr id : 0x%x", dtd, attrId);
+
+ pAttr = sdp_extract_attr(p + attrSize, bufsize - attrSize,
+ &attrValueLength, rec);
+
+ SDPDBG("Attr id : 0x%x attrValueLength : %d", attrId, attrValueLength);
+
+ attrSize += attrValueLength;
+ if (pAttr == NULL) {
+ SDPDBG("Terminating extraction of attributes");
+ break;
+ }
+ localExtractedLength += attrSize;
+ p += attrSize;
+ bufsize -= attrSize;
+ sdp_attr_replace(rec, attrId, pAttr);
+ extractStatus = 0;
+ SDPDBG("Extract PDU, seqLength: %d localExtractedLength: %d",
+ seqlen, localExtractedLength);
+ }
+
+ if (extractStatus == 0) {
+ SDPDBG("Successful extracting of Svc Rec attributes");
+#ifdef SDP_DEBUG
+ sdp_print_service_attr(rec->attrlist);
+#endif
+ *scanned += seqlen;
+ }
+ return rec;
+}
+
+/*
+ * Add the newly created service record to the service repository
+ */
+int service_register_req(sdp_req_t *req, sdp_buf_t *rsp)
+{
+ int scanned = 0;
+ sdp_data_t *handle;
+ uint8_t *p = req->buf + sizeof(sdp_pdu_hdr_t);
+ int bufsize = req->len - sizeof(sdp_pdu_hdr_t);
+ sdp_record_t *rec;
+
+ req->flags = *p++;
+ if (req->flags & SDP_DEVICE_RECORD) {
+ bacpy(&req->device, (bdaddr_t *) p);
+ p += sizeof(bdaddr_t);
+ bufsize -= sizeof(bdaddr_t);
+ }
+
+ // save image of PDU: we need it when clients request this attribute
+ rec = extract_pdu_server(&req->device, p, bufsize, 0xffffffff, &scanned);
+ if (!rec)
+ goto invalid;
+
+ if (rec->handle == 0xffffffff) {
+ rec->handle = sdp_next_handle();
+ if (rec->handle < 0x10000) {
+ sdp_record_free(rec);
+ goto invalid;
+ }
+ } else {
+ if (sdp_record_find(rec->handle)) {
+ /* extract_pdu_server will add the record handle
+ * if it is missing. So instead of failing, skip
+ * the record adding to avoid duplication. */
+ goto success;
+ }
+ }
+
+ sdp_record_add(&req->device, rec);
+ if (!(req->flags & SDP_RECORD_PERSIST))
+ sdp_svcdb_set_collectable(rec, req->sock);
+
+ handle = sdp_data_alloc(SDP_UINT32, &rec->handle);
+ sdp_attr_replace(rec, SDP_ATTR_RECORD_HANDLE, handle);
+
+success:
+ /* if the browse group descriptor is NULL,
+ * ensure that the record belongs to the ROOT group */
+ if (sdp_data_get(rec, SDP_ATTR_BROWSE_GRP_LIST) == NULL) {
+ uuid_t uuid;
+ sdp_uuid16_create(&uuid, PUBLIC_BROWSE_GROUP);
+ sdp_pattern_add_uuid(rec, &uuid);
+ }
+
+ update_db_timestamp();
+
+ /* Build a rsp buffer */
+ bt_put_unaligned(htonl(rec->handle), (uint32_t *) rsp->data);
+ rsp->data_size = sizeof(uint32_t);
+
+ return 0;
+
+invalid:
+ bt_put_unaligned(htons(SDP_INVALID_SYNTAX), (uint16_t *) rsp->data);
+ rsp->data_size = sizeof(uint16_t);
+
+ return -1;
+}
+
+/*
+ * Update a service record
+ */
+int service_update_req(sdp_req_t *req, sdp_buf_t *rsp)
+{
+ sdp_record_t *orec, *nrec;
+ int status = 0, scanned = 0;
+ uint8_t *p = req->buf + sizeof(sdp_pdu_hdr_t);
+ int bufsize = req->len - sizeof(sdp_pdu_hdr_t);
+ uint32_t handle = ntohl(bt_get_unaligned((uint32_t *) p));
+
+ SDPDBG("Svc Rec Handle: 0x%x", handle);
+
+ p += sizeof(uint32_t);
+ bufsize -= sizeof(uint32_t);
+
+ orec = sdp_record_find(handle);
+
+ SDPDBG("SvcRecOld: %p", orec);
+
+ if (!orec) {
+ status = SDP_INVALID_RECORD_HANDLE;
+ goto done;
+ }
+
+ nrec = extract_pdu_server(BDADDR_ANY, p, bufsize, handle, &scanned);
+ if (!nrec) {
+ status = SDP_INVALID_SYNTAX;
+ goto done;
+ }
+
+ assert(nrec == orec);
+
+ update_db_timestamp();
+
+done:
+ p = rsp->data;
+ bt_put_unaligned(htons(status), (uint16_t *) p);
+ rsp->data_size = sizeof(uint16_t);
+ return status;
+}
+
+/*
+ * Remove a registered service record
+ */
+int service_remove_req(sdp_req_t *req, sdp_buf_t *rsp)
+{
+ uint8_t *p = req->buf + sizeof(sdp_pdu_hdr_t);
+ uint32_t handle = ntohl(bt_get_unaligned((uint32_t *) p));
+ sdp_record_t *rec;
+ int status = 0;
+
+ /* extract service record handle */
+ p += sizeof(uint32_t);
+
+ rec = sdp_record_find(handle);
+ if (rec) {
+ sdp_svcdb_collect(rec);
+ status = sdp_record_remove(handle);
+ sdp_record_free(rec);
+ if (status == 0)
+ update_db_timestamp();
+ } else {
+ status = SDP_INVALID_RECORD_HANDLE;
+ SDPDBG("Could not find record : 0x%x", handle);
+ }
+
+ p = rsp->data;
+ bt_put_unaligned(htons(status), (uint16_t *) p);
+ rsp->data_size = sizeof(uint16_t);
+
+ return status;
+}
diff --git a/src/sdpd.h b/src/sdpd.h
new file mode 100644
index 0000000..dc7a256
--- /dev/null
+++ b/src/sdpd.h
@@ -0,0 +1,97 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2001-2002 Nokia Corporation
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2002-2003 Stephen Crane <steve.crane@rococosoft.com>
+ *
+ *
+ * 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
+ *
+ */
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+
+#ifdef SDP_DEBUG
+#include <syslog.h>
+#define SDPDBG(fmt, arg...) syslog(LOG_DEBUG, "%s: " fmt "\n", __func__ , ## arg)
+#else
+#define SDPDBG(fmt...)
+#endif
+
+#define EIR_DATA_LENGTH 240
+
+#define EIR_FLAGS 0x01 /* flags */
+#define EIR_UUID16_SOME 0x02 /* 16-bit UUID, more available */
+#define EIR_UUID16_ALL 0x03 /* 16-bit UUID, all listed */
+#define EIR_UUID32_SOME 0x04 /* 32-bit UUID, more available */
+#define EIR_UUID32_ALL 0x05 /* 32-bit UUID, all listed */
+#define EIR_UUID128_SOME 0x06 /* 128-bit UUID, more available */
+#define EIR_UUID128_ALL 0x07 /* 128-bit UUID, all listed */
+#define EIR_NAME_SHORT 0x08 /* shortened local name */
+#define EIR_NAME_COMPLETE 0x09 /* complete local name */
+#define EIR_TX_POWER 0x0A /* transmit power level */
+#define EIR_DEVICE_ID 0x10 /* device ID */
+
+typedef struct request {
+ bdaddr_t device;
+ bdaddr_t bdaddr;
+ int local;
+ int sock;
+ int mtu;
+ int flags;
+ uint8_t *buf;
+ int len;
+} sdp_req_t;
+
+void handle_request(int sk, uint8_t *data, int len);
+
+int service_register_req(sdp_req_t *req, sdp_buf_t *rsp);
+int service_update_req(sdp_req_t *req, sdp_buf_t *rsp);
+int service_remove_req(sdp_req_t *req, sdp_buf_t *rsp);
+
+void register_public_browse_group(void);
+void register_server_service(void);
+void register_device_id(const uint16_t vendor, const uint16_t product,
+ const uint16_t version);
+
+int record_sort(const void *r1, const void *r2);
+void sdp_svcdb_reset(void);
+void sdp_svcdb_collect_all(int sock);
+void sdp_svcdb_set_collectable(sdp_record_t *rec, int sock);
+void sdp_svcdb_collect(sdp_record_t *rec);
+sdp_record_t *sdp_record_find(uint32_t handle);
+void sdp_record_add(const bdaddr_t *device, sdp_record_t *rec);
+int sdp_record_remove(uint32_t handle);
+sdp_list_t *sdp_get_record_list(void);
+sdp_list_t *sdp_get_access_list(void);
+int sdp_check_access(uint32_t handle, bdaddr_t *device);
+uint32_t sdp_next_handle(void);
+
+uint32_t sdp_get_time();
+
+#define SDP_SERVER_COMPAT (1 << 0)
+#define SDP_SERVER_MASTER (1 << 1)
+
+int start_sdp_server(uint16_t mtu, const char *did, uint32_t flags);
+void stop_sdp_server(void);
+
+int add_record_to_server(const bdaddr_t *src, sdp_record_t *rec);
+int remove_record_from_server(uint32_t handle);
+
+void sdp_init_services_list(bdaddr_t *device);
diff --git a/src/storage.c b/src/storage.c
new file mode 100644
index 0000000..28aea30
--- /dev/null
+++ b/src/storage.c
@@ -0,0 +1,1345 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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 <time.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <glib.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include "textfile.h"
+#include "adapter.h"
+#include "device.h"
+#include "glib-helper.h"
+#include "storage.h"
+
+struct match {
+ GSList *keys;
+ char *pattern;
+};
+
+static inline int create_filename(char *buf, size_t size,
+ const bdaddr_t *bdaddr, const char *name)
+{
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+
+ return create_name(buf, size, STORAGEDIR, addr, name);
+}
+
+int read_device_alias(const char *src, const char *dst, char *alias, size_t size)
+{
+ char filename[PATH_MAX + 1], *tmp;
+ int err;
+
+ create_name(filename, PATH_MAX, STORAGEDIR, src, "aliases");
+
+ tmp = textfile_get(filename, dst);
+ if (!tmp)
+ return -ENXIO;
+
+ err = snprintf(alias, size, "%s", tmp);
+
+ free(tmp);
+
+ return err < 0 ? -EIO : 0;
+}
+
+int write_device_alias(const char *src, const char *dst, const char *alias)
+{
+ char filename[PATH_MAX + 1];
+
+ create_name(filename, PATH_MAX, STORAGEDIR, src, "aliases");
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ return textfile_put(filename, dst, alias);
+}
+
+int write_discoverable_timeout(bdaddr_t *bdaddr, int timeout)
+{
+ char filename[PATH_MAX + 1], str[32];
+
+ snprintf(str, sizeof(str), "%d", timeout);
+
+ create_filename(filename, PATH_MAX, bdaddr, "config");
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ return textfile_put(filename, "discovto", str);
+}
+
+int read_discoverable_timeout(const char *src, int *timeout)
+{
+ char filename[PATH_MAX + 1], *str;
+
+ create_name(filename, PATH_MAX, STORAGEDIR, src, "config");
+
+ str = textfile_get(filename, "discovto");
+ if (!str)
+ return -ENOENT;
+
+ if (sscanf(str, "%d", timeout) != 1) {
+ free(str);
+ return -ENOENT;
+ }
+
+ free(str);
+
+ return 0;
+}
+
+int write_pairable_timeout(bdaddr_t *bdaddr, int timeout)
+{
+ char filename[PATH_MAX + 1], str[32];
+
+ snprintf(str, sizeof(str), "%d", timeout);
+
+ create_filename(filename, PATH_MAX, bdaddr, "config");
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ return textfile_put(filename, "pairto", str);
+}
+
+int read_pairable_timeout(const char *src, int *timeout)
+{
+ char filename[PATH_MAX + 1], *str;
+
+ create_name(filename, PATH_MAX, STORAGEDIR, src, "config");
+
+ str = textfile_get(filename, "pairto");
+ if (!str)
+ return -ENOENT;
+
+ if (sscanf(str, "%d", timeout) != 1) {
+ free(str);
+ return -ENOENT;
+ }
+
+ free(str);
+
+ return 0;
+}
+
+int write_device_mode(bdaddr_t *bdaddr, const char *mode)
+{
+ char filename[PATH_MAX + 1];
+
+ create_filename(filename, PATH_MAX, bdaddr, "config");
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ if (strcmp(mode, "off") != 0)
+ textfile_put(filename, "onmode", mode);
+
+ return textfile_put(filename, "mode", mode);
+}
+
+int read_device_mode(const char *src, char *mode, int length)
+{
+ char filename[PATH_MAX + 1], *str;
+
+ create_name(filename, PATH_MAX, STORAGEDIR, src, "config");
+
+ str = textfile_get(filename, "mode");
+ if (!str)
+ return -ENOENT;
+
+ strncpy(mode, str, length);
+ mode[length - 1] = '\0';
+
+ free(str);
+
+ return 0;
+}
+
+int read_on_mode(const char *src, char *mode, int length)
+{
+ char filename[PATH_MAX + 1], *str;
+
+ create_name(filename, PATH_MAX, STORAGEDIR, src, "config");
+
+ str = textfile_get(filename, "onmode");
+ if (!str)
+ return -ENOENT;
+
+ strncpy(mode, str, length);
+ mode[length - 1] = '\0';
+
+ free(str);
+
+ return 0;
+}
+
+int write_local_name(bdaddr_t *bdaddr, const char *name)
+{
+ char filename[PATH_MAX + 1], str[249];
+ int i;
+
+ memset(str, 0, sizeof(str));
+ for (i = 0; i < 248 && name[i]; i++)
+ if ((unsigned char) name[i] < 32 || name[i] == 127)
+ str[i] = '.';
+ else
+ str[i] = name[i];
+
+ create_filename(filename, PATH_MAX, bdaddr, "config");
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ return textfile_put(filename, "name", str);
+}
+
+int read_local_name(bdaddr_t *bdaddr, char *name)
+{
+ char filename[PATH_MAX + 1], *str;
+ int len;
+
+ create_filename(filename, PATH_MAX, bdaddr, "config");
+
+ str = textfile_get(filename, "name");
+ if (!str)
+ return -ENOENT;
+
+ len = strlen(str);
+ if (len > 248)
+ str[248] = '\0';
+ strcpy(name, str);
+
+ free(str);
+
+ return 0;
+}
+
+int write_local_class(bdaddr_t *bdaddr, uint8_t *class)
+{
+ char filename[PATH_MAX + 1], str[9];
+
+ sprintf(str, "0x%2.2x%2.2x%2.2x", class[2], class[1], class[0]);
+
+ create_filename(filename, PATH_MAX, bdaddr, "config");
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ return textfile_put(filename, "class", str);
+}
+
+int read_local_class(bdaddr_t *bdaddr, uint8_t *class)
+{
+ char filename[PATH_MAX + 1], tmp[3], *str;
+ int i;
+
+ create_filename(filename, PATH_MAX, bdaddr, "config");
+
+ str = textfile_get(filename, "class");
+ if (!str)
+ return -ENOENT;
+
+ memset(tmp, 0, sizeof(tmp));
+ for (i = 0; i < 3; i++) {
+ memcpy(tmp, str + (i * 2) + 2, 2);
+ class[2 - i] = (uint8_t) strtol(tmp, NULL, 16);
+ }
+
+ free(str);
+
+ return 0;
+}
+
+int write_remote_class(bdaddr_t *local, bdaddr_t *peer, uint32_t class)
+{
+ char filename[PATH_MAX + 1], addr[18], str[9];
+
+ create_filename(filename, PATH_MAX, local, "classes");
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ ba2str(peer, addr);
+ sprintf(str, "0x%6.6x", class);
+
+ return textfile_put(filename, addr, str);
+}
+
+int read_remote_class(bdaddr_t *local, bdaddr_t *peer, uint32_t *class)
+{
+ char filename[PATH_MAX + 1], addr[18], *str;
+
+ create_filename(filename, PATH_MAX, local, "classes");
+
+ ba2str(peer, addr);
+
+ str = textfile_get(filename, addr);
+ if (!str)
+ return -ENOENT;
+
+ if (sscanf(str, "%x", class) != 1) {
+ free(str);
+ return -ENOENT;
+ }
+
+ free(str);
+
+ return 0;
+}
+
+int write_device_name(bdaddr_t *local, bdaddr_t *peer, char *name)
+{
+ char filename[PATH_MAX + 1], addr[18], str[249];
+ int i;
+
+ memset(str, 0, sizeof(str));
+ for (i = 0; i < 248 && name[i]; i++)
+ if ((unsigned char) name[i] < 32 || name[i] == 127)
+ str[i] = '.';
+ else
+ str[i] = name[i];
+
+ create_filename(filename, PATH_MAX, local, "names");
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ ba2str(peer, addr);
+ return textfile_put(filename, addr, str);
+}
+
+int read_device_name(const char *src, const char *dst, char *name)
+{
+ char filename[PATH_MAX + 1], *str;
+ int len;
+
+ create_name(filename, PATH_MAX, STORAGEDIR, src, "names");
+
+ str = textfile_get(filename, dst);
+ if (!str)
+ return -ENOENT;
+
+ len = strlen(str);
+ if (len > 248)
+ str[248] = '\0';
+ strcpy(name, str);
+
+ free(str);
+
+ return 0;
+}
+
+int write_remote_eir(bdaddr_t *local, bdaddr_t *peer, uint8_t *data)
+{
+ char filename[PATH_MAX + 1], addr[18], str[481];
+ int i;
+
+ memset(str, 0, sizeof(str));
+ for (i = 0; i < 240; i++)
+ sprintf(str + (i * 2), "%2.2X", data[i]);
+
+ create_filename(filename, PATH_MAX, local, "eir");
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ ba2str(peer, addr);
+ return textfile_put(filename, addr, str);
+}
+
+int read_remote_eir(bdaddr_t *local, bdaddr_t *peer, uint8_t *data)
+{
+ char filename[PATH_MAX + 1], addr[18], *str;
+ int i;
+
+ create_filename(filename, PATH_MAX, local, "eir");
+
+ ba2str(peer, addr);
+
+ str = textfile_get(filename, addr);
+ if (!str)
+ return -ENOENT;
+
+ if (!data) {
+ free(str);
+ return 0;
+ }
+
+ if (strlen(str) < 480) {
+ free(str);
+ return -EIO;
+ }
+
+ for (i = 0; i < 240; i++)
+ sscanf(str + (i * 2), "%02hhX", &data[i]);
+
+ free(str);
+
+ return 0;
+}
+
+int write_version_info(bdaddr_t *local, bdaddr_t *peer, uint16_t manufacturer,
+ uint8_t lmp_ver, uint16_t lmp_subver)
+{
+ char filename[PATH_MAX + 1], addr[18], str[16];
+
+ memset(str, 0, sizeof(str));
+ sprintf(str, "%d %d %d", manufacturer, lmp_ver, lmp_subver);
+
+ create_filename(filename, PATH_MAX, local, "manufacturers");
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ ba2str(peer, addr);
+ return textfile_put(filename, addr, str);
+}
+
+int write_features_info(bdaddr_t *local, bdaddr_t *peer,
+ unsigned char *page1, unsigned char *page2)
+{
+ char filename[PATH_MAX + 1], addr[18];
+ char str[] = "0000000000000000 0000000000000000";
+ char *old_value;
+ int i;
+
+ ba2str(peer, addr);
+
+ create_filename(filename, PATH_MAX, local, "features");
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ old_value = textfile_get(filename, addr);
+
+ if (page1)
+ for (i = 0; i < 8; i++)
+ sprintf(str + (i * 2), "%2.2X", page1[i]);
+ else if (old_value && strlen(old_value) >= 16)
+ strncpy(str, old_value, 16);
+
+ if (page2)
+ for (i = 0; i < 8; i++)
+ sprintf(str + 17 + (i * 2), "%2.2X", page2[i]);
+ else if (old_value && strlen(old_value) >= 33)
+ strncpy(str + 17, old_value + 17, 16);
+
+ free(old_value);
+
+ return textfile_put(filename, addr, str);
+}
+
+static int decode_bytes(const char *str, unsigned char *bytes, size_t len)
+{
+ unsigned int i;
+
+ for (i = 0; i < len; i++) {
+ if (sscanf(str + (i * 2), "%02hhX", &bytes[i]) != 1)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int read_remote_features(bdaddr_t *local, bdaddr_t *peer,
+ unsigned char *page1, unsigned char *page2)
+{
+ char filename[PATH_MAX + 1], addr[18], *str;
+ size_t len;
+ int err;
+
+ if (page1 == NULL && page2 == NULL)
+ return -EINVAL;
+
+ create_filename(filename, PATH_MAX, local, "features");
+
+ ba2str(peer, addr);
+
+ str = textfile_get(filename, addr);
+ if (!str)
+ return -ENOENT;
+
+ len = strlen(str);
+
+ err = -ENOENT;
+
+ if (page1 && len >= 16)
+ err = decode_bytes(str, page1, 8);
+
+ if (page2 && len >= 33)
+ err = decode_bytes(str + 17, page2, 8);
+
+ free(str);
+
+ return err;
+}
+
+int write_lastseen_info(bdaddr_t *local, bdaddr_t *peer, struct tm *tm)
+{
+ char filename[PATH_MAX + 1], addr[18], str[24];
+
+ memset(str, 0, sizeof(str));
+ strftime(str, sizeof(str), "%Y-%m-%d %H:%M:%S %Z", tm);
+
+ create_filename(filename, PATH_MAX, local, "lastseen");
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ ba2str(peer, addr);
+ return textfile_put(filename, addr, str);
+}
+
+int write_lastused_info(bdaddr_t *local, bdaddr_t *peer, struct tm *tm)
+{
+ char filename[PATH_MAX + 1], addr[18], str[24];
+
+ memset(str, 0, sizeof(str));
+ strftime(str, sizeof(str), "%Y-%m-%d %H:%M:%S %Z", tm);
+
+ create_filename(filename, PATH_MAX, local, "lastused");
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ ba2str(peer, addr);
+ return textfile_put(filename, addr, str);
+}
+
+int write_link_key(bdaddr_t *local, bdaddr_t *peer, unsigned char *key, uint8_t type, int length)
+{
+ char filename[PATH_MAX + 1], addr[18], str[38];
+ int i;
+
+ memset(str, 0, sizeof(str));
+ for (i = 0; i < 16; i++)
+ sprintf(str + (i * 2), "%2.2X", key[i]);
+ sprintf(str + 32, " %d %d", type, length);
+
+ create_filename(filename, PATH_MAX, local, "linkkeys");
+
+ create_file(filename, S_IRUSR | S_IWUSR);
+
+ ba2str(peer, addr);
+
+ if (length < 0) {
+ char *tmp = textfile_get(filename, addr);
+ if (tmp) {
+ if (strlen(tmp) > 34)
+ memcpy(str + 34, tmp + 34, 3);
+ free(tmp);
+ }
+ }
+
+ return textfile_put(filename, addr, str);
+}
+
+int read_link_key(bdaddr_t *local, bdaddr_t *peer, unsigned char *key, uint8_t *type)
+{
+ char filename[PATH_MAX + 1], addr[18], tmp[3], *str;
+ int i;
+
+ create_filename(filename, PATH_MAX, local, "linkkeys");
+
+ ba2str(peer, addr);
+ str = textfile_get(filename, addr);
+ if (!str)
+ return -ENOENT;
+
+ if (!key) {
+ free(str);
+ return 0;
+ }
+
+ memset(tmp, 0, sizeof(tmp));
+ for (i = 0; i < 16; i++) {
+ memcpy(tmp, str + (i * 2), 2);
+ key[i] = (uint8_t) strtol(tmp, NULL, 16);
+ }
+
+ if (type) {
+ memcpy(tmp, str + 33, 2);
+ *type = (uint8_t) strtol(tmp, NULL, 10);
+ }
+
+ free(str);
+
+ return 0;
+}
+
+int read_pin_code(bdaddr_t *local, bdaddr_t *peer, char *pin)
+{
+ char filename[PATH_MAX + 1], addr[18], *str;
+ int len;
+
+ create_filename(filename, PATH_MAX, local, "pincodes");
+
+ ba2str(peer, addr);
+ str = textfile_get(filename, addr);
+ if (!str)
+ return -ENOENT;
+
+ strncpy(pin, str, 16);
+ len = strlen(pin);
+
+ free(str);
+
+ return len;
+}
+
+static GSList *service_string_to_list(char *services)
+{
+ GSList *l = NULL;
+ char *start = services;
+ int i, finished = 0;
+
+ for (i = 0; !finished; i++) {
+ if (services[i] == '\0')
+ finished = 1;
+
+ if (services[i] == ' ' || services[i] == '\0') {
+ services[i] = '\0';
+ l = g_slist_append(l, start);
+ start = services + i + 1;
+ }
+ }
+
+ return l;
+}
+
+static char *service_list_to_string(GSList *services)
+{
+ char str[1024];
+ int len = 0;
+
+ if (!services)
+ return g_strdup("");
+
+ memset(str, 0, sizeof(str));
+
+ while (services) {
+ int ret;
+ char *ident = services->data;
+
+ ret = snprintf(str + len, sizeof(str) - len - 1, "%s%s",
+ ident, services->next ? " " : "");
+
+ if (ret > 0)
+ len += ret;
+
+ services = services->next;
+ }
+
+ return g_strdup(str);
+}
+
+int write_trust(const char *src, const char *addr, const char *service,
+ gboolean trust)
+{
+ char filename[PATH_MAX + 1], *str;
+ GSList *services = NULL, *match;
+ gboolean trusted;
+ int ret;
+
+ create_name(filename, PATH_MAX, STORAGEDIR, src, "trusts");
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ str = textfile_caseget(filename, addr);
+ if (str)
+ services = service_string_to_list(str);
+
+ match = g_slist_find_custom(services, service, (GCompareFunc) strcmp);
+ trusted = match ? TRUE : FALSE;
+
+ /* If the old setting is the same as the requested one, we're done */
+ if (trusted == trust) {
+ g_slist_free(services);
+ free(str);
+ return 0;
+ }
+
+ if (trust)
+ services = g_slist_append(services, (void *) service);
+ else
+ services = g_slist_remove(services, match->data);
+
+ /* Remove the entry if the last trusted service was removed */
+ if (!trust && !services)
+ ret = textfile_casedel(filename, addr);
+ else {
+ char *new_str = service_list_to_string(services);
+ ret = textfile_caseput(filename, addr, new_str);
+ free(new_str);
+ }
+
+ g_slist_free(services);
+
+ free(str);
+
+ return ret;
+}
+
+gboolean read_trust(const bdaddr_t *local, const char *addr, const char *service)
+{
+ char filename[PATH_MAX + 1], *str;
+ GSList *services;
+ gboolean ret;
+
+ create_filename(filename, PATH_MAX, local, "trusts");
+
+ str = textfile_caseget(filename, addr);
+ if (!str)
+ return FALSE;
+
+ services = service_string_to_list(str);
+
+ if (g_slist_find_custom(services, service, (GCompareFunc) strcmp))
+ ret = TRUE;
+ else
+ ret = FALSE;
+
+ g_slist_free(services);
+ free(str);
+
+ return ret;
+}
+
+struct trust_list {
+ GSList *trusts;
+ const char *service;
+};
+
+static void append_trust(char *key, char *value, void *data)
+{
+ struct trust_list *list = data;
+
+ if (strstr(value, list->service))
+ list->trusts = g_slist_append(list->trusts, g_strdup(key));
+}
+
+GSList *list_trusts(bdaddr_t *local, const char *service)
+{
+ char filename[PATH_MAX + 1];
+ struct trust_list list;
+
+ create_filename(filename, PATH_MAX, local, "trusts");
+
+ list.trusts = NULL;
+ list.service = service;
+
+ if (textfile_foreach(filename, append_trust, &list) < 0)
+ return NULL;
+
+ return list.trusts;
+}
+
+int write_device_profiles(bdaddr_t *src, bdaddr_t *dst, const char *profiles)
+{
+ char filename[PATH_MAX + 1], addr[18];
+
+ if (!profiles)
+ return -EINVAL;
+
+ create_filename(filename, PATH_MAX, src, "profiles");
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ ba2str(dst, addr);
+ return textfile_put(filename, addr, profiles);
+}
+
+int delete_entry(bdaddr_t *src, const char *storage, const char *key)
+{
+ char filename[PATH_MAX + 1];
+
+ create_filename(filename, PATH_MAX, src, storage);
+
+ return textfile_del(filename, key);
+}
+
+int store_record(const gchar *src, const gchar *dst, sdp_record_t *rec)
+{
+ char filename[PATH_MAX + 1], key[28];
+ sdp_buf_t buf;
+ int err, size, i;
+ char *str;
+
+ create_name(filename, PATH_MAX, STORAGEDIR, src, "sdp");
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ snprintf(key, sizeof(key), "%17s#%08X", dst, rec->handle);
+
+ if (sdp_gen_record_pdu(rec, &buf) < 0)
+ return -1;
+
+ size = buf.data_size;
+
+ str = g_malloc0(size*2+1);
+
+ for (i = 0; i < size; i++)
+ sprintf(str + (i * 2), "%02X", buf.data[i]);
+
+ err = textfile_put(filename, key, str);
+
+ free(buf.data);
+ free(str);
+
+ return err;
+}
+
+sdp_record_t *record_from_string(const gchar *str)
+{
+ sdp_record_t *rec;
+ int size, i, len;
+ uint8_t *pdata;
+ char tmp[3];
+
+ size = strlen(str)/2;
+ pdata = g_malloc0(size);
+
+ tmp[2] = 0;
+ for (i = 0; i < size; i++) {
+ memcpy(tmp, str + (i * 2), 2);
+ pdata[i] = (uint8_t) strtol(tmp, NULL, 16);
+ }
+
+ rec = sdp_extract_pdu(pdata, size, &len);
+ free(pdata);
+
+ return rec;
+}
+
+
+sdp_record_t *fetch_record(const gchar *src, const gchar *dst,
+ const uint32_t handle)
+{
+ char filename[PATH_MAX + 1], key[28], *str;
+ sdp_record_t *rec;
+
+ create_name(filename, PATH_MAX, STORAGEDIR, src, "sdp");
+
+ snprintf(key, sizeof(key), "%17s#%08X", dst, handle);
+
+ str = textfile_get(filename, key);
+ if (!str)
+ return NULL;
+
+ rec = record_from_string(str);
+ free(str);
+
+ return rec;
+}
+
+int delete_record(const gchar *src, const gchar *dst, const uint32_t handle)
+{
+ char filename[PATH_MAX + 1], key[28];
+
+ create_name(filename, PATH_MAX, STORAGEDIR, src, "sdp");
+
+ snprintf(key, sizeof(key), "%17s#%08X", dst, handle);
+
+ return textfile_del(filename, key);
+}
+
+struct record_list {
+ sdp_list_t *recs;
+ const gchar *addr;
+};
+
+static void create_stored_records_from_keys(char *key, char *value,
+ void *user_data)
+{
+ struct record_list *rec_list = user_data;
+ const gchar *addr = rec_list->addr;
+ sdp_record_t *rec;
+
+ if (strncmp(key, addr, 17))
+ return;
+
+ rec = record_from_string(value);
+
+ rec_list->recs = sdp_list_append(rec_list->recs, rec);
+}
+
+void delete_all_records(const bdaddr_t *src, const bdaddr_t *dst)
+{
+ sdp_list_t *records, *seq;
+ char srcaddr[18], dstaddr[18];
+
+ ba2str(src, srcaddr);
+ ba2str(dst, dstaddr);
+
+ records = read_records(src, dst);
+
+ for (seq = records; seq; seq = seq->next) {
+ sdp_record_t *rec = seq->data;
+ delete_record(srcaddr, dstaddr, rec->handle);
+ }
+
+ if (records)
+ sdp_list_free(records, (sdp_free_func_t) sdp_record_free);
+}
+
+sdp_list_t *read_records(const bdaddr_t *src, const bdaddr_t *dst)
+{
+ char filename[PATH_MAX + 1];
+ struct record_list rec_list;
+ char srcaddr[18], dstaddr[18];
+
+ ba2str(src, srcaddr);
+ ba2str(dst, dstaddr);
+
+ rec_list.addr = dstaddr;
+ rec_list.recs = NULL;
+
+ create_name(filename, PATH_MAX, STORAGEDIR, srcaddr, "sdp");
+ textfile_foreach(filename, create_stored_records_from_keys, &rec_list);
+
+ return rec_list.recs;
+}
+
+sdp_record_t *find_record_in_list(sdp_list_t *recs, const char *uuid)
+{
+ sdp_list_t *seq;
+
+ for (seq = recs; seq; seq = seq->next) {
+ sdp_record_t *rec = (sdp_record_t *) seq->data;
+ sdp_list_t *svcclass = NULL;
+ char *uuid_str;
+
+ if (sdp_get_service_classes(rec, &svcclass) < 0)
+ continue;
+
+ /* Extract the uuid */
+ uuid_str = bt_uuid2string(svcclass->data);
+ if (!uuid_str)
+ continue;
+
+ if (!strcasecmp(uuid_str, uuid)) {
+ sdp_list_free(svcclass, free);
+ free(uuid_str);
+ return rec;
+ }
+
+ sdp_list_free(svcclass, free);
+ free(uuid_str);
+ }
+ return NULL;
+}
+
+int store_device_id(const gchar *src, const gchar *dst,
+ const uint16_t source, const uint16_t vendor,
+ const uint16_t product, const uint16_t version)
+{
+ char filename[PATH_MAX + 1], str[20];
+
+ create_name(filename, PATH_MAX, STORAGEDIR, src, "did");
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ snprintf(str, sizeof(str), "%04X %04X %04X %04X", source,
+ vendor, product, version);
+
+ return textfile_put(filename, dst, str);
+}
+
+static int read_device_id_from_did(const gchar *src, const gchar *dst,
+ uint16_t *source, uint16_t *vendor,
+ uint16_t *product, uint16_t *version)
+{
+ char filename[PATH_MAX + 1];
+ char *str, *vendor_str, *product_str, *version_str;
+
+ create_name(filename, PATH_MAX, STORAGEDIR, src, "did");
+
+ str = textfile_get(filename, dst);
+ if (!str)
+ return -ENOENT;
+
+ vendor_str = strchr(str, ' ');
+ if (!vendor_str) {
+ free(str);
+ return -ENOENT;
+ }
+ *(vendor_str++) = 0;
+
+ product_str = strchr(vendor_str, ' ');
+ if (!product_str) {
+ free(str);
+ return -ENOENT;
+ }
+ *(product_str++) = 0;
+
+ version_str = strchr(product_str, ' ');
+ if (!version_str) {
+ free(str);
+ return -ENOENT;
+ }
+ *(version_str++) = 0;
+
+ if (source)
+ *source = (uint16_t) strtol(str, NULL, 16);
+ if (vendor)
+ *vendor = (uint16_t) strtol(vendor_str, NULL, 16);
+ if (product)
+ *product = (uint16_t) strtol(product_str, NULL, 16);
+ if (version)
+ *version = (uint16_t) strtol(version_str, NULL, 16);
+
+ free(str);
+
+ return 0;
+}
+
+int read_device_id(const gchar *srcaddr, const gchar *dstaddr,
+ uint16_t *source, uint16_t *vendor,
+ uint16_t *product, uint16_t *version)
+{
+ uint16_t lsource, lvendor, lproduct, lversion;
+ sdp_list_t *recs;
+ sdp_record_t *rec;
+ bdaddr_t src, dst;
+ int err;
+
+ err = read_device_id_from_did(srcaddr, dstaddr, &lsource,
+ vendor, product, version);
+ if (!err) {
+ if (lsource == 0xffff)
+ err = -ENOENT;
+
+ return err;
+ }
+
+ str2ba(srcaddr, &src);
+ str2ba(dstaddr, &dst);
+
+ recs = read_records(&src, &dst);
+ rec = find_record_in_list(recs, PNP_UUID);
+
+ if (rec) {
+ sdp_data_t *pdlist;
+
+ pdlist = sdp_data_get(rec, SDP_ATTR_VENDOR_ID_SOURCE);
+ lsource = pdlist ? pdlist->val.uint16 : 0x0000;
+
+ pdlist = sdp_data_get(rec, SDP_ATTR_VENDOR_ID);
+ lvendor = pdlist ? pdlist->val.uint16 : 0x0000;
+
+ pdlist = sdp_data_get(rec, SDP_ATTR_PRODUCT_ID);
+ lproduct = pdlist ? pdlist->val.uint16 : 0x0000;
+
+ pdlist = sdp_data_get(rec, SDP_ATTR_VERSION);
+ lversion = pdlist ? pdlist->val.uint16 : 0x0000;
+
+ err = 0;
+ }
+
+ sdp_list_free(recs, (sdp_free_func_t)sdp_record_free);
+
+ if (err) {
+ /* FIXME: We should try EIR data if we have it, too */
+
+ /* If we don't have the data, we don't want to go through the
+ * above search every time. */
+ lsource = 0xffff;
+ lvendor = 0x0000;
+ lproduct = 0x0000;
+ lversion = 0x0000;
+ }
+
+ store_device_id(srcaddr, dstaddr, lsource, lvendor, lproduct, lversion);
+
+ if (err)
+ return err;
+
+ if (source)
+ *source = lsource;
+ if (vendor)
+ *vendor = lvendor;
+ if (product)
+ *product = lproduct;
+ if (version)
+ *version = lversion;
+
+ return 0;
+}
+
+int write_device_pairable(bdaddr_t *bdaddr, gboolean mode)
+{
+ char filename[PATH_MAX + 1];
+
+ create_filename(filename, PATH_MAX, bdaddr, "config");
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ return textfile_put(filename, "pairable", mode ? "yes" : "no");
+}
+
+int read_device_pairable(bdaddr_t *bdaddr, gboolean *mode)
+{
+ char filename[PATH_MAX + 1], *str;
+
+ create_filename(filename, PATH_MAX, bdaddr, "config");
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ str = textfile_get(filename, "pairable");
+ if (!str)
+ return -ENOENT;
+
+ *mode = strcmp(str, "yes") == 0 ? TRUE : FALSE;
+
+ free(str);
+
+ return 0;
+}
+
+gboolean read_blocked(const bdaddr_t *local, const bdaddr_t *remote)
+{
+ char filename[PATH_MAX + 1], *str, addr[18];
+
+ create_filename(filename, PATH_MAX, local, "blocked");
+
+ ba2str(remote, addr);
+
+ str = textfile_caseget(filename, addr);
+ if (!str)
+ return FALSE;
+
+ free(str);
+
+ return TRUE;
+}
+
+int write_blocked(const bdaddr_t *local, const bdaddr_t *remote,
+ gboolean blocked)
+{
+ char filename[PATH_MAX + 1], addr[18];
+
+ create_filename(filename, PATH_MAX, local, "blocked");
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ ba2str(remote, addr);
+
+ if (blocked == FALSE)
+ return textfile_casedel(filename, addr);
+
+ return textfile_caseput(filename, addr, "");
+}
+
+int write_device_services(const bdaddr_t *sba, const bdaddr_t *dba,
+ const char *services)
+{
+ char filename[PATH_MAX + 1], addr[18];
+
+ create_filename(filename, PATH_MAX, sba, "primary");
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ ba2str(dba, addr);
+
+ return textfile_put(filename, addr, services);
+}
+
+static void filter_keys(char *key, char *value, void *data)
+{
+ struct match *match = data;
+ const char *address = match->pattern;
+
+ /* Each key contains: MAC#handle*/
+ if (strncasecmp(key, address, 17) == 0)
+ match->keys = g_slist_append(match->keys, g_strdup(key));
+}
+
+int delete_device_service(const bdaddr_t *sba, const bdaddr_t *dba)
+{
+ GSList *l;
+ struct match match;
+ char filename[PATH_MAX + 1], address[18];
+ int err;
+
+ create_filename(filename, PATH_MAX, sba, "primary");
+
+ memset(address, 0, sizeof(address));
+ ba2str(dba, address);
+
+ err = textfile_del(filename, address);
+ if (err < 0)
+ return err;
+
+ /* Deleting all characteristics of a given address */
+ memset(&match, 0, sizeof(match));
+ match.pattern = address;
+
+ create_filename(filename, PATH_MAX, sba, "characteristic");
+ err = textfile_foreach(filename, filter_keys, &match);
+ if (err < 0)
+ return err;
+
+ for (l = match.keys; l; l = l->next) {
+ const char *key = l->data;
+ textfile_del(filename, key);
+ }
+
+ g_slist_foreach(match.keys, (GFunc) g_free, NULL);
+ g_slist_free(match.keys);
+
+ /* Deleting all attributes values of a given address */
+ memset(&match, 0, sizeof(match));
+ match.pattern = address;
+
+ create_filename(filename, PATH_MAX, sba, "attributes");
+ err = textfile_foreach(filename, filter_keys, &match);
+ if (err < 0)
+ return err;
+
+ for (l = match.keys; l; l = l->next) {
+ const char *key = l->data;
+ textfile_del(filename, key);
+ }
+
+ g_slist_foreach(match.keys, (GFunc) g_free, NULL);
+ g_slist_free(match.keys);
+
+ return 0;
+}
+
+char *read_device_services(const bdaddr_t *sba, const bdaddr_t *dba)
+{
+ char filename[PATH_MAX + 1], addr[18];
+
+ create_filename(filename, PATH_MAX, sba, "primary");
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ ba2str(dba, addr);
+
+ return textfile_caseget(filename, addr);
+}
+
+int write_device_characteristics(const bdaddr_t *sba, const bdaddr_t *dba,
+ uint16_t handle, const char *chars)
+{
+ char filename[PATH_MAX + 1], addr[18], key[23];
+
+ create_filename(filename, PATH_MAX, sba, "characteristic");
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ ba2str(dba, addr);
+
+ snprintf(key, sizeof(key), "%17s#%04X", addr, handle);
+
+ return textfile_put(filename, key, chars);
+}
+
+char *read_device_characteristics(const bdaddr_t *sba, const bdaddr_t *dba,
+ uint16_t handle)
+{
+ char filename[PATH_MAX + 1], addr[18], key[23];
+
+ create_filename(filename, PATH_MAX, sba, "characteristic");
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ ba2str(dba, addr);
+
+ snprintf(key, sizeof(key), "%17s#%04X", addr, handle);
+
+ return textfile_caseget(filename, key);
+}
+
+int write_device_attribute(const bdaddr_t *sba, const bdaddr_t *dba,
+ uint16_t handle, const char *chars)
+{
+ char filename[PATH_MAX + 1], addr[18], key[23];
+
+ create_filename(filename, PATH_MAX, sba, "attributes");
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ ba2str(dba, addr);
+
+ snprintf(key, sizeof(key), "%17s#%04X", addr, handle);
+
+ return textfile_put(filename, key, chars);
+}
+
+int read_device_attributes(const bdaddr_t *sba, textfile_cb func, void *data)
+{
+ char filename[PATH_MAX + 1];
+
+ create_filename(filename, PATH_MAX, sba, "attributes");
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ return textfile_foreach(filename, func, data);
+}
+
+int write_device_type(const bdaddr_t *sba, const bdaddr_t *dba,
+ device_type_t type)
+{
+ char filename[PATH_MAX + 1], addr[18], chars[3];
+
+ create_filename(filename, PATH_MAX, sba, "types");
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ ba2str(dba, addr);
+
+ snprintf(chars, sizeof(chars), "%2.2X", type);
+
+ return textfile_put(filename, addr, chars);
+}
+
+device_type_t read_device_type(const bdaddr_t *sba, const bdaddr_t *dba)
+{
+ char filename[PATH_MAX + 1], addr[18], *chars;
+ device_type_t type;
+
+ create_filename(filename, PATH_MAX, sba, "types");
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ ba2str(dba, addr);
+
+ chars = textfile_caseget(filename, addr);
+ if (chars == NULL)
+ return DEVICE_TYPE_UNKNOWN;
+
+ type = strtol(chars, NULL, 16);
+
+ free(chars);
+
+ return type;
+}
diff --git a/src/storage.h b/src/storage.h
new file mode 100644
index 0000000..6929ada
--- /dev/null
+++ b/src/storage.h
@@ -0,0 +1,92 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2010 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
+ *
+ */
+
+#include "textfile.h"
+
+int read_device_alias(const char *src, const char *dst, char *alias, size_t size);
+int write_device_alias(const char *src, const char *dst, const char *alias);
+int write_discoverable_timeout(bdaddr_t *bdaddr, int timeout);
+int read_discoverable_timeout(const char *src, int *timeout);
+int write_pairable_timeout(bdaddr_t *bdaddr, int timeout);
+int read_pairable_timeout(const char *src, int *timeout);
+int write_device_mode(bdaddr_t *bdaddr, const char *mode);
+int read_device_mode(const char *src, char *mode, int length);
+int read_on_mode(const char *src, char *mode, int length);
+int write_local_name(bdaddr_t *bdaddr, const char *name);
+int read_local_name(bdaddr_t *bdaddr, char *name);
+int write_local_class(bdaddr_t *bdaddr, uint8_t *class);
+int read_local_class(bdaddr_t *bdaddr, uint8_t *class);
+int write_remote_class(bdaddr_t *local, bdaddr_t *peer, uint32_t class);
+int read_remote_class(bdaddr_t *local, bdaddr_t *peer, uint32_t *class);
+int write_device_name(bdaddr_t *local, bdaddr_t *peer, char *name);
+int read_device_name(const char *src, const char *dst, char *name);
+int write_remote_eir(bdaddr_t *local, bdaddr_t *peer, uint8_t *data);
+int read_remote_eir(bdaddr_t *local, bdaddr_t *peer, uint8_t *data);
+int write_version_info(bdaddr_t *local, bdaddr_t *peer, uint16_t manufacturer, uint8_t lmp_ver, uint16_t lmp_subver);
+int write_features_info(bdaddr_t *local, bdaddr_t *peer, unsigned char *page1, unsigned char *page2);
+int read_remote_features(bdaddr_t *local, bdaddr_t *peer, unsigned char *page1, unsigned char *page2);
+int write_lastseen_info(bdaddr_t *local, bdaddr_t *peer, struct tm *tm);
+int write_lastused_info(bdaddr_t *local, bdaddr_t *peer, struct tm *tm);
+int write_link_key(bdaddr_t *local, bdaddr_t *peer, unsigned char *key, uint8_t type, int length);
+int read_link_key(bdaddr_t *local, bdaddr_t *peer, unsigned char *key, uint8_t *type);
+int read_pin_code(bdaddr_t *local, bdaddr_t *peer, char *pin);
+gboolean read_trust(const bdaddr_t *local, const char *addr, const char *service);
+int write_trust(const char *src, const char *addr, const char *service, gboolean trust);
+GSList *list_trusts(bdaddr_t *local, const char *service);
+int write_device_profiles(bdaddr_t *src, bdaddr_t *dst, const char *profiles);
+int delete_entry(bdaddr_t *src, const char *storage, const char *key);
+int store_record(const gchar *src, const gchar *dst, sdp_record_t *rec);
+sdp_record_t *record_from_string(const gchar *str);
+sdp_record_t *fetch_record(const gchar *src, const gchar *dst, const uint32_t handle);
+int delete_record(const gchar *src, const gchar *dst, const uint32_t handle);
+void delete_all_records(const bdaddr_t *src, const bdaddr_t *dst);
+sdp_list_t *read_records(const bdaddr_t *src, const bdaddr_t *dst);
+sdp_record_t *find_record_in_list(sdp_list_t *recs, const char *uuid);
+int store_device_id(const gchar *src, const gchar *dst,
+ const uint16_t source, const uint16_t vendor,
+ const uint16_t product, const uint16_t version);
+int read_device_id(const gchar *src, const gchar *dst,
+ uint16_t *source, uint16_t *vendor,
+ uint16_t *product, uint16_t *version);
+int write_device_pairable(bdaddr_t *local, gboolean mode);
+int read_device_pairable(bdaddr_t *local, gboolean *mode);
+gboolean read_blocked(const bdaddr_t *local, const bdaddr_t *remote);
+int write_blocked(const bdaddr_t *local, const bdaddr_t *remote,
+ gboolean blocked);
+int write_device_services(const bdaddr_t *sba, const bdaddr_t *dba,
+ const char *services);
+int delete_device_service(const bdaddr_t *sba, const bdaddr_t *dba);
+char *read_device_services(const bdaddr_t *sba, const bdaddr_t *dba);
+int write_device_characteristics(const bdaddr_t *sba, const bdaddr_t *dba,
+ uint16_t handle, const char *chars);
+char *read_device_characteristics(const bdaddr_t *sba, const bdaddr_t *dba,
+ uint16_t handle);
+int write_device_attribute(const bdaddr_t *sba, const bdaddr_t *dba,
+ uint16_t handle, const char *chars);
+int read_device_attributes(const bdaddr_t *sba, textfile_cb func, void *data);
+int write_device_type(const bdaddr_t *sba, const bdaddr_t *dba,
+ device_type_t type);
+device_type_t read_device_type(const bdaddr_t *sba, const bdaddr_t *dba);
+
+#define PNP_UUID "00001200-0000-1000-8000-00805f9b34fb"
+
diff --git a/src/textfile.c b/src/textfile.c
new file mode 100644
index 0000000..d115ff6
--- /dev/null
+++ b/src/textfile.c
@@ -0,0 +1,492 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+
+#include "textfile.h"
+
+int create_dirs(const char *filename, const mode_t mode)
+{
+ struct stat st;
+ char dir[PATH_MAX + 1], *prev, *next;
+ int err;
+
+ err = stat(filename, &st);
+ if (!err && S_ISREG(st.st_mode))
+ return 0;
+
+ memset(dir, 0, PATH_MAX + 1);
+ strcat(dir, "/");
+
+ prev = strchr(filename, '/');
+
+ while (prev) {
+ next = strchr(prev + 1, '/');
+ if (!next)
+ break;
+
+ if (next - prev == 1) {
+ prev = next;
+ continue;
+ }
+
+ strncat(dir, prev + 1, next - prev);
+ mkdir(dir, mode);
+
+ prev = next;
+ }
+
+ return 0;
+}
+
+int create_file(const char *filename, const mode_t mode)
+{
+ int fd;
+
+ umask(S_IWGRP | S_IWOTH);
+ create_dirs(filename, S_IRUSR | S_IWUSR | S_IXUSR |
+ S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
+
+ fd = open(filename, O_RDWR | O_CREAT, mode);
+ if (fd < 0)
+ return fd;
+
+ close(fd);
+
+ return 0;
+}
+
+int create_name(char *buf, size_t size, const char *path, const char *address, const char *name)
+{
+ return snprintf(buf, size, "%s/%s/%s", path, address, name);
+}
+
+static inline char *find_key(char *map, size_t size, const char *key, size_t len, int icase)
+{
+ char *ptr = map;
+ size_t ptrlen = size;
+
+ while (ptrlen > len + 1) {
+ int cmp = (icase) ? strncasecmp(ptr, key, len) : strncmp(ptr, key, len);
+ if (cmp == 0) {
+ if (ptr == map)
+ return ptr;
+
+ if ((*(ptr - 1) == '\r' || *(ptr - 1) == '\n') &&
+ *(ptr + len) == ' ')
+ return ptr;
+ }
+
+ if (icase) {
+ char *p1 = memchr(ptr + 1, tolower(*key), ptrlen - 1);
+ char *p2 = memchr(ptr + 1, toupper(*key), ptrlen - 1);
+
+ if (!p1)
+ ptr = p2;
+ else if (!p2)
+ ptr = p1;
+ else
+ ptr = (p1 < p2) ? p1 : p2;
+ } else
+ ptr = memchr(ptr + 1, *key, ptrlen - 1);
+
+ if (!ptr)
+ return NULL;
+
+ ptrlen = size - (ptr - map);
+ }
+
+ return NULL;
+}
+
+static inline int write_key_value(int fd, const char *key, const char *value)
+{
+ char *str;
+ size_t size;
+ int err = 0;
+
+ size = strlen(key) + strlen(value) + 2;
+
+ str = malloc(size + 1);
+ if (!str)
+ return ENOMEM;
+
+ sprintf(str, "%s %s\n", key, value);
+
+ if (write(fd, str, size) < 0)
+ err = errno;
+
+ free(str);
+
+ return err;
+}
+
+static char *strnpbrk(const char *s, ssize_t len, const char *accept)
+{
+ const char *p = s;
+ const char *end;
+
+ end = s + len - 1;
+
+ while (p <= end && *p) {
+ const char *a = accept;
+
+ while (*a) {
+ if (*p == *a)
+ return (char *) p;
+ a++;
+ }
+
+ p++;
+ }
+
+ return NULL;
+}
+
+static int write_key(const char *pathname, const char *key, const char *value, int icase)
+{
+ struct stat st;
+ char *map, *off, *end, *str;
+ off_t size, pos; size_t base;
+ int fd, len, err = 0;
+
+ fd = open(pathname, O_RDWR);
+ if (fd < 0)
+ return -errno;
+
+ if (flock(fd, LOCK_EX) < 0) {
+ err = errno;
+ goto close;
+ }
+
+ if (fstat(fd, &st) < 0) {
+ err = errno;
+ goto unlock;
+ }
+
+ size = st.st_size;
+
+ if (!size) {
+ if (value) {
+ pos = lseek(fd, size, SEEK_SET);
+ err = write_key_value(fd, key, value);
+ }
+ goto unlock;
+ }
+
+ map = mmap(NULL, size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_LOCKED, fd, 0);
+ if (!map || map == MAP_FAILED) {
+ err = errno;
+ goto unlock;
+ }
+
+ len = strlen(key);
+ off = find_key(map, size, key, len, icase);
+ if (!off) {
+ if (value) {
+ munmap(map, size);
+ pos = lseek(fd, size, SEEK_SET);
+ err = write_key_value(fd, key, value);
+ }
+ goto unlock;
+ }
+
+ base = off - map;
+
+ end = strnpbrk(off, size, "\r\n");
+ if (!end) {
+ err = EILSEQ;
+ goto unmap;
+ }
+
+ if (value && ((ssize_t) strlen(value) == end - off - len - 1) &&
+ !strncmp(off + len + 1, value, end - off - len - 1))
+ goto unmap;
+
+ len = strspn(end, "\r\n");
+ end += len;
+
+ len = size - (end - map);
+ if (!len) {
+ munmap(map, size);
+ if (ftruncate(fd, base) < 0) {
+ err = errno;
+ goto unlock;
+ }
+ pos = lseek(fd, base, SEEK_SET);
+ if (value)
+ err = write_key_value(fd, key, value);
+
+ goto unlock;
+ }
+
+ if (len < 0 || len > size) {
+ err = EILSEQ;
+ goto unmap;
+ }
+
+ str = malloc(len);
+ if (!str) {
+ err = errno;
+ goto unmap;
+ }
+
+ memcpy(str, end, len);
+
+ munmap(map, size);
+ if (ftruncate(fd, base) < 0) {
+ err = errno;
+ free(str);
+ goto unlock;
+ }
+ pos = lseek(fd, base, SEEK_SET);
+ if (value)
+ err = write_key_value(fd, key, value);
+
+ if (write(fd, str, len) < 0)
+ err = errno;
+
+ free(str);
+
+ goto unlock;
+
+unmap:
+ munmap(map, size);
+
+unlock:
+ flock(fd, LOCK_UN);
+
+close:
+ fdatasync(fd);
+
+ close(fd);
+ errno = err;
+
+ return -err;
+}
+
+static char *read_key(const char *pathname, const char *key, int icase)
+{
+ struct stat st;
+ char *map, *off, *end, *str = NULL;
+ off_t size; size_t len;
+ int fd, err = 0;
+
+ fd = open(pathname, O_RDONLY);
+ if (fd < 0)
+ return NULL;
+
+ if (flock(fd, LOCK_SH) < 0) {
+ err = errno;
+ goto close;
+ }
+
+ if (fstat(fd, &st) < 0) {
+ err = errno;
+ goto unlock;
+ }
+
+ size = st.st_size;
+
+ map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+ if (!map || map == MAP_FAILED) {
+ err = errno;
+ goto unlock;
+ }
+
+ len = strlen(key);
+ off = find_key(map, size, key, len, icase);
+ if (!off) {
+ err = EILSEQ;
+ goto unmap;
+ }
+
+ end = strnpbrk(off, size - (map - off), "\r\n");
+ if (!end) {
+ err = EILSEQ;
+ goto unmap;
+ }
+
+ str = malloc(end - off - len);
+ if (!str) {
+ err = EILSEQ;
+ goto unmap;
+ }
+
+ memset(str, 0, end - off - len);
+ strncpy(str, off + len + 1, end - off - len - 1);
+
+unmap:
+ munmap(map, size);
+
+unlock:
+ flock(fd, LOCK_UN);
+
+close:
+ close(fd);
+ errno = err;
+
+ return str;
+}
+
+int textfile_put(const char *pathname, const char *key, const char *value)
+{
+ return write_key(pathname, key, value, 0);
+}
+
+int textfile_caseput(const char *pathname, const char *key, const char *value)
+{
+ return write_key(pathname, key, value, 1);
+}
+
+int textfile_del(const char *pathname, const char *key)
+{
+ return write_key(pathname, key, NULL, 0);
+}
+
+int textfile_casedel(const char *pathname, const char *key)
+{
+ return write_key(pathname, key, NULL, 1);
+}
+
+char *textfile_get(const char *pathname, const char *key)
+{
+ return read_key(pathname, key, 0);
+}
+
+char *textfile_caseget(const char *pathname, const char *key)
+{
+ return read_key(pathname, key, 1);
+}
+
+int textfile_foreach(const char *pathname, textfile_cb func, void *data)
+{
+ struct stat st;
+ char *map, *off, *end, *key, *value;
+ off_t size; size_t len;
+ int fd, err = 0;
+
+ fd = open(pathname, O_RDONLY);
+ if (fd < 0)
+ return -errno;
+
+ if (flock(fd, LOCK_SH) < 0) {
+ err = errno;
+ goto close;
+ }
+
+ if (fstat(fd, &st) < 0) {
+ err = errno;
+ goto unlock;
+ }
+
+ size = st.st_size;
+
+ map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+ if (!map || map == MAP_FAILED) {
+ err = errno;
+ goto unlock;
+ }
+
+ off = map;
+
+ while (size - (off - map) > 0) {
+ end = strnpbrk(off, size - (off - map), " ");
+ if (!end) {
+ err = EILSEQ;
+ break;
+ }
+
+ len = end - off;
+
+ key = malloc(len + 1);
+ if (!key) {
+ err = errno;
+ break;
+ }
+
+ memset(key, 0, len + 1);
+ memcpy(key, off, len);
+
+ off = end + 1;
+
+ if (size - (off - map) < 0) {
+ err = EILSEQ;
+ free(key);
+ break;
+ }
+
+ end = strnpbrk(off, size - (off - map), "\r\n");
+ if (!end) {
+ err = EILSEQ;
+ free(key);
+ break;
+ }
+
+ len = end - off;
+
+ value = malloc(len + 1);
+ if (!value) {
+ err = errno;
+ free(key);
+ break;
+ }
+
+ memset(value, 0, len + 1);
+ memcpy(value, off, len);
+
+ func(key, value, data);
+
+ free(key);
+ free(value);
+
+ off = end + 1;
+ }
+
+ munmap(map, size);
+
+unlock:
+ flock(fd, LOCK_UN);
+
+close:
+ close(fd);
+ errno = err;
+
+ return 0;
+}
diff --git a/src/textfile.h b/src/textfile.h
new file mode 100644
index 0000000..dc5fc2b
--- /dev/null
+++ b/src/textfile.h
@@ -0,0 +1,43 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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
+ *
+ */
+
+#ifndef __TEXTFILE_H
+#define __TEXTFILE_H
+
+int create_dirs(const char *filename, const mode_t mode);
+int create_file(const char *filename, const mode_t mode);
+int create_name(char *buf, size_t size, const char *path,
+ const char *address, const char *name);
+
+int textfile_put(const char *pathname, const char *key, const char *value);
+int textfile_caseput(const char *pathname, const char *key, const char *value);
+int textfile_del(const char *pathname, const char *key);
+int textfile_casedel(const char *pathname, const char *key);
+char *textfile_get(const char *pathname, const char *key);
+char *textfile_caseget(const char *pathname, const char *key);
+
+typedef void (*textfile_cb) (char *key, char *value, void *data);
+
+int textfile_foreach(const char *pathname, textfile_cb func, void *data);
+
+#endif /* __TEXTFILE_H */
diff --git a/src/uinput.h b/src/uinput.h
new file mode 100644
index 0000000..e83986b
--- /dev/null
+++ b/src/uinput.h
@@ -0,0 +1,724 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2003-2010 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
+ *
+ */
+
+#ifndef __UINPUT_H
+#define __UINPUT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+
+/* Events */
+
+#define EV_SYN 0x00
+#define EV_KEY 0x01
+#define EV_REL 0x02
+#define EV_ABS 0x03
+#define EV_MSC 0x04
+#define EV_LED 0x11
+#define EV_SND 0x12
+#define EV_REP 0x14
+#define EV_FF 0x15
+#define EV_PWR 0x16
+#define EV_FF_STATUS 0x17
+#define EV_MAX 0x1f
+
+/* Synchronization events */
+
+#define SYN_REPORT 0
+#define SYN_CONFIG 1
+
+/*
+ * Keys and buttons
+ *
+ * Most of the keys/buttons are modeled after USB HUT 1.12
+ * (see http://www.usb.org/developers/hidpage).
+ * Abbreviations in the comments:
+ * AC - Application Control
+ * AL - Application Launch Button
+ * SC - System Control
+ */
+
+#define KEY_RESERVED 0
+#define KEY_ESC 1
+#define KEY_1 2
+#define KEY_2 3
+#define KEY_3 4
+#define KEY_4 5
+#define KEY_5 6
+#define KEY_6 7
+#define KEY_7 8
+#define KEY_8 9
+#define KEY_9 10
+#define KEY_0 11
+#define KEY_MINUS 12
+#define KEY_EQUAL 13
+#define KEY_BACKSPACE 14
+#define KEY_TAB 15
+#define KEY_Q 16
+#define KEY_W 17
+#define KEY_E 18
+#define KEY_R 19
+#define KEY_T 20
+#define KEY_Y 21
+#define KEY_U 22
+#define KEY_I 23
+#define KEY_O 24
+#define KEY_P 25
+#define KEY_LEFTBRACE 26
+#define KEY_RIGHTBRACE 27
+#define KEY_ENTER 28
+#define KEY_LEFTCTRL 29
+#define KEY_A 30
+#define KEY_S 31
+#define KEY_D 32
+#define KEY_F 33
+#define KEY_G 34
+#define KEY_H 35
+#define KEY_J 36
+#define KEY_K 37
+#define KEY_L 38
+#define KEY_SEMICOLON 39
+#define KEY_APOSTROPHE 40
+#define KEY_GRAVE 41
+#define KEY_LEFTSHIFT 42
+#define KEY_BACKSLASH 43
+#define KEY_Z 44
+#define KEY_X 45
+#define KEY_C 46
+#define KEY_V 47
+#define KEY_B 48
+#define KEY_N 49
+#define KEY_M 50
+#define KEY_COMMA 51
+#define KEY_DOT 52
+#define KEY_SLASH 53
+#define KEY_RIGHTSHIFT 54
+#define KEY_KPASTERISK 55
+#define KEY_LEFTALT 56
+#define KEY_SPACE 57
+#define KEY_CAPSLOCK 58
+#define KEY_F1 59
+#define KEY_F2 60
+#define KEY_F3 61
+#define KEY_F4 62
+#define KEY_F5 63
+#define KEY_F6 64
+#define KEY_F7 65
+#define KEY_F8 66
+#define KEY_F9 67
+#define KEY_F10 68
+#define KEY_NUMLOCK 69
+#define KEY_SCROLLLOCK 70
+#define KEY_KP7 71
+#define KEY_KP8 72
+#define KEY_KP9 73
+#define KEY_KPMINUS 74
+#define KEY_KP4 75
+#define KEY_KP5 76
+#define KEY_KP6 77
+#define KEY_KPPLUS 78
+#define KEY_KP1 79
+#define KEY_KP2 80
+#define KEY_KP3 81
+#define KEY_KP0 82
+#define KEY_KPDOT 83
+
+#define KEY_ZENKAKUHANKAKU 85
+#define KEY_102ND 86
+#define KEY_F11 87
+#define KEY_F12 88
+#define KEY_RO 89
+#define KEY_KATAKANA 90
+#define KEY_HIRAGANA 91
+#define KEY_HENKAN 92
+#define KEY_KATAKANAHIRAGANA 93
+#define KEY_MUHENKAN 94
+#define KEY_KPJPCOMMA 95
+#define KEY_KPENTER 96
+#define KEY_RIGHTCTRL 97
+#define KEY_KPSLASH 98
+#define KEY_SYSRQ 99
+#define KEY_RIGHTALT 100
+#define KEY_LINEFEED 101
+#define KEY_HOME 102
+#define KEY_UP 103
+#define KEY_PAGEUP 104
+#define KEY_LEFT 105
+#define KEY_RIGHT 106
+#define KEY_END 107
+#define KEY_DOWN 108
+#define KEY_PAGEDOWN 109
+#define KEY_INSERT 110
+#define KEY_DELETE 111
+#define KEY_MACRO 112
+#define KEY_MUTE 113
+#define KEY_VOLUMEDOWN 114
+#define KEY_VOLUMEUP 115
+#define KEY_POWER 116 /* SC System Power Down */
+#define KEY_KPEQUAL 117
+#define KEY_KPPLUSMINUS 118
+#define KEY_PAUSE 119
+
+#define KEY_KPCOMMA 121
+#define KEY_HANGEUL 122
+#define KEY_HANGUEL KEY_HANGEUL
+#define KEY_HANJA 123
+#define KEY_YEN 124
+#define KEY_LEFTMETA 125
+#define KEY_RIGHTMETA 126
+#define KEY_COMPOSE 127
+
+#define KEY_STOP 128 /* AC Stop */
+#define KEY_AGAIN 129
+#define KEY_PROPS 130 /* AC Properties */
+#define KEY_UNDO 131 /* AC Undo */
+#define KEY_FRONT 132
+#define KEY_COPY 133 /* AC Copy */
+#define KEY_OPEN 134 /* AC Open */
+#define KEY_PASTE 135 /* AC Paste */
+#define KEY_FIND 136 /* AC Search */
+#define KEY_CUT 137 /* AC Cut */
+#define KEY_HELP 138 /* AL Integrated Help Center */
+#define KEY_MENU 139 /* Menu (show menu) */
+#define KEY_CALC 140 /* AL Calculator */
+#define KEY_SETUP 141
+#define KEY_SLEEP 142 /* SC System Sleep */
+#define KEY_WAKEUP 143 /* System Wake Up */
+#define KEY_FILE 144 /* AL Local Machine Browser */
+#define KEY_SENDFILE 145
+#define KEY_DELETEFILE 146
+#define KEY_XFER 147
+#define KEY_PROG1 148
+#define KEY_PROG2 149
+#define KEY_WWW 150 /* AL Internet Browser */
+#define KEY_MSDOS 151
+#define KEY_COFFEE 152 /* AL Terminal Lock/Screensaver */
+#define KEY_SCREENLOCK KEY_COFFEE
+#define KEY_DIRECTION 153
+#define KEY_CYCLEWINDOWS 154
+#define KEY_MAIL 155
+#define KEY_BOOKMARKS 156 /* AC Bookmarks */
+#define KEY_COMPUTER 157
+#define KEY_BACK 158 /* AC Back */
+#define KEY_FORWARD 159 /* AC Forward */
+#define KEY_CLOSECD 160
+#define KEY_EJECTCD 161
+#define KEY_EJECTCLOSECD 162
+#define KEY_NEXTSONG 163
+#define KEY_PLAYPAUSE 164
+#define KEY_PREVIOUSSONG 165
+#define KEY_STOPCD 166
+#define KEY_RECORD 167
+#define KEY_REWIND 168
+#define KEY_PHONE 169 /* Media Select Telephone */
+#define KEY_ISO 170
+#define KEY_CONFIG 171 /* AL Consumer Control Configuration */
+#define KEY_HOMEPAGE 172 /* AC Home */
+#define KEY_REFRESH 173 /* AC Refresh */
+#define KEY_EXIT 174 /* AC Exit */
+#define KEY_MOVE 175
+#define KEY_EDIT 176
+#define KEY_SCROLLUP 177
+#define KEY_SCROLLDOWN 178
+#define KEY_KPLEFTPAREN 179
+#define KEY_KPRIGHTPAREN 180
+#define KEY_NEW 181 /* AC New */
+#define KEY_REDO 182 /* AC Redo/Repeat */
+
+#define KEY_F13 183
+#define KEY_F14 184
+#define KEY_F15 185
+#define KEY_F16 186
+#define KEY_F17 187
+#define KEY_F18 188
+#define KEY_F19 189
+#define KEY_F20 190
+#define KEY_F21 191
+#define KEY_F22 192
+#define KEY_F23 193
+#define KEY_F24 194
+
+#define KEY_PLAYCD 200
+#define KEY_PAUSECD 201
+#define KEY_PROG3 202
+#define KEY_PROG4 203
+#define KEY_SUSPEND 205
+#define KEY_CLOSE 206 /* AC Close */
+#define KEY_PLAY 207
+#define KEY_FASTFORWARD 208
+#define KEY_BASSBOOST 209
+#define KEY_PRINT 210 /* AC Print */
+#define KEY_HP 211
+#define KEY_CAMERA 212
+#define KEY_SOUND 213
+#define KEY_QUESTION 214
+#define KEY_EMAIL 215
+#define KEY_CHAT 216
+#define KEY_SEARCH 217
+#define KEY_CONNECT 218
+#define KEY_FINANCE 219 /* AL Checkbook/Finance */
+#define KEY_SPORT 220
+#define KEY_SHOP 221
+#define KEY_ALTERASE 222
+#define KEY_CANCEL 223 /* AC Cancel */
+#define KEY_BRIGHTNESSDOWN 224
+#define KEY_BRIGHTNESSUP 225
+#define KEY_MEDIA 226
+
+#define KEY_SWITCHVIDEOMODE 227 /* Cycle between available video
+ outputs (Monitor/LCD/TV-out/etc) */
+#define KEY_KBDILLUMTOGGLE 228
+#define KEY_KBDILLUMDOWN 229
+#define KEY_KBDILLUMUP 230
+
+#define KEY_SEND 231 /* AC Send */
+#define KEY_REPLY 232 /* AC Reply */
+#define KEY_FORWARDMAIL 233 /* AC Forward Msg */
+#define KEY_SAVE 234 /* AC Save */
+#define KEY_DOCUMENTS 235
+
+#define KEY_BATTERY 236
+
+#define KEY_BLUETOOTH 237
+#define KEY_WLAN 238
+#define KEY_UWB 239
+
+#define KEY_UNKNOWN 240
+
+#define KEY_VIDEO_NEXT 241 /* drive next video source */
+#define KEY_VIDEO_PREV 242 /* drive previous video source */
+#define KEY_BRIGHTNESS_CYCLE 243 /* brightness up, after max is min */
+#define KEY_BRIGHTNESS_ZERO 244 /* brightness off, use ambient */
+#define KEY_DISPLAY_OFF 245 /* display device to off state */
+
+#define KEY_WIMAX 246
+
+/* Range 248 - 255 is reserved for special needs of AT keyboard driver */
+
+#define BTN_MISC 0x100
+#define BTN_0 0x100
+#define BTN_1 0x101
+#define BTN_2 0x102
+#define BTN_3 0x103
+#define BTN_4 0x104
+#define BTN_5 0x105
+#define BTN_6 0x106
+#define BTN_7 0x107
+#define BTN_8 0x108
+#define BTN_9 0x109
+
+#define BTN_MOUSE 0x110
+#define BTN_LEFT 0x110
+#define BTN_RIGHT 0x111
+#define BTN_MIDDLE 0x112
+#define BTN_SIDE 0x113
+#define BTN_EXTRA 0x114
+#define BTN_FORWARD 0x115
+#define BTN_BACK 0x116
+#define BTN_TASK 0x117
+
+#define BTN_JOYSTICK 0x120
+#define BTN_TRIGGER 0x120
+#define BTN_THUMB 0x121
+#define BTN_THUMB2 0x122
+#define BTN_TOP 0x123
+#define BTN_TOP2 0x124
+#define BTN_PINKIE 0x125
+#define BTN_BASE 0x126
+#define BTN_BASE2 0x127
+#define BTN_BASE3 0x128
+#define BTN_BASE4 0x129
+#define BTN_BASE5 0x12a
+#define BTN_BASE6 0x12b
+#define BTN_DEAD 0x12f
+
+#define BTN_GAMEPAD 0x130
+#define BTN_A 0x130
+#define BTN_B 0x131
+#define BTN_C 0x132
+#define BTN_X 0x133
+#define BTN_Y 0x134
+#define BTN_Z 0x135
+#define BTN_TL 0x136
+#define BTN_TR 0x137
+#define BTN_TL2 0x138
+#define BTN_TR2 0x139
+#define BTN_SELECT 0x13a
+#define BTN_START 0x13b
+#define BTN_MODE 0x13c
+#define BTN_THUMBL 0x13d
+#define BTN_THUMBR 0x13e
+
+#define BTN_DIGI 0x140
+#define BTN_TOOL_PEN 0x140
+#define BTN_TOOL_RUBBER 0x141
+#define BTN_TOOL_BRUSH 0x142
+#define BTN_TOOL_PENCIL 0x143
+#define BTN_TOOL_AIRBRUSH 0x144
+#define BTN_TOOL_FINGER 0x145
+#define BTN_TOOL_MOUSE 0x146
+#define BTN_TOOL_LENS 0x147
+#define BTN_TOUCH 0x14a
+#define BTN_STYLUS 0x14b
+#define BTN_STYLUS2 0x14c
+#define BTN_TOOL_DOUBLETAP 0x14d
+#define BTN_TOOL_TRIPLETAP 0x14e
+
+#define BTN_WHEEL 0x150
+#define BTN_GEAR_DOWN 0x150
+#define BTN_GEAR_UP 0x151
+
+#define KEY_OK 0x160
+#define KEY_SELECT 0x161
+#define KEY_GOTO 0x162
+#define KEY_CLEAR 0x163
+#define KEY_POWER2 0x164
+#define KEY_OPTION 0x165
+#define KEY_INFO 0x166 /* AL OEM Features/Tips/Tutorial */
+#define KEY_TIME 0x167
+#define KEY_VENDOR 0x168
+#define KEY_ARCHIVE 0x169
+#define KEY_PROGRAM 0x16a /* Media Select Program Guide */
+#define KEY_CHANNEL 0x16b
+#define KEY_FAVORITES 0x16c
+#define KEY_EPG 0x16d
+#define KEY_PVR 0x16e /* Media Select Home */
+#define KEY_MHP 0x16f
+#define KEY_LANGUAGE 0x170
+#define KEY_TITLE 0x171
+#define KEY_SUBTITLE 0x172
+#define KEY_ANGLE 0x173
+#define KEY_ZOOM 0x174
+#define KEY_MODE 0x175
+#define KEY_KEYBOARD 0x176
+#define KEY_SCREEN 0x177
+#define KEY_PC 0x178 /* Media Select Computer */
+#define KEY_TV 0x179 /* Media Select TV */
+#define KEY_TV2 0x17a /* Media Select Cable */
+#define KEY_VCR 0x17b /* Media Select VCR */
+#define KEY_VCR2 0x17c /* VCR Plus */
+#define KEY_SAT 0x17d /* Media Select Satellite */
+#define KEY_SAT2 0x17e
+#define KEY_CD 0x17f /* Media Select CD */
+#define KEY_TAPE 0x180 /* Media Select Tape */
+#define KEY_RADIO 0x181
+#define KEY_TUNER 0x182 /* Media Select Tuner */
+#define KEY_PLAYER 0x183
+#define KEY_TEXT 0x184
+#define KEY_DVD 0x185 /* Media Select DVD */
+#define KEY_AUX 0x186
+#define KEY_MP3 0x187
+#define KEY_AUDIO 0x188
+#define KEY_VIDEO 0x189
+#define KEY_DIRECTORY 0x18a
+#define KEY_LIST 0x18b
+#define KEY_MEMO 0x18c /* Media Select Messages */
+#define KEY_CALENDAR 0x18d
+#define KEY_RED 0x18e
+#define KEY_GREEN 0x18f
+#define KEY_YELLOW 0x190
+#define KEY_BLUE 0x191
+#define KEY_CHANNELUP 0x192 /* Channel Increment */
+#define KEY_CHANNELDOWN 0x193 /* Channel Decrement */
+#define KEY_FIRST 0x194
+#define KEY_LAST 0x195 /* Recall Last */
+#define KEY_AB 0x196
+#define KEY_NEXT 0x197
+#define KEY_RESTART 0x198
+#define KEY_SLOW 0x199
+#define KEY_SHUFFLE 0x19a
+#define KEY_BREAK 0x19b
+#define KEY_PREVIOUS 0x19c
+#define KEY_DIGITS 0x19d
+#define KEY_TEEN 0x19e
+#define KEY_TWEN 0x19f
+#define KEY_VIDEOPHONE 0x1a0 /* Media Select Video Phone */
+#define KEY_GAMES 0x1a1 /* Media Select Games */
+#define KEY_ZOOMIN 0x1a2 /* AC Zoom In */
+#define KEY_ZOOMOUT 0x1a3 /* AC Zoom Out */
+#define KEY_ZOOMRESET 0x1a4 /* AC Zoom */
+#define KEY_WORDPROCESSOR 0x1a5 /* AL Word Processor */
+#define KEY_EDITOR 0x1a6 /* AL Text Editor */
+#define KEY_SPREADSHEET 0x1a7 /* AL Spreadsheet */
+#define KEY_GRAPHICSEDITOR 0x1a8 /* AL Graphics Editor */
+#define KEY_PRESENTATION 0x1a9 /* AL Presentation App */
+#define KEY_DATABASE 0x1aa /* AL Database App */
+#define KEY_NEWS 0x1ab /* AL Newsreader */
+#define KEY_VOICEMAIL 0x1ac /* AL Voicemail */
+#define KEY_ADDRESSBOOK 0x1ad /* AL Contacts/Address Book */
+#define KEY_MESSENGER 0x1ae /* AL Instant Messaging */
+#define KEY_DISPLAYTOGGLE 0x1af /* Turn display (LCD) on and off */
+#define KEY_SPELLCHECK 0x1b0 /* AL Spell Check */
+#define KEY_LOGOFF 0x1b1 /* AL Logoff */
+
+#define KEY_DOLLAR 0x1b2
+#define KEY_EURO 0x1b3
+
+#define KEY_FRAMEBACK 0x1b4 /* Consumer - transport controls */
+#define KEY_FRAMEFORWARD 0x1b5
+#define KEY_CONTEXT_MENU 0x1b6 /* GenDesc - system context menu */
+#define KEY_MEDIA_REPEAT 0x1b7 /* Consumer - transport control */
+
+#define KEY_DEL_EOL 0x1c0
+#define KEY_DEL_EOS 0x1c1
+#define KEY_INS_LINE 0x1c2
+#define KEY_DEL_LINE 0x1c3
+
+#define KEY_FN 0x1d0
+#define KEY_FN_ESC 0x1d1
+#define KEY_FN_F1 0x1d2
+#define KEY_FN_F2 0x1d3
+#define KEY_FN_F3 0x1d4
+#define KEY_FN_F4 0x1d5
+#define KEY_FN_F5 0x1d6
+#define KEY_FN_F6 0x1d7
+#define KEY_FN_F7 0x1d8
+#define KEY_FN_F8 0x1d9
+#define KEY_FN_F9 0x1da
+#define KEY_FN_F10 0x1db
+#define KEY_FN_F11 0x1dc
+#define KEY_FN_F12 0x1dd
+#define KEY_FN_1 0x1de
+#define KEY_FN_2 0x1df
+#define KEY_FN_D 0x1e0
+#define KEY_FN_E 0x1e1
+#define KEY_FN_F 0x1e2
+#define KEY_FN_S 0x1e3
+#define KEY_FN_B 0x1e4
+
+#define KEY_BRL_DOT1 0x1f1
+#define KEY_BRL_DOT2 0x1f2
+#define KEY_BRL_DOT3 0x1f3
+#define KEY_BRL_DOT4 0x1f4
+#define KEY_BRL_DOT5 0x1f5
+#define KEY_BRL_DOT6 0x1f6
+#define KEY_BRL_DOT7 0x1f7
+#define KEY_BRL_DOT8 0x1f8
+#define KEY_BRL_DOT9 0x1f9
+#define KEY_BRL_DOT10 0x1fa
+
+/* We avoid low common keys in module aliases so they don't get huge. */
+#define KEY_MIN_INTERESTING KEY_MUTE
+#define KEY_MAX 0x1ff
+#define KEY_CNT (KEY_MAX+1)
+
+/*
+ * Relative axes
+ */
+
+#define REL_X 0x00
+#define REL_Y 0x01
+#define REL_Z 0x02
+#define REL_RX 0x03
+#define REL_RY 0x04
+#define REL_RZ 0x05
+#define REL_HWHEEL 0x06
+#define REL_DIAL 0x07
+#define REL_WHEEL 0x08
+#define REL_MISC 0x09
+#define REL_MAX 0x0f
+#define REL_CNT (REL_MAX+1)
+
+/*
+ * Absolute axes
+ */
+
+#define ABS_X 0x00
+#define ABS_Y 0x01
+#define ABS_Z 0x02
+#define ABS_RX 0x03
+#define ABS_RY 0x04
+#define ABS_RZ 0x05
+#define ABS_THROTTLE 0x06
+#define ABS_RUDDER 0x07
+#define ABS_WHEEL 0x08
+#define ABS_GAS 0x09
+#define ABS_BRAKE 0x0a
+#define ABS_HAT0X 0x10
+#define ABS_HAT0Y 0x11
+#define ABS_HAT1X 0x12
+#define ABS_HAT1Y 0x13
+#define ABS_HAT2X 0x14
+#define ABS_HAT2Y 0x15
+#define ABS_HAT3X 0x16
+#define ABS_HAT3Y 0x17
+#define ABS_PRESSURE 0x18
+#define ABS_DISTANCE 0x19
+#define ABS_TILT_X 0x1a
+#define ABS_TILT_Y 0x1b
+#define ABS_TOOL_WIDTH 0x1c
+#define ABS_VOLUME 0x20
+#define ABS_MISC 0x28
+#define ABS_MAX 0x3f
+#define ABS_CNT (ABS_MAX+1)
+
+/*
+ * Switch events
+ */
+
+#define SW_LID 0x00 /* set = lid shut */
+#define SW_TABLET_MODE 0x01 /* set = tablet mode */
+#define SW_HEADPHONE_INSERT 0x02 /* set = inserted */
+#define SW_RFKILL_ALL 0x03 /* rfkill master switch, type "any"
+ set = radio enabled */
+#define SW_RADIO SW_RFKILL_ALL /* deprecated */
+#define SW_MICROPHONE_INSERT 0x04 /* set = inserted */
+#define SW_DOCK 0x05 /* set = plugged into dock */
+#define SW_MAX 0x0f
+#define SW_CNT (SW_MAX+1)
+
+/*
+ * Misc events
+ */
+
+#define MSC_SERIAL 0x00
+#define MSC_PULSELED 0x01
+#define MSC_GESTURE 0x02
+#define MSC_RAW 0x03
+#define MSC_SCAN 0x04
+#define MSC_MAX 0x07
+#define MSC_CNT (MSC_MAX+1)
+
+/*
+ * LEDs
+ */
+
+#define LED_NUML 0x00
+#define LED_CAPSL 0x01
+#define LED_SCROLLL 0x02
+#define LED_COMPOSE 0x03
+#define LED_KANA 0x04
+#define LED_SLEEP 0x05
+#define LED_SUSPEND 0x06
+#define LED_MUTE 0x07
+#define LED_MISC 0x08
+#define LED_MAIL 0x09
+#define LED_CHARGING 0x0a
+#define LED_MAX 0x0f
+#define LED_CNT (LED_MAX+1)
+
+/*
+ * Autorepeat values
+ */
+
+#define REP_DELAY 0x00
+#define REP_PERIOD 0x01
+#define REP_MAX 0x01
+
+/*
+ * Sounds
+ */
+
+#define SND_CLICK 0x00
+#define SND_BELL 0x01
+#define SND_TONE 0x02
+#define SND_MAX 0x07
+#define SND_CNT (SND_MAX+1)
+
+/*
+ * IDs.
+ */
+
+#define ID_BUS 0
+#define ID_VENDOR 1
+#define ID_PRODUCT 2
+#define ID_VERSION 3
+
+#define BUS_PCI 0x01
+#define BUS_ISAPNP 0x02
+#define BUS_USB 0x03
+#define BUS_HIL 0x04
+#define BUS_BLUETOOTH 0x05
+#define BUS_VIRTUAL 0x06
+
+#define BUS_ISA 0x10
+#define BUS_I8042 0x11
+#define BUS_XTKBD 0x12
+#define BUS_RS232 0x13
+#define BUS_GAMEPORT 0x14
+#define BUS_PARPORT 0x15
+#define BUS_AMIGA 0x16
+#define BUS_ADB 0x17
+#define BUS_I2C 0x18
+#define BUS_HOST 0x19
+#define BUS_GSC 0x1A
+#define BUS_ATARI 0x1B
+
+/* User input interface */
+
+#define UINPUT_IOCTL_BASE 'U'
+
+#define UI_DEV_CREATE _IO(UINPUT_IOCTL_BASE, 1)
+#define UI_DEV_DESTROY _IO(UINPUT_IOCTL_BASE, 2)
+
+#define UI_SET_EVBIT _IOW(UINPUT_IOCTL_BASE, 100, int)
+#define UI_SET_KEYBIT _IOW(UINPUT_IOCTL_BASE, 101, int)
+#define UI_SET_RELBIT _IOW(UINPUT_IOCTL_BASE, 102, int)
+#define UI_SET_ABSBIT _IOW(UINPUT_IOCTL_BASE, 103, int)
+#define UI_SET_MSCBIT _IOW(UINPUT_IOCTL_BASE, 104, int)
+#define UI_SET_LEDBIT _IOW(UINPUT_IOCTL_BASE, 105, int)
+#define UI_SET_SNDBIT _IOW(UINPUT_IOCTL_BASE, 106, int)
+#define UI_SET_FFBIT _IOW(UINPUT_IOCTL_BASE, 107, int)
+#define UI_SET_PHYS _IOW(UINPUT_IOCTL_BASE, 108, char*)
+#define UI_SET_SWBIT _IOW(UINPUT_IOCTL_BASE, 109, int)
+
+#ifndef NBITS
+#define NBITS(x) ((((x) - 1) / (sizeof(long) * 8)) + 1)
+#endif
+
+#define UINPUT_MAX_NAME_SIZE 80
+
+struct uinput_id {
+ uint16_t bustype;
+ uint16_t vendor;
+ uint16_t product;
+ uint16_t version;
+};
+
+struct uinput_dev {
+ char name[UINPUT_MAX_NAME_SIZE];
+ struct uinput_id id;
+ int ff_effects_max;
+ int absmax[ABS_MAX + 1];
+ int absmin[ABS_MAX + 1];
+ int absfuzz[ABS_MAX + 1];
+ int absflat[ABS_MAX + 1];
+};
+
+struct uinput_event {
+ struct timeval time;
+ uint16_t type;
+ uint16_t code;
+ int32_t value;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __UINPUT_H */
diff --git a/test/agent.c b/test/agent.c
new file mode 100644
index 0000000..a40039e
--- /dev/null
+++ b/test/agent.c
@@ -0,0 +1,700 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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>
+
+static char *passkey_value = NULL;
+static int passkey_delay = 0;
+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, "Agent has been terminated\n");
+ __io_terminated = 1;
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusHandlerResult request_pincode_message(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessage *reply;
+ const char *path;
+
+ if (!passkey_value)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID)) {
+ fprintf(stderr, "Invalid arguments for RequestPinCode 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("Pincode request for device %s\n", path);
+
+ if (passkey_delay) {
+ printf("Waiting for %d seconds\n", passkey_delay);
+ sleep(passkey_delay);
+ }
+
+ dbus_message_append_args(reply, DBUS_TYPE_STRING, &passkey_value,
+ 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 request_passkey_message(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessage *reply;
+ const char *path;
+ unsigned int passkey;
+
+ if (!passkey_value)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID)) {
+ fprintf(stderr, "Invalid arguments for RequestPasskey 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", path);
+
+ if (passkey_delay) {
+ printf("Waiting for %d seconds\n", passkey_delay);
+ sleep(passkey_delay);
+ }
+
+ passkey = strtoul(passkey_value, NULL, 10);
+
+ dbus_message_append_args(reply, DBUS_TYPE_UINT32, &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 request_confirmation_message(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessage *reply;
+ const char *path;
+ unsigned int passkey;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_UINT32, &passkey,
+ DBUS_TYPE_INVALID)) {
+ fprintf(stderr, "Invalid arguments for RequestPasskey 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("Confirmation request of %u for device %s\n", passkey, path);
+
+ if (passkey_delay) {
+ printf("Waiting for %d seconds\n", passkey_delay);
+ sleep(passkey_delay);
+ }
+
+send:
+ dbus_connection_send(conn, reply, NULL);
+
+ dbus_connection_flush(conn);
+
+ dbus_message_unref(reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult authorize_message(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessage *reply;
+ const char *path, *uuid;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_STRING, &uuid,
+ DBUS_TYPE_INVALID)) {
+ fprintf(stderr, "Invalid arguments for Authorize 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("Authorizing request for %s\n", path);
+
+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;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INVALID)) {
+ fprintf(stderr, "Invalid arguments for passkey Confirm method");
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ printf("Request canceled\n");
+
+ 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, "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 agent_message(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ if (dbus_message_is_method_call(msg, "org.bluez.Agent",
+ "RequestPinCode"))
+ return request_pincode_message(conn, msg, data);
+
+ if (dbus_message_is_method_call(msg, "org.bluez.Agent",
+ "RequestPasskey"))
+ return request_passkey_message(conn, msg, data);
+
+ if (dbus_message_is_method_call(msg, "org.bluez.Agent",
+ "RequestConfirmation"))
+ return request_confirmation_message(conn, msg, data);
+
+ if (dbus_message_is_method_call(msg, "org.bluez.Agent", "Authorize"))
+ return authorize_message(conn, msg, data);
+
+ if (dbus_message_is_method_call(msg, "org.bluez.Agent", "Cancel"))
+ return cancel_message(conn, msg, data);
+
+ if (dbus_message_is_method_call(msg, "org.bluez.Agent", "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 *adapter_path,
+ const char *agent_path,
+ const char *capabilities)
+{
+ DBusMessage *msg, *reply;
+ DBusError err;
+
+ msg = dbus_message_new_method_call("org.bluez", adapter_path,
+ "org.bluez.Adapter", "RegisterAgent");
+ if (!msg) {
+ fprintf(stderr, "Can't allocate new method call\n");
+ return -1;
+ }
+
+ dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &agent_path,
+ DBUS_TYPE_STRING, &capabilities,
+ 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 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 *adapter_path,
+ const char *agent_path)
+{
+ DBusMessage *msg, *reply;
+ DBusError err;
+
+ msg = dbus_message_new_method_call("org.bluez", adapter_path,
+ "org.bluez.Adapter", "UnregisterAgent");
+ if (!msg) {
+ fprintf(stderr, "Can't allocate new method call\n");
+ return -1;
+ }
+
+ dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &agent_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 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 int create_paired_device(DBusConnection *conn, const char *adapter_path,
+ const char *agent_path,
+ const char *capabilities,
+ const char *device)
+{
+ dbus_bool_t success;
+ DBusMessage *msg;
+
+ msg = dbus_message_new_method_call("org.bluez", adapter_path,
+ "org.bluez.Adapter",
+ "CreatePairedDevice");
+ if (!msg) {
+ fprintf(stderr, "Can't allocate new method call\n");
+ return -1;
+ }
+
+ dbus_message_append_args(msg, DBUS_TYPE_STRING, &device,
+ DBUS_TYPE_OBJECT_PATH, &agent_path,
+ DBUS_TYPE_STRING, &capabilities,
+ DBUS_TYPE_INVALID);
+
+ success = dbus_connection_send(conn, msg, NULL);
+
+ dbus_message_unref(msg);
+
+ if (!success) {
+ fprintf(stderr, "Not enough memory for message send\n");
+ return -1;
+ }
+
+ dbus_connection_flush(conn);
+
+ return 0;
+}
+
+static char *get_default_adapter_path(DBusConnection *conn)
+{
+ DBusMessage *msg, *reply;
+ DBusError err;
+ const char *reply_path;
+ char *path;
+
+ msg = dbus_message_new_method_call("org.bluez", "/",
+ "org.bluez.Manager", "DefaultAdapter");
+
+ if (!msg) {
+ fprintf(stderr, "Can't allocate new method call\n");
+ return NULL;
+ }
+
+ 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 get default adapter\n");
+ if (dbus_error_is_set(&err)) {
+ fprintf(stderr, "%s\n", err.message);
+ dbus_error_free(&err);
+ }
+ return NULL;
+ }
+
+ if (!dbus_message_get_args(reply, &err,
+ DBUS_TYPE_OBJECT_PATH, &reply_path,
+ DBUS_TYPE_INVALID)) {
+ fprintf(stderr,
+ "Can't get reply arguments\n");
+ if (dbus_error_is_set(&err)) {
+ fprintf(stderr, "%s\n", err.message);
+ dbus_error_free(&err);
+ }
+ return NULL;
+ }
+
+ path = strdup(reply_path);
+
+ dbus_message_unref(reply);
+
+ dbus_connection_flush(conn);
+
+ return path;
+}
+
+static char *get_adapter_path(DBusConnection *conn, const char *adapter)
+{
+ DBusMessage *msg, *reply;
+ DBusError err;
+ const char *reply_path;
+ char *path;
+
+ if (!adapter)
+ return get_default_adapter_path(conn);
+
+ msg = dbus_message_new_method_call("org.bluez", "/",
+ "org.bluez.Manager", "FindAdapter");
+
+ if (!msg) {
+ fprintf(stderr, "Can't allocate new method call\n");
+ return NULL;
+ }
+
+ dbus_message_append_args(msg, DBUS_TYPE_STRING, &adapter,
+ 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 find adapter %s\n", adapter);
+ if (dbus_error_is_set(&err)) {
+ fprintf(stderr, "%s\n", err.message);
+ dbus_error_free(&err);
+ }
+ return NULL;
+ }
+
+ if (!dbus_message_get_args(reply, &err,
+ DBUS_TYPE_OBJECT_PATH, &reply_path,
+ DBUS_TYPE_INVALID)) {
+ fprintf(stderr,
+ "Can't get reply arguments\n");
+ if (dbus_error_is_set(&err)) {
+ fprintf(stderr, "%s\n", err.message);
+ dbus_error_free(&err);
+ }
+ return NULL;
+ }
+
+ path = strdup(reply_path);
+
+ dbus_message_unref(reply);
+
+ dbus_connection_flush(conn);
+
+ return path;
+}
+
+static void usage(void)
+{
+ printf("Bluetooth agent ver %s\n\n", VERSION);
+
+ printf("Usage:\n"
+ "\tagent [--adapter adapter-path] [--path agent-path] <passkey> [<device>]\n"
+ "\n");
+}
+
+static struct option main_options[] = {
+ { "adapter", 1, 0, 'a' },
+ { "path", 1, 0, 'p' },
+ { "capabilites",1, 0, 'c' },
+ { "delay", 1, 0, 'd' },
+ { "reject", 0, 0, 'r' },
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ const char *capabilities = "DisplayYesNo";
+ struct sigaction sa;
+ DBusConnection *conn;
+ char match_string[128], default_path[128], *adapter_id = NULL;
+ char *adapter_path = NULL, *agent_path = NULL, *device = NULL;
+ int opt;
+
+ snprintf(default_path, sizeof(default_path),
+ "/org/bluez/agent_%d", getpid());
+
+ while ((opt = getopt_long(argc, argv, "+a:p:c:d:rh", main_options, NULL)) != EOF) {
+ switch(opt) {
+ case 'a':
+ adapter_id = optarg;
+ break;
+ case 'p':
+ if (optarg[0] != '/') {
+ fprintf(stderr, "Invalid path\n");
+ exit(1);
+ }
+ agent_path = strdup(optarg);
+ break;
+ case 'c':
+ capabilities = optarg;
+ break;
+ case 'd':
+ passkey_delay = atoi(optarg);
+ break;
+ case 'r':
+ do_reject = 1;
+ break;
+ case 'h':
+ usage();
+ exit(0);
+ default:
+ exit(1);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ if (argc < 1) {
+ usage();
+ exit(1);
+ }
+
+ passkey_value = strdup(argv[0]);
+
+ if (argc > 1)
+ device = strdup(argv[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);
+ }
+
+ adapter_path = get_adapter_path(conn, adapter_id);
+ if (!adapter_path)
+ exit(1);
+
+ if (!dbus_connection_register_object_path(conn, agent_path,
+ &agent_table, NULL)) {
+ fprintf(stderr, "Can't register object path for agent\n");
+ exit(1);
+ }
+
+ if (device) {
+ if (create_paired_device(conn, adapter_path, agent_path,
+ capabilities, device) < 0) {
+ dbus_connection_unref(conn);
+ exit(1);
+ }
+ } else {
+ if (register_agent(conn, adapter_path, agent_path,
+ capabilities) < 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 && !device)
+ unregister_agent(conn, adapter_path, agent_path);
+
+ free(adapter_path);
+ free(agent_path);
+
+ free(passkey_value);
+
+ dbus_connection_unref(conn);
+
+ return 0;
+}
diff --git a/test/apitest b/test/apitest
new file mode 100755
index 0000000..b1c3f10
--- /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 0000000..12ba682
--- /dev/null
+++ b/test/attest.c
@@ -0,0 +1,183 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2001-2010 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/avtest.c b/test/avtest.c
new file mode 100644
index 0000000..168326f
--- /dev/null
+++ b/test/avtest.c
@@ -0,0 +1,869 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2007-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2009-2010 Nokia Corporation
+ *
+ *
+ * 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 <string.h>
+#include <getopt.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/sdp.h>
+
+#define AVDTP_PKT_TYPE_SINGLE 0x00
+#define AVDTP_PKT_TYPE_START 0x01
+#define AVDTP_PKT_TYPE_CONTINUE 0x02
+#define AVDTP_PKT_TYPE_END 0x03
+
+#define AVDTP_MSG_TYPE_COMMAND 0x00
+#define AVDTP_MSG_TYPE_GEN_REJECT 0x01
+#define AVDTP_MSG_TYPE_ACCEPT 0x02
+#define AVDTP_MSG_TYPE_REJECT 0x03
+
+#define AVDTP_DISCOVER 0x01
+#define AVDTP_GET_CAPABILITIES 0x02
+#define AVDTP_SET_CONFIGURATION 0x03
+#define AVDTP_GET_CONFIGURATION 0x04
+#define AVDTP_RECONFIGURE 0x05
+#define AVDTP_OPEN 0x06
+#define AVDTP_START 0x07
+#define AVDTP_CLOSE 0x08
+#define AVDTP_SUSPEND 0x09
+#define AVDTP_ABORT 0x0A
+
+#define AVDTP_SEP_TYPE_SOURCE 0x00
+#define AVDTP_SEP_TYPE_SINK 0x01
+
+#define AVDTP_MEDIA_TYPE_AUDIO 0x00
+#define AVDTP_MEDIA_TYPE_VIDEO 0x01
+#define AVDTP_MEDIA_TYPE_MULTIMEDIA 0x02
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avdtp_header {
+ uint8_t message_type:2;
+ uint8_t packet_type:2;
+ uint8_t transaction:4;
+ uint8_t signal_id:6;
+ uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct seid_info {
+ uint8_t rfa0:1;
+ uint8_t inuse:1;
+ uint8_t seid:6;
+ uint8_t rfa2:3;
+ uint8_t type:1;
+ uint8_t media_type:4;
+} __attribute__ ((packed));
+
+struct avdtp_start_header {
+ uint8_t message_type:2;
+ uint8_t packet_type:2;
+ uint8_t transaction:4;
+ uint8_t no_of_packets;
+ uint8_t signal_id:6;
+ uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct avdtp_continue_header {
+ uint8_t message_type:2;
+ uint8_t packet_type:2;
+ uint8_t transaction:4;
+} __attribute__ ((packed));
+
+struct avctp_header {
+ uint8_t ipid:1;
+ uint8_t cr:1;
+ uint8_t packet_type:2;
+ uint8_t transaction:4;
+ uint16_t pid;
+} __attribute__ ((packed));
+#define AVCTP_HEADER_LENGTH 3
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct avdtp_header {
+ uint8_t transaction:4;
+ uint8_t packet_type:2;
+ uint8_t message_type:2;
+ uint8_t rfa0:2;
+ uint8_t signal_id:6;
+} __attribute__ ((packed));
+
+struct seid_info {
+ uint8_t seid:6;
+ uint8_t inuse:1;
+ uint8_t rfa0:1;
+ uint8_t media_type:4;
+ uint8_t type:1;
+ uint8_t rfa2:3;
+} __attribute__ ((packed));
+
+struct avdtp_start_header {
+ uint8_t transaction:4;
+ uint8_t packet_type:2;
+ uint8_t message_type:2;
+ uint8_t no_of_packets;
+ uint8_t rfa0:2;
+ uint8_t signal_id:6;
+} __attribute__ ((packed));
+
+struct avdtp_continue_header {
+ uint8_t transaction:4;
+ uint8_t packet_type:2;
+ uint8_t message_type:2;
+} __attribute__ ((packed));
+
+struct avctp_header {
+ uint8_t transaction:4;
+ uint8_t packet_type:2;
+ uint8_t cr:1;
+ uint8_t ipid:1;
+ uint16_t pid;
+} __attribute__ ((packed));
+#define AVCTP_HEADER_LENGTH 3
+
+#else
+#error "Unknown byte order"
+#endif
+
+#define AVCTP_COMMAND 0
+#define AVCTP_RESPONSE 1
+
+#define AVCTP_PACKET_SINGLE 0
+
+static const unsigned char media_transport[] = {
+ 0x01, /* Media transport category */
+ 0x00,
+ 0x07, /* Media codec category */
+ 0x06,
+ 0x00, /* Media type audio */
+ 0x00, /* Codec SBC */
+ 0x22, /* 44.1 kHz, stereo */
+ 0x15, /* 16 blocks, 8 subbands */
+ 0x02,
+ 0x33,
+};
+
+static int media_sock = -1;
+
+static void dump_avctp_header(struct avctp_header *hdr)
+{
+ printf("TL %d PT %d CR %d IPID %d PID 0x%04x\n", hdr->transaction,
+ hdr->packet_type, hdr->cr, hdr->ipid, ntohs(hdr->pid));
+}
+
+static void dump_avdtp_header(struct avdtp_header *hdr)
+{
+ printf("TL %d PT %d MT %d SI %d\n", hdr->transaction,
+ hdr->packet_type, hdr->message_type, hdr->signal_id);
+}
+
+static void dump_buffer(const unsigned char *buf, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ printf("%02x ", buf[i]);
+ printf("\n");
+}
+
+static void process_avdtp(int srv_sk, int sk, unsigned char reject,
+ int fragment)
+{
+ unsigned char buf[672];
+ ssize_t len;
+
+ while (1) {
+ struct avdtp_header *hdr = (void *) buf;
+
+ len = read(sk, buf, sizeof(buf));
+ if (len <= 0) {
+ perror("Read failed");
+ break;
+ }
+
+ dump_buffer(buf, len);
+ dump_avdtp_header(hdr);
+
+ if (hdr->packet_type != AVDTP_PKT_TYPE_SINGLE) {
+ fprintf(stderr, "Only single packets are supported\n");
+ break;
+ }
+
+ if (hdr->message_type != AVDTP_MSG_TYPE_COMMAND) {
+ fprintf(stderr, "Ignoring non-command messages\n");
+ continue;
+ }
+
+ switch (hdr->signal_id) {
+ case AVDTP_DISCOVER:
+ if (reject == AVDTP_DISCOVER) {
+ hdr->message_type = AVDTP_MSG_TYPE_REJECT;
+ buf[2] = 0x29; /* Unsupported configuration */
+ printf("Rejecting discover command\n");
+ len = write(sk, buf, 3);
+ } else {
+ struct seid_info *sei = (void *) (buf + 2);
+ hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+ buf[2] = 0x00;
+ buf[3] = 0x00;
+ sei->seid = 0x01;
+ sei->type = AVDTP_SEP_TYPE_SINK;
+ sei->media_type = AVDTP_MEDIA_TYPE_AUDIO;
+ printf("Accepting discover command\n");
+ len = write(sk, buf, 4);
+ }
+ break;
+
+ case AVDTP_GET_CAPABILITIES:
+ if (reject == AVDTP_GET_CAPABILITIES) {
+ hdr->message_type = AVDTP_MSG_TYPE_REJECT;
+ buf[2] = 0x29; /* Unsupported configuration */
+ printf("Rejecting get capabilties command\n");
+ len = write(sk, buf, 3);
+ } else if (fragment) {
+ struct avdtp_start_header *start = (void *) buf;
+
+ printf("Sending fragmented reply to getcap\n");
+
+ hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+
+ /* Start packet */
+ hdr->packet_type = AVDTP_PKT_TYPE_START;
+ start->signal_id = AVDTP_GET_CAPABILITIES;
+ start->no_of_packets = 3;
+ memcpy(&buf[3], media_transport,
+ sizeof(media_transport));
+ len = write(sk, buf,
+ 3 + sizeof(media_transport));
+
+ /* Continue packet */
+ hdr->packet_type = AVDTP_PKT_TYPE_CONTINUE;
+ memcpy(&buf[1], media_transport,
+ sizeof(media_transport));
+ len = write(sk, buf,
+ 1 + sizeof(media_transport));
+
+ /* End packet */
+ hdr->packet_type = AVDTP_PKT_TYPE_END;
+ memcpy(&buf[1], media_transport,
+ sizeof(media_transport));
+ len = write(sk, buf,
+ 1 + sizeof(media_transport));
+ } else {
+ hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+ memcpy(&buf[2], media_transport,
+ sizeof(media_transport));
+ printf("Accepting get capabilities command\n");
+ len = write(sk, buf,
+ 2 + sizeof(media_transport));
+ }
+ break;
+
+ case AVDTP_SET_CONFIGURATION:
+ if (reject == AVDTP_SET_CONFIGURATION) {
+ hdr->message_type = AVDTP_MSG_TYPE_REJECT;
+ buf[2] = buf[4];
+ buf[3] = 0x13; /* SEP In Use */
+ printf("Rejecting set configuration command\n");
+ len = write(sk, buf, 4);
+ } else {
+ hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+ printf("Accepting set configuration command\n");
+ len = write(sk, buf, 2);
+ }
+ break;
+
+ case AVDTP_GET_CONFIGURATION:
+ if (reject == AVDTP_GET_CONFIGURATION) {
+ hdr->message_type = AVDTP_MSG_TYPE_REJECT;
+ buf[2] = 0x12; /* Bad ACP SEID */
+ printf("Rejecting get configuration command\n");
+ len = write(sk, buf, 3);
+ } else {
+ hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+ printf("Accepting get configuration command\n");
+ len = write(sk, buf, 2);
+ }
+ break;
+
+ case AVDTP_OPEN:
+ if (reject == AVDTP_OPEN) {
+ hdr->message_type = AVDTP_MSG_TYPE_REJECT;
+ buf[2] = 0x31; /* Bad State */
+ printf("Rejecting open command\n");
+ len = write(sk, buf, 3);
+ } else {
+ struct sockaddr_l2 addr;
+ socklen_t optlen;
+
+ hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+ printf("Accepting open command\n");
+ len = write(sk, buf, 2);
+
+ memset(&addr, 0, sizeof(addr));
+ optlen = sizeof(addr);
+
+ media_sock = accept(srv_sk,
+ (struct sockaddr *) &addr,
+ &optlen);
+ if (media_sock < 0) {
+ perror("Accept failed");
+ break;
+ }
+ }
+ break;
+
+ case AVDTP_START:
+ if (reject == AVDTP_ABORT)
+ printf("Ignoring start to cause abort");
+ else if (reject == AVDTP_START) {
+ hdr->message_type = AVDTP_MSG_TYPE_REJECT;
+ buf[3] = 0x31; /* Bad State */
+ printf("Rejecting start command\n");
+ len = write(sk, buf, 4);
+ } else {
+ hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+ printf("Accepting start command\n");
+ len = write(sk, buf, 2);
+ }
+ break;
+
+ case AVDTP_CLOSE:
+ if (reject == AVDTP_CLOSE) {
+ hdr->message_type = AVDTP_MSG_TYPE_REJECT;
+ buf[2] = 0x31; /* Bad State */
+ printf("Rejecting close command\n");
+ len = write(sk, buf, 3);
+ } else {
+ hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+ printf("Accepting close command\n");
+ len = write(sk, buf, 2);
+ if (media_sock >= 0) {
+ close(media_sock);
+ media_sock = -1;
+ }
+ }
+ break;
+
+ case AVDTP_SUSPEND:
+ if (reject == AVDTP_SUSPEND) {
+ hdr->message_type = AVDTP_MSG_TYPE_REJECT;
+ buf[3] = 0x31; /* Bad State */
+ printf("Rejecting suspend command\n");
+ len = write(sk, buf, 4);
+ } else {
+ hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+ printf("Accepting suspend command\n");
+ len = write(sk, buf, 2);
+ }
+ break;
+
+ case AVDTP_ABORT:
+ hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+ printf("Accepting abort command\n");
+ len = write(sk, buf, 2);
+ if (media_sock >= 0) {
+ close(media_sock);
+ media_sock = -1;
+ }
+ break;
+
+ default:
+ buf[1] = 0x00;
+ printf("Unknown command\n");
+ len = write(sk, buf, 2);
+ break;
+ }
+ }
+}
+
+static void process_avctp(int sk, int reject)
+{
+ unsigned char buf[672];
+ ssize_t len;
+
+ while (1) {
+ struct avctp_header *hdr = (void *) buf;
+
+ len = read(sk, buf, sizeof(buf));
+ if (len <= 0) {
+ perror("Read failed");
+ break;
+ }
+
+ dump_buffer(buf, len);
+
+ if (len >= AVCTP_HEADER_LENGTH)
+ dump_avctp_header(hdr);
+ }
+}
+
+static int set_minimum_mtu(int sk)
+{
+ struct l2cap_options l2o;
+ socklen_t optlen;
+
+ memset(&l2o, 0, sizeof(l2o));
+ optlen = sizeof(l2o);
+
+ if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &optlen) < 0) {
+ perror("getsockopt");
+ return -1;
+ }
+
+ l2o.imtu = 48;
+ l2o.omtu = 48;
+
+ if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, sizeof(l2o)) < 0) {
+ perror("setsockopt");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void do_listen(const bdaddr_t *src, unsigned char reject, int fragment)
+{
+ struct sockaddr_l2 addr;
+ socklen_t optlen;
+ int sk, nsk;
+
+ sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, 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, src);
+ addr.l2_psm = htobs(25);
+
+ if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("Can't bind socket");
+ goto error;
+ }
+
+ if (fragment)
+ set_minimum_mtu(sk);
+
+ if (listen(sk, 10)) {
+ perror("Can't listen on the socket");
+ goto error;
+ }
+
+ while (1) {
+ memset(&addr, 0, sizeof(addr));
+ optlen = sizeof(addr);
+
+ nsk = accept(sk, (struct sockaddr *) &addr, &optlen);
+ if (nsk < 0) {
+ perror("Accept failed");
+ continue;
+ }
+
+ process_avdtp(sk, nsk, reject, fragment);
+
+ if (media_sock >= 0) {
+ close(media_sock);
+ media_sock = -1;
+ }
+
+ close(nsk);
+ }
+
+error:
+ close(sk);
+}
+
+static int do_connect(const bdaddr_t *src, const bdaddr_t *dst, int avctp,
+ int fragment)
+{
+ struct sockaddr_l2 addr;
+ int sk, err;
+
+ sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+ if (sk < 0) {
+ perror("Can't create socket");
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ bacpy(&addr.l2_bdaddr, src);
+
+ if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("Can't bind socket");
+ goto error;
+ }
+
+ if (fragment)
+ set_minimum_mtu(sk);
+
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ bacpy(&addr.l2_bdaddr, dst);
+ addr.l2_psm = htobs(avctp ? 23 : 25);
+
+ err = connect(sk, (struct sockaddr *) &addr, sizeof(addr));
+ if (err < 0) {
+ perror("Unable to connect");
+ goto error;
+ }
+
+ return sk;
+
+error:
+ close(sk);
+ return -1;
+}
+
+static void do_avdtp_send(int sk, const bdaddr_t *src, const bdaddr_t *dst,
+ unsigned char cmd, int invalid, int preconf)
+{
+ unsigned char buf[672];
+ struct avdtp_header *hdr = (void *) buf;
+ ssize_t len;
+
+ memset(buf, 0, sizeof(buf));
+
+ switch (cmd) {
+ case AVDTP_DISCOVER:
+ if (invalid)
+ hdr->message_type = 0x01;
+ else
+ hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+ hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+ hdr->signal_id = AVDTP_DISCOVER;
+ len = write(sk, buf, 2);
+ break;
+
+ case AVDTP_GET_CAPABILITIES:
+ hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+ hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+ hdr->signal_id = AVDTP_GET_CAPABILITIES;
+ buf[2] = 1 << 2; /* SEID 1 */
+ len = write(sk, buf, invalid ? 2 : 3);
+ break;
+
+ case AVDTP_SET_CONFIGURATION:
+ if (preconf)
+ do_avdtp_send(sk, src, dst, cmd, 0, 0);
+ hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+ hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+ hdr->signal_id = AVDTP_SET_CONFIGURATION;
+ buf[2] = 1 << 2; /* ACP SEID */
+ buf[3] = 1 << 2; /* INT SEID */
+ memcpy(&buf[4], media_transport, sizeof(media_transport));
+ if (invalid)
+ buf[5] = 0x01; /* LOSC != 0 */
+ len = write(sk, buf, 4 + sizeof(media_transport));
+ break;
+
+ case AVDTP_GET_CONFIGURATION:
+ if (preconf)
+ do_avdtp_send(sk, src, dst, AVDTP_SET_CONFIGURATION, 0, 0);
+ hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+ hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+ hdr->signal_id = AVDTP_GET_CONFIGURATION;
+ if (invalid)
+ buf[2] = 13 << 2; /* Invalid ACP SEID */
+ else
+ buf[2] = 1 << 2; /* Valid ACP SEID */
+ len = write(sk, buf, 3);
+ break;
+
+ case AVDTP_OPEN:
+ if (preconf)
+ do_avdtp_send(sk, src, dst, AVDTP_SET_CONFIGURATION, 0, 0);
+ hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+ hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+ hdr->signal_id = AVDTP_OPEN;
+ buf[2] = 1 << 2; /* ACP SEID */
+ len = write(sk, buf, 3);
+ break;
+
+ case AVDTP_START:
+ if (preconf)
+ do_avdtp_send(sk, src, dst, AVDTP_SET_CONFIGURATION, 0, 0);
+ if (!invalid)
+ do_avdtp_send(sk, src, dst, AVDTP_OPEN, 0, 0);
+ hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+ hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+ hdr->signal_id = AVDTP_START;
+ buf[2] = 1 << 2; /* ACP SEID */
+ len = write(sk, buf, 3);
+ break;
+
+ case AVDTP_CLOSE:
+ if (preconf) {
+ do_avdtp_send(sk, src, dst, AVDTP_SET_CONFIGURATION, 0, 0);
+ do_avdtp_send(sk, src, dst, AVDTP_OPEN, 0, 0);
+ }
+ hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+ hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+ hdr->signal_id = AVDTP_CLOSE;
+ if (invalid)
+ buf[2] = 13 << 2; /* Invalid ACP SEID */
+ else
+ buf[2] = 1 << 2; /* Valid ACP SEID */
+ len = write(sk, buf, 3);
+ break;
+
+ case AVDTP_SUSPEND:
+ if (invalid)
+ do_avdtp_send(sk, src, dst, AVDTP_OPEN, 0, preconf);
+ else
+ do_avdtp_send(sk, src, dst, AVDTP_START, 0, preconf);
+ hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+ hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+ hdr->signal_id = AVDTP_SUSPEND;
+ buf[2] = 1 << 2; /* ACP SEID */
+ len = write(sk, buf, 3);
+ break;
+
+ case AVDTP_ABORT:
+ do_avdtp_send(sk, src, dst, AVDTP_OPEN, 0, 1);
+ hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+ hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+ hdr->signal_id = AVDTP_ABORT;
+ buf[2] = 1 << 2; /* ACP SEID */
+ len = write(sk, buf, 3);
+ break;
+
+ default:
+ hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+ hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+ hdr->signal_id = cmd;
+ len = write(sk, buf, 2);
+ break;
+ }
+
+ do {
+ len = read(sk, buf, sizeof(buf));
+
+ dump_buffer(buf, len);
+ dump_avdtp_header(hdr);
+ } while (len < 2 || (hdr->message_type != AVDTP_MSG_TYPE_ACCEPT &&
+ hdr->message_type != AVDTP_MSG_TYPE_REJECT &&
+ hdr->message_type != AVDTP_MSG_TYPE_GEN_REJECT));
+
+ if (cmd == AVDTP_OPEN && len >= 2 &&
+ hdr->message_type == AVDTP_MSG_TYPE_ACCEPT)
+ media_sock = do_connect(src, dst, 0, 0);
+}
+
+static void do_avctp_send(int sk, int invalid)
+{
+ unsigned char buf[672];
+ struct avctp_header *hdr = (void *) buf;
+ unsigned char play_pressed[] = { 0x00, 0x48, 0x7c, 0x44, 0x00 };
+ ssize_t len;
+
+ memset(buf, 0, sizeof(buf));
+
+ hdr->packet_type = AVCTP_PACKET_SINGLE;
+ hdr->cr = AVCTP_COMMAND;
+ if (invalid)
+ hdr->pid = 0xffff;
+ else
+ hdr->pid = htons(AV_REMOTE_SVCLASS_ID);
+
+ memcpy(&buf[AVCTP_HEADER_LENGTH], play_pressed, sizeof(play_pressed));
+
+ len = write(sk, buf, AVCTP_HEADER_LENGTH + sizeof(play_pressed));
+
+ len = read(sk, buf, sizeof(buf));
+
+ dump_buffer(buf, len);
+ if (len >= AVCTP_HEADER_LENGTH)
+ dump_avctp_header(hdr);
+}
+
+static void usage()
+{
+ printf("avtest - Audio/Video testing ver %s\n", VERSION);
+ printf("Usage:\n"
+ "\tavtest [options] [remote address]\n");
+ printf("Options:\n"
+ "\t--device <hcidev> HCI device\n"
+ "\t--reject <command> Reject command\n"
+ "\t--send <command> Send command\n"
+ "\t--preconf Configure stream before actual command\n"
+ "\t--wait <N> Wait N seconds before exiting\n"
+ "\t--fragment Use minimum MTU and fragmented messages\n"
+ "\t--invalid <command> Send invalid command\n");
+}
+
+static struct option main_options[] = {
+ { "help", 0, 0, 'h' },
+ { "device", 1, 0, 'i' },
+ { "reject", 1, 0, 'r' },
+ { "send", 1, 0, 's' },
+ { "invalid", 1, 0, 'f' },
+ { "preconf", 0, 0, 'c' },
+ { "fragment", 0, 0, 'F' },
+ { "avctp", 0, 0, 'C' },
+ { "wait", 1, 0, 'w' },
+ { 0, 0, 0, 0 }
+};
+
+static unsigned char parse_cmd(const char *arg)
+{
+ if (!strncmp(arg, "discov", 6))
+ return AVDTP_DISCOVER;
+ else if (!strncmp(arg, "capa", 4))
+ return AVDTP_GET_CAPABILITIES;
+ else if (!strncmp(arg, "getcapa", 7))
+ return AVDTP_GET_CAPABILITIES;
+ else if (!strncmp(arg, "setconf", 7))
+ return AVDTP_SET_CONFIGURATION;
+ else if (!strncmp(arg, "getconf", 7))
+ return AVDTP_GET_CONFIGURATION;
+ else if (!strncmp(arg, "open", 4))
+ return AVDTP_OPEN;
+ else if (!strncmp(arg, "start", 5))
+ return AVDTP_START;
+ else if (!strncmp(arg, "close", 5))
+ return AVDTP_CLOSE;
+ else if (!strncmp(arg, "suspend", 7))
+ return AVDTP_SUSPEND;
+ else if (!strncmp(arg, "abort", 7))
+ return AVDTP_ABORT;
+ else
+ return atoi(arg);
+}
+
+enum {
+ MODE_NONE, MODE_REJECT, MODE_SEND,
+};
+
+int main(int argc, char *argv[])
+{
+ unsigned char cmd = 0x00;
+ bdaddr_t src, dst;
+ int opt, mode = MODE_NONE, sk, invalid = 0, preconf = 0, fragment = 0;
+ int avctp = 0, wait_before_exit = 0;
+
+ bacpy(&src, BDADDR_ANY);
+ bacpy(&dst, BDADDR_ANY);
+
+ while ((opt = getopt_long(argc, argv, "+i:r:s:f:hcFCw:",
+ main_options, NULL)) != EOF) {
+ switch (opt) {
+ case 'i':
+ if (!strncmp(optarg, "hci", 3))
+ hci_devba(atoi(optarg + 3), &src);
+ else
+ str2ba(optarg, &src);
+ break;
+
+ case 'r':
+ mode = MODE_REJECT;
+ cmd = parse_cmd(optarg);
+ break;
+
+ case 'f':
+ invalid = 1;
+ /* Intentionally missing break */
+
+ case 's':
+ mode = MODE_SEND;
+ cmd = parse_cmd(optarg);
+ break;
+
+ case 'c':
+ preconf = 1;
+ break;
+
+ case 'F':
+ fragment = 1;
+ break;
+
+ case 'C':
+ avctp = 1;
+ break;
+
+ case 'w':
+ wait_before_exit = atoi(optarg);
+ break;
+
+ case 'h':
+ default:
+ usage();
+ exit(0);
+ }
+ }
+
+ if (argv[optind])
+ str2ba(argv[optind], &dst);
+
+ if (avctp) {
+ avctp = mode;
+ mode = MODE_SEND;
+ }
+
+ switch (mode) {
+ case MODE_REJECT:
+ do_listen(&src, cmd, fragment);
+ break;
+ case MODE_SEND:
+ sk = do_connect(&src, &dst, avctp, fragment);
+ if (sk < 0)
+ exit(1);
+ if (avctp) {
+ if (avctp == MODE_SEND)
+ do_avctp_send(sk, invalid);
+ else
+ process_avctp(sk, cmd);
+ } else
+ do_avdtp_send(sk, &src, &dst, cmd, invalid, preconf);
+ if (wait_before_exit) {
+ printf("Waiting %d seconds before exiting\n", wait_before_exit);
+ sleep(wait_before_exit);
+ }
+ if (media_sock >= 0)
+ close(media_sock);
+ close(sk);
+ break;
+ default:
+ fprintf(stderr, "No operating mode specified!\n");
+ exit(1);
+ }
+
+ return 0;
+}
diff --git a/test/bdaddr.8 b/test/bdaddr.8
new file mode 100644
index 0000000..88345f8
--- /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 0000000..683b3b9
--- /dev/null
+++ b/test/bdaddr.c
@@ -0,0 +1,460 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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/btiotest.c b/test/btiotest.c
new file mode 100644
index 0000000..c02a25a
--- /dev/null
+++ b/test/btiotest.c
@@ -0,0 +1,555 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2009-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2009-2010 Nokia Corporation
+ *
+ *
+ * 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
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+
+#include <glib.h>
+
+#include "btio.h"
+
+#define DEFAULT_ACCEPT_TIMEOUT 2
+
+struct io_data {
+ guint ref;
+ GIOChannel *io;
+ BtIOType type;
+ gint reject;
+ gint disconn;
+ gint accept;
+};
+
+static void io_data_unref(struct io_data *data)
+{
+ data->ref--;
+
+ if (data->ref)
+ return;
+
+ if (data->io)
+ g_io_channel_unref(data->io);
+
+ g_free(data);
+}
+
+static struct io_data *io_data_ref(struct io_data *data)
+{
+ data->ref++;
+ return data;
+}
+
+static struct io_data *io_data_new(GIOChannel *io, BtIOType type, gint reject,
+ gint disconn, gint accept)
+{
+ struct io_data *data;
+
+ data = g_new0(struct io_data, 1);
+ data->io = io;
+ data->type = type;
+ data->reject = reject;
+ data->disconn = disconn;
+ data->accept = accept;
+
+ return io_data_ref(data);
+}
+
+static gboolean io_watch(GIOChannel *io, GIOCondition cond, gpointer user_data)
+{
+ printf("Disconnected\n");
+ return FALSE;
+}
+
+static gboolean disconn_timeout(gpointer user_data)
+{
+ struct io_data *data = user_data;
+
+ printf("Disconnecting\n");
+
+ g_io_channel_shutdown(data->io, TRUE, NULL);
+
+ return FALSE;
+}
+
+static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
+{
+ struct io_data *data = user_data;
+ GIOCondition cond;
+ char addr[18];
+ uint16_t handle;
+ uint8_t cls[3];
+
+ if (err) {
+ printf("Connecting failed: %s\n", err->message);
+ return;
+ }
+
+ if (!bt_io_get(io, data->type, &err,
+ BT_IO_OPT_DEST, addr,
+ BT_IO_OPT_HANDLE, &handle,
+ BT_IO_OPT_CLASS, cls,
+ BT_IO_OPT_INVALID)) {
+ printf("Unable to get destination address: %s\n",
+ err->message);
+ g_clear_error(&err);
+ strcpy(addr, "(unknown)");
+ }
+
+ printf("Successfully connected to %s. handle=%u, class=%02x%02x%02x\n",
+ addr, handle, cls[0], cls[1], cls[2]);
+
+ if (data->type == BT_IO_L2CAP || data->type == BT_IO_SCO) {
+ uint16_t omtu, imtu;
+
+ if (!bt_io_get(io, data->type, &err,
+ BT_IO_OPT_OMTU, &omtu,
+ BT_IO_OPT_IMTU, &imtu,
+ BT_IO_OPT_INVALID)) {
+ printf("Unable to get L2CAP MTU sizes: %s\n",
+ err->message);
+ g_clear_error(&err);
+ } else
+ printf("imtu=%u, omtu=%u\n", imtu, omtu);
+ }
+
+ if (data->disconn == 0) {
+ g_io_channel_shutdown(io, TRUE, NULL);
+ printf("Disconnected\n");
+ return;
+ }
+
+ if (data->io == NULL)
+ data->io = g_io_channel_ref(io);
+
+ if (data->disconn > 0) {
+ io_data_ref(data);
+ g_timeout_add_seconds_full(G_PRIORITY_DEFAULT, data->disconn,
+ disconn_timeout, data,
+ (GDestroyNotify) io_data_unref);
+ }
+
+
+ io_data_ref(data);
+ cond = G_IO_NVAL | G_IO_HUP | G_IO_ERR;
+ g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, io_watch, data,
+ (GDestroyNotify) io_data_unref);
+}
+
+static gboolean confirm_timeout(gpointer user_data)
+{
+ struct io_data *data = user_data;
+
+ if (data->reject >= 0) {
+ printf("Rejecting connection\n");
+ g_io_channel_shutdown(data->io, TRUE, NULL);
+ return FALSE;
+ }
+
+ printf("Accepting connection\n");
+
+ io_data_ref(data);
+
+ if (!bt_io_accept(data->io, connect_cb, data,
+ (GDestroyNotify) io_data_unref, NULL)) {
+ printf("bt_io_accept() failed\n");
+ io_data_unref(data);
+ }
+
+ return FALSE;
+}
+
+static void confirm_cb(GIOChannel *io, gpointer user_data)
+{
+ char addr[18];
+ struct io_data *data = user_data;
+ GError *err = NULL;
+
+ if (!bt_io_get(io, data->type, &err, BT_IO_OPT_DEST, addr,
+ BT_IO_OPT_INVALID)) {
+ printf("bt_io_get(OPT_DEST): %s\n", err->message);
+ g_clear_error(&err);
+ } else
+ printf("Got confirmation request for %s\n", addr);
+
+ if (data->accept < 0 && data->reject < 0)
+ return;
+
+ if (data->reject == 0) {
+ printf("Rejecting connection\n");
+ g_io_channel_shutdown(io, TRUE, NULL);
+ return;
+ }
+
+ data->io = g_io_channel_ref(io);
+ io_data_ref(data);
+
+ if (data->accept == 0) {
+ if (!bt_io_accept(io, connect_cb, data,
+ (GDestroyNotify) io_data_unref,
+ &err)) {
+ printf("bt_io_accept() failed: %s\n", err->message);
+ g_clear_error(&err);
+ io_data_unref(data);
+ return;
+ }
+ } else {
+ gint seconds = (data->reject > 0) ?
+ data->reject : data->accept;
+ g_timeout_add_seconds_full(G_PRIORITY_DEFAULT, seconds,
+ confirm_timeout, data,
+ (GDestroyNotify) io_data_unref);
+ }
+}
+
+static void l2cap_connect(const char *src, const char *dst, uint16_t psm,
+ gint disconn, gint sec)
+{
+ struct io_data *data;
+ GError *err = NULL;
+
+ printf("Connecting to %s L2CAP PSM %u\n", dst, psm);
+
+ data = io_data_new(NULL, BT_IO_L2CAP, -1, disconn, -1);
+
+ if (src)
+ data->io = bt_io_connect(BT_IO_L2CAP, connect_cb, data,
+ (GDestroyNotify) io_data_unref,
+ &err,
+ BT_IO_OPT_SOURCE, src,
+ BT_IO_OPT_DEST, dst,
+ BT_IO_OPT_PSM, psm,
+ BT_IO_OPT_SEC_LEVEL, sec,
+ BT_IO_OPT_INVALID);
+ else
+ data->io = bt_io_connect(BT_IO_L2CAP, connect_cb, data,
+ (GDestroyNotify) io_data_unref,
+ &err,
+ BT_IO_OPT_DEST, dst,
+ BT_IO_OPT_PSM, psm,
+ BT_IO_OPT_SEC_LEVEL, sec,
+ BT_IO_OPT_INVALID);
+
+ if (!data->io) {
+ printf("Connecting to %s failed: %s\n", dst, err->message);
+ g_error_free(err);
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void l2cap_listen(const char *src, uint16_t psm, gint defer,
+ gint reject, gint disconn, gint accept,
+ gint sec, gboolean master)
+{
+ struct io_data *data;
+ BtIOConnect conn;
+ BtIOConfirm cfm;
+ GIOChannel *l2_srv;
+ GError *err = NULL;
+
+ if (defer) {
+ conn = NULL;
+ cfm = confirm_cb;
+ } else {
+ conn = connect_cb;
+ cfm = NULL;
+ }
+
+ printf("Listening on L2CAP PSM %u\n", psm);
+
+ data = io_data_new(NULL, BT_IO_L2CAP, reject, disconn, accept);
+
+ if (src)
+ l2_srv = bt_io_listen(BT_IO_L2CAP, conn, cfm,
+ data, (GDestroyNotify) io_data_unref,
+ &err,
+ BT_IO_OPT_SOURCE, src,
+ BT_IO_OPT_PSM, psm,
+ BT_IO_OPT_SEC_LEVEL, sec,
+ BT_IO_OPT_MASTER, master,
+ BT_IO_OPT_INVALID);
+ else
+ l2_srv = bt_io_listen(BT_IO_L2CAP, conn, cfm,
+ data, (GDestroyNotify) io_data_unref,
+ &err,
+ BT_IO_OPT_PSM, psm,
+ BT_IO_OPT_SEC_LEVEL, sec,
+ BT_IO_OPT_MASTER, master,
+ BT_IO_OPT_INVALID);
+
+ if (!l2_srv) {
+ printf("Listening failed: %s\n", err->message);
+ g_error_free(err);
+ exit(EXIT_FAILURE);
+ }
+
+ g_io_channel_unref(l2_srv);
+}
+
+static void rfcomm_connect(const char *src, const char *dst, uint8_t ch,
+ gint disconn, gint sec)
+{
+ struct io_data *data;
+ GError *err = NULL;
+
+ printf("Connecting to %s RFCOMM channel %u\n", dst, ch);
+
+ data = io_data_new(NULL, BT_IO_RFCOMM, -1, disconn, -1);
+
+ if (src)
+ data->io = bt_io_connect(BT_IO_RFCOMM, connect_cb, data,
+ (GDestroyNotify) io_data_unref,
+ &err,
+ BT_IO_OPT_SOURCE, src,
+ BT_IO_OPT_DEST, dst,
+ BT_IO_OPT_CHANNEL, ch,
+ BT_IO_OPT_SEC_LEVEL, sec,
+ BT_IO_OPT_INVALID);
+ else
+ data->io = bt_io_connect(BT_IO_RFCOMM, connect_cb, data,
+ (GDestroyNotify) io_data_unref,
+ &err,
+ BT_IO_OPT_DEST, dst,
+ BT_IO_OPT_CHANNEL, ch,
+ BT_IO_OPT_SEC_LEVEL, sec,
+ BT_IO_OPT_INVALID);
+
+ if (!data->io) {
+ printf("Connecting to %s failed: %s\n", dst, err->message);
+ g_error_free(err);
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void rfcomm_listen(const char *src, uint8_t ch, gboolean defer,
+ gint reject, gint disconn, gint accept,
+ gint sec, gboolean master)
+{
+ struct io_data *data;
+ BtIOConnect conn;
+ BtIOConfirm cfm;
+ GIOChannel *rc_srv;
+ GError *err = NULL;
+
+ if (defer) {
+ conn = NULL;
+ cfm = confirm_cb;
+ } else {
+ conn = connect_cb;
+ cfm = NULL;
+ }
+
+ data = io_data_new(NULL, BT_IO_RFCOMM, reject, disconn, accept);
+
+ if (src)
+ rc_srv = bt_io_listen(BT_IO_RFCOMM, conn, cfm,
+ data, (GDestroyNotify) io_data_unref,
+ &err,
+ BT_IO_OPT_SOURCE, src,
+ BT_IO_OPT_CHANNEL, ch,
+ BT_IO_OPT_SEC_LEVEL, sec,
+ BT_IO_OPT_MASTER, master,
+ BT_IO_OPT_INVALID);
+ else
+ rc_srv = bt_io_listen(BT_IO_RFCOMM, conn, cfm,
+ data, (GDestroyNotify) io_data_unref,
+ &err,
+ BT_IO_OPT_CHANNEL, ch,
+ BT_IO_OPT_SEC_LEVEL, sec,
+ BT_IO_OPT_MASTER, master,
+ BT_IO_OPT_INVALID);
+
+ if (!rc_srv) {
+ printf("Listening failed: %s\n", err->message);
+ g_error_free(err);
+ exit(EXIT_FAILURE);
+ }
+
+ bt_io_get(rc_srv, BT_IO_RFCOMM, &err,
+ BT_IO_OPT_CHANNEL, &ch,
+ BT_IO_OPT_INVALID);
+
+ printf("Listening on RFCOMM channel %u\n", ch);
+
+ g_io_channel_unref(rc_srv);
+}
+
+static void sco_connect(const char *src, const char *dst, gint disconn)
+{
+ struct io_data *data;
+ GError *err = NULL;
+
+ printf("Connecting SCO to %s\n", dst);
+
+ data = io_data_new(NULL, BT_IO_SCO, -1, disconn, -1);
+
+ if (src)
+ data->io = bt_io_connect(BT_IO_SCO, connect_cb, data,
+ (GDestroyNotify) io_data_unref,
+ &err,
+ BT_IO_OPT_SOURCE, src,
+ BT_IO_OPT_DEST, dst,
+ BT_IO_OPT_INVALID);
+ else
+ data->io = bt_io_connect(BT_IO_SCO, connect_cb, data,
+ (GDestroyNotify) io_data_unref,
+ &err,
+ BT_IO_OPT_DEST, dst,
+ BT_IO_OPT_INVALID);
+
+ if (!data->io) {
+ printf("Connecting to %s failed: %s\n", dst, err->message);
+ g_error_free(err);
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void sco_listen(const char *src, gint disconn)
+{
+ struct io_data *data;
+ GIOChannel *sco_srv;
+ GError *err = NULL;
+
+ printf("Listening for SCO connections\n");
+
+ data = io_data_new(NULL, BT_IO_SCO, -1, disconn, -1);
+
+ if (src)
+ sco_srv = bt_io_listen(BT_IO_SCO, connect_cb, NULL,
+ data, (GDestroyNotify) io_data_unref,
+ &err,
+ BT_IO_OPT_SOURCE, src,
+ BT_IO_OPT_INVALID);
+ else
+ sco_srv = bt_io_listen(BT_IO_SCO, connect_cb, NULL,
+ data, (GDestroyNotify) io_data_unref,
+ &err, BT_IO_OPT_INVALID);
+
+ if (!sco_srv) {
+ printf("Listening failed: %s\n", err->message);
+ g_error_free(err);
+ exit(EXIT_FAILURE);
+ }
+
+ g_io_channel_unref(sco_srv);
+}
+
+static gint opt_channel = -1;
+static gint opt_psm = 0;
+static gboolean opt_sco = FALSE;
+static gboolean opt_defer = FALSE;
+static char *opt_dev = NULL;
+static gint opt_reject = -1;
+static gint opt_disconn = -1;
+static gint opt_accept = DEFAULT_ACCEPT_TIMEOUT;
+static gint opt_sec = 0;
+static gboolean opt_master = FALSE;
+
+static GMainLoop *main_loop;
+
+static GOptionEntry options[] = {
+ { "channel", 'c', 0, G_OPTION_ARG_INT, &opt_channel,
+ "RFCOMM channel" },
+ { "psm", 'p', 0, G_OPTION_ARG_INT, &opt_psm,
+ "L2CAP PSM" },
+ { "sco", 's', 0, G_OPTION_ARG_NONE, &opt_sco,
+ "Use SCO" },
+ { "defer", 'd', 0, G_OPTION_ARG_NONE, &opt_defer,
+ "Use DEFER_SETUP for incoming connections" },
+ { "sec-level", 'S', 0, G_OPTION_ARG_INT, &opt_sec,
+ "Security level" },
+ { "dev", 'i', 0, G_OPTION_ARG_STRING, &opt_dev,
+ "Which HCI device to use" },
+ { "reject", 'r', 0, G_OPTION_ARG_INT, &opt_reject,
+ "Reject connection after N seconds" },
+ { "disconnect", 'D', 0, G_OPTION_ARG_INT, &opt_disconn,
+ "Disconnect connection after N seconds" },
+ { "accept", 'a', 0, G_OPTION_ARG_INT, &opt_accept,
+ "Accept connection after N seconds" },
+ { "master", 'm', 0, G_OPTION_ARG_NONE, &opt_master,
+ "Master role switch (incoming connections)" },
+ { NULL },
+};
+
+static void sig_term(int sig)
+{
+ g_main_loop_quit(main_loop);
+}
+
+int main(int argc, char *argv[])
+{
+ GOptionContext *context;
+
+ context = g_option_context_new(NULL);
+ g_option_context_add_main_entries(context, options, NULL);
+
+ if (!g_option_context_parse(context, &argc, &argv, NULL))
+ exit(EXIT_FAILURE);
+
+ g_option_context_free(context);
+
+ printf("accept=%d, reject=%d, discon=%d, defer=%d, sec=%d\n",
+ opt_accept, opt_reject, opt_disconn, opt_defer, opt_sec);
+
+ if (opt_psm) {
+ if (argc > 1)
+ l2cap_connect(opt_dev, argv[1], opt_psm,
+ opt_disconn, opt_sec);
+ else
+ l2cap_listen(opt_dev, opt_psm, opt_defer, opt_reject,
+ opt_disconn, opt_accept, opt_sec,
+ opt_master);
+ }
+
+ if (opt_channel != -1) {
+ if (argc > 1)
+ rfcomm_connect(opt_dev, argv[1], opt_channel,
+ opt_disconn, opt_sec);
+ else
+ rfcomm_listen(opt_dev, opt_channel, opt_defer,
+ opt_reject, opt_disconn, opt_accept,
+ opt_sec, opt_master);
+ }
+
+ if (opt_sco) {
+ if (argc > 1)
+ sco_connect(opt_dev, argv[1], opt_disconn);
+ else
+ sco_listen(opt_dev, opt_disconn);
+ }
+
+ signal(SIGTERM, sig_term);
+ signal(SIGINT, sig_term);
+
+ main_loop = g_main_loop_new(NULL, FALSE);
+
+ g_main_loop_run(main_loop);
+
+ g_main_loop_unref(main_loop);
+
+ printf("Exiting\n");
+
+ exit(EXIT_SUCCESS);
+}
diff --git a/test/dbusdef.py b/test/dbusdef.py
new file mode 100644
index 0000000..5af6153
--- /dev/null
+++ b/test/dbusdef.py
@@ -0,0 +1,16 @@
+import dbus
+
+bus = dbus.SystemBus()
+
+
+dummy = dbus.Interface(bus.get_object('org.bluez', '/'), 'org.freedesktop.DBus.Introspectable')
+
+#print dummy.Introspect()
+
+
+manager = dbus.Interface(bus.get_object('org.bluez', '/'), 'org.bluez.Manager')
+
+try:
+ adapter = dbus.Interface(bus.get_object('org.bluez', manager.DefaultAdapter()), 'org.bluez.Adapter')
+except:
+ pass
diff --git a/test/gaptest.c b/test/gaptest.c
new file mode 100644
index 0000000..3e9f534
--- /dev/null
+++ b/test/gaptest.c
@@ -0,0 +1,335 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2007-2010 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 <string.h>
+#include <getopt.h>
+
+#include <dbus/dbus.h>
+
+#define BLUEZ_SERVICE "org.bluez"
+
+#define MANAGER_PATH "/"
+#define MANAGER_INTF BLUEZ_SERVICE ".Manager"
+#define ADAPTER_INTF BLUEZ_SERVICE ".Adapter"
+
+static char *get_adapter(DBusConnection *conn)
+{
+ DBusMessage *message, *reply;
+ DBusError error;
+ const char *path;
+ char *result = NULL;
+
+ message = dbus_message_new_method_call(BLUEZ_SERVICE, MANAGER_PATH,
+ MANAGER_INTF, "DefaultAdapter");
+ if (!message)
+ return NULL;
+
+ dbus_error_init(&error);
+
+ reply = dbus_connection_send_with_reply_and_block(conn,
+ message, -1, &error);
+
+ dbus_message_unref(message);
+
+ if (!reply) {
+ if (dbus_error_is_set(&error) == TRUE) {
+ fprintf(stderr, "%s\n", error.message);
+ dbus_error_free(&error);
+ } else
+ fprintf(stderr, "Failed to set property\n");
+ return NULL;
+ }
+
+ if (dbus_message_get_args(reply, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID) == FALSE)
+ goto done;
+
+ printf("Using default adapter %s\n", path);
+
+ result = strdup(path);
+
+done:
+ dbus_message_unref(reply);
+
+ return result;
+}
+
+static char *find_device(DBusConnection *conn, const char *adapter,
+ const char *address)
+{
+ DBusMessage *message, *reply;
+ DBusError error;
+ const char *path;
+ char *result = NULL;
+
+ message = dbus_message_new_method_call(BLUEZ_SERVICE, adapter,
+ ADAPTER_INTF, "FindDevice");
+ if (!message)
+ return NULL;
+
+ dbus_message_append_args(message, DBUS_TYPE_STRING, &address,
+ DBUS_TYPE_INVALID);
+
+ dbus_error_init(&error);
+
+ reply = dbus_connection_send_with_reply_and_block(conn,
+ message, -1, &error);
+
+ dbus_message_unref(message);
+
+ if (!reply) {
+ if (dbus_error_is_set(&error) == TRUE) {
+ fprintf(stderr, "%s\n", error.message);
+ dbus_error_free(&error);
+ } else
+ fprintf(stderr, "Failed to set property\n");
+ return NULL;
+ }
+
+ if (dbus_message_get_args(reply, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID) == FALSE)
+ goto done;
+
+ printf("Using device %s for address %s\n", path, address);
+
+ result = strdup(path);
+
+done:
+ dbus_message_unref(reply);
+
+ return result;
+}
+
+static int remove_device(DBusConnection *conn, const char *adapter,
+ const char *device)
+{
+ DBusMessage *message, *reply;
+ DBusError error;
+
+ message = dbus_message_new_method_call(BLUEZ_SERVICE, adapter,
+ ADAPTER_INTF, "RemoveDevice");
+ if (!message)
+ return -ENOMEM;
+
+ dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &device,
+ DBUS_TYPE_INVALID);
+
+ dbus_error_init(&error);
+
+ reply = dbus_connection_send_with_reply_and_block(conn,
+ message, -1, &error);
+
+ dbus_message_unref(message);
+
+ if (!reply) {
+ if (dbus_error_is_set(&error) == TRUE) {
+ fprintf(stderr, "%s\n", error.message);
+ dbus_error_free(&error);
+ } else
+ fprintf(stderr, "Failed to set property\n");
+ return -EIO;
+ }
+
+ dbus_message_unref(reply);
+
+ printf("Removed device %s\n", device);
+
+ return 0;
+}
+
+static int set_property(DBusConnection *conn, const char *adapter,
+ const char *key, int type, void *val)
+{
+ DBusMessage *message, *reply;
+ DBusMessageIter array, value;
+ DBusError error;
+ const char *signature;
+
+ message = dbus_message_new_method_call(BLUEZ_SERVICE, adapter,
+ ADAPTER_INTF, "SetProperty");
+ if (!message)
+ return -ENOMEM;
+
+ switch (type) {
+ case DBUS_TYPE_BOOLEAN:
+ signature = DBUS_TYPE_BOOLEAN_AS_STRING;
+ break;
+ case DBUS_TYPE_UINT32:
+ signature = DBUS_TYPE_UINT32_AS_STRING;
+ break;
+ default:
+ return -EILSEQ;
+ }
+
+ dbus_message_iter_init_append(message, &array);
+
+ dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &key);
+
+ dbus_message_iter_open_container(&array, DBUS_TYPE_VARIANT,
+ signature, &value);
+ dbus_message_iter_append_basic(&value, type, val);
+ dbus_message_iter_close_container(&array, &value);
+
+ dbus_error_init(&error);
+
+ reply = dbus_connection_send_with_reply_and_block(conn,
+ message, -1, &error);
+
+ dbus_message_unref(message);
+
+ if (!reply) {
+ if (dbus_error_is_set(&error) == TRUE) {
+ fprintf(stderr, "%s\n", error.message);
+ dbus_error_free(&error);
+ } else
+ fprintf(stderr, "Failed to set property\n");
+ return -EIO;
+ }
+
+ dbus_message_unref(reply);
+
+ printf("Set property %s for %s\n", key, adapter);
+
+ return 0;
+}
+
+static void usage(void)
+{
+ printf("gaptest - GAP testing\n"
+ "Usage:\n");
+ printf("\tgaptest [options]\n");
+ printf("Options:\n"
+ "\t-T <timeout> Set timeout\n"
+ "\t-P <powered> Set powered\n"
+ "\t-D <discoverable> Set discoverable\n"
+ "\t-B <pairable> Set pairable\n"
+ "\t-C <address> Create device\n"
+ "\t-R <address> Remove device\n");
+}
+
+int main(int argc, char *argv[])
+{
+ DBusConnection *conn;
+ char *adapter, *device;
+ const char *create = NULL, *remove = NULL;
+ int opt, timeout = -1, powered = -1, discoverable = -1, pairable = -1;
+
+ while ((opt = getopt(argc, argv, "T:P:D:B:C:R:h")) != EOF) {
+ switch (opt) {
+ case 'T':
+ timeout = atoi(optarg);
+ break;
+ case 'P':
+ powered = atoi(optarg);
+ break;
+ case 'D':
+ discoverable = atoi(optarg);
+ break;
+ case 'B':
+ pairable = atoi(optarg);
+ break;
+ case 'C':
+ create = optarg;
+ break;
+ case 'R':
+ remove = optarg;
+ break;
+ case 'h':
+ usage();
+ exit(0);
+ default:
+ usage();
+ exit(1);
+ }
+ }
+
+ conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+ if (!conn) {
+ fprintf(stderr, "Can't get on system bus\n");
+ exit(1);
+ }
+
+ adapter = get_adapter(conn);
+ if (!adapter) {
+ fprintf(stderr, "Can't get default adapter\n");
+ exit(1);
+ }
+
+ if (powered >= 0) {
+ set_property(conn, adapter, "Powered",
+ DBUS_TYPE_BOOLEAN, &powered);
+ }
+
+ if (discoverable >= 0) {
+ set_property(conn, adapter, "Discoverable",
+ DBUS_TYPE_BOOLEAN, &discoverable);
+
+ if (timeout >= 0)
+ set_property(conn, adapter, "DiscoverableTimeout",
+ DBUS_TYPE_UINT32, &timeout);
+ }
+
+ if (pairable >= 0) {
+ set_property(conn, adapter, "Pairable",
+ DBUS_TYPE_BOOLEAN, &pairable);
+
+ if (timeout >= 0)
+ set_property(conn, adapter, "PairableTimeout",
+ DBUS_TYPE_UINT32, &timeout);
+ }
+
+ if (create) {
+ device = find_device(conn, adapter, create);
+ if (!device) {
+ fprintf(stderr, "Can't find device\n");
+ exit(1);
+ }
+
+ free(device);
+ }
+
+ if (remove) {
+ device = find_device(conn, adapter, remove);
+ if (!device) {
+ fprintf(stderr, "Can't find device\n");
+ exit(1);
+ }
+
+ remove_device(conn, adapter, device);
+
+ free(device);
+ }
+
+ free(adapter);
+
+ dbus_connection_unref(conn);
+
+ return 0;
+}
diff --git a/test/hciemu.1 b/test/hciemu.1
new file mode 100644
index 0000000..cecaeb7
--- /dev/null
+++ b/test/hciemu.1
@@ -0,0 +1,31 @@
+.TH HCIEMU 1 "Jul 6 2009" BlueZ ""
+.SH NAME
+hciemu \- HCI emulator
+.SH SYNOPSIS
+.B hciemu
+[\fIoptions\fR] \fIlocal_address\fR
+
+.SH DESCRIPTION
+.LP
+.B
+hciemu
+is used to emulate an HCI via \fBhci_vhci\fR kernel module
+
+.SH OPTIONS
+.TP
+.BI -d\ device
+use specified \fIdevice\fR
+.TP
+.BI -b\ bdaddr
+emulate \fIbdaddr\fR
+.TP
+.BI -s\ file
+create snoop file \fIfile\fR
+.TP
+.B -n
+do not detach
+
+.SH AUTHORS
+Written by Marcel Holtmann <marcel@holtmann.org> and Maxim Krasnyansky
+<maxk@qualcomm.com>, man page by Filippo Giunchedi <filippo@debian.org>
+.PP
diff --git a/test/hciemu.c b/test/hciemu.c
new file mode 100644
index 0000000..9950372
--- /dev/null
+++ b/test/hciemu.c
@@ -0,0 +1,1343 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2000-2002 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2003-2010 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>
+
+#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_shutdown(vdev.scan, TRUE, NULL);
+ 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;
+ memcpy(&sa.sin_addr.s_addr, &ba, sizeof(sa.sin_addr.s_addr));
+ 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)
+{
+ char addr[18];
+
+ ba2str(&conn->dest, addr);
+ syslog(LOG_INFO, "Closing connection %s handle %d",
+ addr, conn->handle);
+
+ g_io_channel_shutdown(conn->chan, TRUE, NULL);
+ 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 > 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;
+ memcpy(&sa.sin_addr.s_addr, &ba, sizeof(sa.sin_addr.s_addr));
+ 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 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 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 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 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;
+ ssize_t len;
+ int fd;
+
+ ptr = buf;
+
+ fd = g_io_channel_unix_get_fd(chan);
+
+ len = read(fd, buf, sizeof(buf));
+ if (len < 0) {
+ if (errno == EAGAIN)
+ 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 */
+ str2ba(str, ba);
+ 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 [options] local_address\n"
+ "Options:\n"
+ "\t[-d device] use specified device\n"
+ "\t[-b bdaddr] emulate specified address\n"
+ "\t[-s file] create snoop file\n"
+ "\t[-n] do not detach\n"
+ "\t[-h] help, you are looking at it\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) - 1);
+
+ 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 0000000..c254226
--- /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 0000000..8cecbff
--- /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 0000000..08f2257
--- /dev/null
+++ b/test/hstest.c
@@ -0,0 +1,308 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2010 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/ipctest.c b/test/ipctest.c
new file mode 100644
index 0000000..9fdfac4
--- /dev/null
+++ b/test/ipctest.c
@@ -0,0 +1,1129 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2009 Lennart Poettering
+ * Copyright (C) 2008 Joao Paulo Rechi Vita
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; 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 <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <assert.h>
+#include <libgen.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#include <glib.h>
+
+#include "ipc.h"
+#include "sbc.h"
+
+#define DBG(fmt, arg...) \
+ printf("debug %s: " fmt "\n" , __FUNCTION__ , ## arg)
+#define ERR(fmt, arg...) \
+ fprintf(stderr, "ERROR %s: " fmt "\n" , __FUNCTION__ , ## arg)
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#ifndef MIN
+# define MIN(x, y) ((x) < (y) ? (x) : (y))
+#endif
+
+#ifndef MAX
+# define MAX(x, y) ((x) > (y) ? (x) : (y))
+#endif
+
+#ifndef TRUE
+# define TRUE (1)
+#endif
+
+#ifndef FALSE
+# define FALSE (0)
+#endif
+
+#define YES_NO(t) ((t) ? "yes" : "no")
+
+#define BUFFER_SIZE 2048
+#define MAX_BITPOOL 64
+#define MIN_BITPOOL 2
+
+struct a2dp_info {
+ sbc_capabilities_t sbc_capabilities;
+ sbc_t sbc; /* Codec data */
+ int sbc_initialized; /* Keep track if the encoder is initialized */
+ size_t codesize; /* SBC codesize */
+
+ void* buffer; /* Codec transfer buffer */
+ size_t buffer_size; /* Size of the buffer */
+
+ uint16_t seq_num; /* Cumulative packet sequence */
+};
+
+struct hsp_info {
+ pcm_capabilities_t pcm_capabilities;
+};
+
+struct userdata {
+ int service_fd;
+ int stream_fd;
+ GIOChannel *stream_channel;
+ guint stream_watch;
+ GIOChannel *gin; /* dude, I am thirsty now */
+ guint gin_watch;
+ int transport;
+ uint32_t rate;
+ int channels;
+ char *address;
+ struct a2dp_info a2dp;
+ struct hsp_info hsp;
+ size_t link_mtu;
+ size_t block_size;
+ gboolean debug_stream_read : 1;
+ gboolean debug_stream_write : 1;
+};
+
+static struct userdata data = {
+ .service_fd = -1,
+ .stream_fd = -1,
+ .transport = BT_CAPABILITIES_TRANSPORT_A2DP,
+ .rate = 48000,
+ .channels = 2,
+ .address = NULL
+};
+
+static int start_stream(struct userdata *u);
+static int stop_stream(struct userdata *u);
+static gboolean input_cb(GIOChannel *gin, GIOCondition condition, gpointer data);
+
+static GMainLoop *main_loop;
+
+static int service_send(struct userdata *u, const bt_audio_msg_header_t *msg)
+{
+ int err;
+ uint16_t length;
+
+ assert(u);
+
+ length = msg->length ? msg->length : BT_SUGGESTED_BUFFER_SIZE;
+
+ DBG("sending %s:%s", bt_audio_strtype(msg->type),
+ bt_audio_strname(msg->name));
+
+ if (send(u->service_fd, msg, length, 0) > 0)
+ err = 0;
+ else {
+ err = -errno;
+ ERR("Error sending data to audio service: %s(%d)",
+ strerror(errno), errno);
+ }
+
+ return err;
+}
+
+static int service_recv(struct userdata *u, bt_audio_msg_header_t *rsp)
+{
+ int err;
+ const char *type, *name;
+ uint16_t length;
+
+ assert(u);
+
+ length = rsp->length ? : BT_SUGGESTED_BUFFER_SIZE;
+
+ DBG("trying to receive msg from audio service...");
+ if (recv(u->service_fd, rsp, length, 0) > 0) {
+ type = bt_audio_strtype(rsp->type);
+ name = bt_audio_strname(rsp->name);
+ if (type && name) {
+ DBG("Received %s - %s", type, name);
+ err = 0;
+ } else {
+ err = -EINVAL;
+ ERR("Bogus message type %d - name %d"
+ "received from audio service",
+ rsp->type, rsp->name);
+ }
+ } else {
+ err = -errno;
+ ERR("Error receiving data from audio service: %s(%d)",
+ strerror(errno), errno);
+ }
+
+ return err;
+}
+
+static ssize_t service_expect(struct userdata *u, bt_audio_msg_header_t *rsp,
+ uint8_t expected_name)
+{
+ int r;
+
+ assert(u);
+ assert(u->service_fd >= 0);
+ assert(rsp);
+
+ if ((r = service_recv(u, rsp)) < 0)
+ return r;
+
+ if ((rsp->type != BT_INDICATION && rsp->type != BT_RESPONSE) ||
+ (rsp->name != expected_name)) {
+ if (rsp->type == BT_ERROR && rsp->length == sizeof(bt_audio_error_t))
+ ERR("Received error condition: %s",
+ strerror(((bt_audio_error_t*) rsp)->posix_errno));
+ else
+ ERR("Bogus message %s received while %s was expected",
+ bt_audio_strname(rsp->name),
+ bt_audio_strname(expected_name));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int init_bt(struct userdata *u)
+{
+ assert(u);
+
+ if (u->service_fd != -1)
+ return 0;
+
+ DBG("bt_audio_service_open");
+
+ u->service_fd = bt_audio_service_open();
+ if (u->service_fd <= 0) {
+ perror(strerror(errno));
+ return errno;
+ }
+
+ return 0;
+}
+
+static int parse_caps(struct userdata *u, const struct bt_get_capabilities_rsp *rsp)
+{
+ unsigned char *ptr;
+ uint16_t bytes_left;
+ codec_capabilities_t codec;
+
+ assert(u);
+ assert(rsp);
+
+ bytes_left = rsp->h.length - sizeof(*rsp);
+
+ if (bytes_left < sizeof(codec_capabilities_t)) {
+ ERR("Packet too small to store codec information.");
+ return -1;
+ }
+
+ ptr = ((void *) rsp) + sizeof(*rsp);
+
+ memcpy(&codec, ptr, sizeof(codec)); /** ALIGNMENT? **/
+
+ DBG("Payload size is %lu %lu",
+ (unsigned long) bytes_left, (unsigned long) sizeof(codec));
+
+ if (u->transport != codec.transport) {
+ ERR("Got capabilities for wrong codec.");
+ return -1;
+ }
+
+ if (u->transport == BT_CAPABILITIES_TRANSPORT_SCO) {
+
+ if (bytes_left <= 0 ||
+ codec.length != sizeof(u->hsp.pcm_capabilities))
+ return -1;
+
+ assert(codec.type == BT_HFP_CODEC_PCM);
+
+ memcpy(&u->hsp.pcm_capabilities,
+ &codec, sizeof(u->hsp.pcm_capabilities));
+
+ DBG("Has NREC: %s",
+ YES_NO(u->hsp.pcm_capabilities.flags & BT_PCM_FLAG_NREC));
+
+ } else if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
+
+ while (bytes_left > 0) {
+ if (codec.type == BT_A2DP_SBC_SINK &&
+ !(codec.lock & BT_WRITE_LOCK))
+ break;
+
+ bytes_left -= codec.length;
+ ptr += codec.length;
+ memcpy(&codec, ptr, sizeof(codec));
+ }
+
+ DBG("bytes_left = %d, codec.length = %d",
+ bytes_left, codec.length);
+
+ if (bytes_left <= 0 ||
+ codec.length != sizeof(u->a2dp.sbc_capabilities))
+ return -1;
+
+ assert(codec.type == BT_A2DP_SBC_SINK);
+
+ memcpy(&u->a2dp.sbc_capabilities, &codec,
+ sizeof(u->a2dp.sbc_capabilities));
+ } else {
+ assert(0);
+ }
+
+ return 0;
+}
+
+static int get_caps(struct userdata *u)
+{
+ union {
+ struct bt_get_capabilities_req getcaps_req;
+ struct bt_get_capabilities_rsp getcaps_rsp;
+ bt_audio_error_t error;
+ uint8_t buf[BT_SUGGESTED_BUFFER_SIZE];
+ } msg;
+
+ assert(u);
+
+ memset(&msg, 0, sizeof(msg));
+ msg.getcaps_req.h.type = BT_REQUEST;
+ msg.getcaps_req.h.name = BT_GET_CAPABILITIES;
+ msg.getcaps_req.h.length = sizeof(msg.getcaps_req);
+
+ strncpy(msg.getcaps_req.destination, u->address,
+ sizeof(msg.getcaps_req.destination));
+ msg.getcaps_req.transport = u->transport;
+ msg.getcaps_req.flags = BT_FLAG_AUTOCONNECT;
+
+ if (service_send(u, &msg.getcaps_req.h) < 0)
+ return -1;
+
+ msg.getcaps_rsp.h.length = 0;
+ if (service_expect(u, &msg.getcaps_rsp.h, BT_GET_CAPABILITIES) < 0)
+ return -1;
+
+ return parse_caps(u, &msg.getcaps_rsp);
+}
+
+static uint8_t a2dp_default_bitpool(uint8_t freq, uint8_t mode)
+{
+ switch (freq) {
+ case BT_SBC_SAMPLING_FREQ_16000:
+ case BT_SBC_SAMPLING_FREQ_32000:
+ return 53;
+
+ case BT_SBC_SAMPLING_FREQ_44100:
+
+ switch (mode) {
+ case BT_A2DP_CHANNEL_MODE_MONO:
+ case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL:
+ return 31;
+
+ case BT_A2DP_CHANNEL_MODE_STEREO:
+ case BT_A2DP_CHANNEL_MODE_JOINT_STEREO:
+ return 53;
+
+ default:
+ DBG("Invalid channel mode %u", mode);
+ return 53;
+ }
+
+ case BT_SBC_SAMPLING_FREQ_48000:
+
+ switch (mode) {
+ case BT_A2DP_CHANNEL_MODE_MONO:
+ case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL:
+ return 29;
+
+ case BT_A2DP_CHANNEL_MODE_STEREO:
+ case BT_A2DP_CHANNEL_MODE_JOINT_STEREO:
+ return 51;
+
+ default:
+ DBG("Invalid channel mode %u", mode);
+ return 51;
+ }
+
+ default:
+ DBG("Invalid sampling freq %u", freq);
+ return 53;
+ }
+}
+
+static int setup_a2dp(struct userdata *u)
+{
+ sbc_capabilities_t *cap;
+ int i;
+
+ static const struct {
+ uint32_t rate;
+ uint8_t cap;
+ } freq_table[] = {
+ { 16000U, BT_SBC_SAMPLING_FREQ_16000 },
+ { 32000U, BT_SBC_SAMPLING_FREQ_32000 },
+ { 44100U, BT_SBC_SAMPLING_FREQ_44100 },
+ { 48000U, BT_SBC_SAMPLING_FREQ_48000 }
+ };
+
+ assert(u);
+ assert(u->transport == BT_CAPABILITIES_TRANSPORT_A2DP);
+
+ cap = &u->a2dp.sbc_capabilities;
+
+ /* Find the lowest freq that is at least as high as the requested
+ * sampling rate */
+ for (i = 0; (unsigned) i < ARRAY_SIZE(freq_table); i++)
+ if (freq_table[i].rate >= u->rate &&
+ (cap->frequency & freq_table[i].cap)) {
+ u->rate = freq_table[i].rate;
+ cap->frequency = freq_table[i].cap;
+ break;
+ }
+
+ if ((unsigned) i >= ARRAY_SIZE(freq_table)) {
+ for (; i >= 0; i--) {
+ if (cap->frequency & freq_table[i].cap) {
+ u->rate = freq_table[i].rate;
+ cap->frequency = freq_table[i].cap;
+ break;
+ }
+ }
+
+ if (i < 0) {
+ DBG("Not suitable sample rate");
+ return -1;
+ }
+ }
+
+ if (u->channels <= 1) {
+ if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) {
+ cap->channel_mode = BT_A2DP_CHANNEL_MODE_MONO;
+ u->channels = 1;
+ } else
+ u->channels = 2;
+ }
+
+ if (u->channels >= 2) {
+ u->channels = 2;
+
+ if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO)
+ cap->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO;
+ else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO)
+ cap->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO;
+ else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL)
+ cap->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL;
+ else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) {
+ cap->channel_mode = BT_A2DP_CHANNEL_MODE_MONO;
+ u->channels = 1;
+ } else {
+ DBG("No supported channel modes");
+ return -1;
+ }
+ }
+
+ if (cap->block_length & BT_A2DP_BLOCK_LENGTH_16)
+ cap->block_length = BT_A2DP_BLOCK_LENGTH_16;
+ else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_12)
+ cap->block_length = BT_A2DP_BLOCK_LENGTH_12;
+ else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_8)
+ cap->block_length = BT_A2DP_BLOCK_LENGTH_8;
+ else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_4)
+ cap->block_length = BT_A2DP_BLOCK_LENGTH_4;
+ else {
+ DBG("No supported block lengths");
+ return -1;
+ }
+
+ if (cap->subbands & BT_A2DP_SUBBANDS_8)
+ cap->subbands = BT_A2DP_SUBBANDS_8;
+ else if (cap->subbands & BT_A2DP_SUBBANDS_4)
+ cap->subbands = BT_A2DP_SUBBANDS_4;
+ else {
+ DBG("No supported subbands");
+ return -1;
+ }
+
+ if (cap->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS)
+ cap->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS;
+ else if (cap->allocation_method & BT_A2DP_ALLOCATION_SNR)
+ cap->allocation_method = BT_A2DP_ALLOCATION_SNR;
+
+ cap->min_bitpool = (uint8_t) MAX(MIN_BITPOOL, cap->min_bitpool);
+ cap->max_bitpool = (uint8_t) MIN(
+ a2dp_default_bitpool(cap->frequency, cap->channel_mode),
+ cap->max_bitpool);
+
+ return 0;
+}
+
+static void setup_sbc(struct a2dp_info *a2dp)
+{
+ sbc_capabilities_t *active_capabilities;
+
+ assert(a2dp);
+
+ active_capabilities = &a2dp->sbc_capabilities;
+
+ if (a2dp->sbc_initialized)
+ sbc_reinit(&a2dp->sbc, 0);
+ else
+ sbc_init(&a2dp->sbc, 0);
+ a2dp->sbc_initialized = TRUE;
+
+ switch (active_capabilities->frequency) {
+ case BT_SBC_SAMPLING_FREQ_16000:
+ a2dp->sbc.frequency = SBC_FREQ_16000;
+ break;
+ case BT_SBC_SAMPLING_FREQ_32000:
+ a2dp->sbc.frequency = SBC_FREQ_32000;
+ break;
+ case BT_SBC_SAMPLING_FREQ_44100:
+ a2dp->sbc.frequency = SBC_FREQ_44100;
+ break;
+ case BT_SBC_SAMPLING_FREQ_48000:
+ a2dp->sbc.frequency = SBC_FREQ_48000;
+ break;
+ default:
+ assert(0);
+ }
+
+ switch (active_capabilities->channel_mode) {
+ case BT_A2DP_CHANNEL_MODE_MONO:
+ a2dp->sbc.mode = SBC_MODE_MONO;
+ break;
+ case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL:
+ a2dp->sbc.mode = SBC_MODE_DUAL_CHANNEL;
+ break;
+ case BT_A2DP_CHANNEL_MODE_STEREO:
+ a2dp->sbc.mode = SBC_MODE_STEREO;
+ break;
+ case BT_A2DP_CHANNEL_MODE_JOINT_STEREO:
+ a2dp->sbc.mode = SBC_MODE_JOINT_STEREO;
+ break;
+ default:
+ assert(0);
+ }
+
+ switch (active_capabilities->allocation_method) {
+ case BT_A2DP_ALLOCATION_SNR:
+ a2dp->sbc.allocation = SBC_AM_SNR;
+ break;
+ case BT_A2DP_ALLOCATION_LOUDNESS:
+ a2dp->sbc.allocation = SBC_AM_LOUDNESS;
+ break;
+ default:
+ assert(0);
+ }
+
+ switch (active_capabilities->subbands) {
+ case BT_A2DP_SUBBANDS_4:
+ a2dp->sbc.subbands = SBC_SB_4;
+ break;
+ case BT_A2DP_SUBBANDS_8:
+ a2dp->sbc.subbands = SBC_SB_8;
+ break;
+ default:
+ assert(0);
+ }
+
+ switch (active_capabilities->block_length) {
+ case BT_A2DP_BLOCK_LENGTH_4:
+ a2dp->sbc.blocks = SBC_BLK_4;
+ break;
+ case BT_A2DP_BLOCK_LENGTH_8:
+ a2dp->sbc.blocks = SBC_BLK_8;
+ break;
+ case BT_A2DP_BLOCK_LENGTH_12:
+ a2dp->sbc.blocks = SBC_BLK_12;
+ break;
+ case BT_A2DP_BLOCK_LENGTH_16:
+ a2dp->sbc.blocks = SBC_BLK_16;
+ break;
+ default:
+ assert(0);
+ }
+
+ a2dp->sbc.bitpool = active_capabilities->max_bitpool;
+ a2dp->codesize = (uint16_t) sbc_get_codesize(&a2dp->sbc);
+}
+
+static int bt_open(struct userdata *u)
+{
+ union {
+ struct bt_open_req open_req;
+ struct bt_open_rsp open_rsp;
+ bt_audio_error_t error;
+ uint8_t buf[BT_SUGGESTED_BUFFER_SIZE];
+ } msg;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.open_req.h.type = BT_REQUEST;
+ msg.open_req.h.name = BT_OPEN;
+ msg.open_req.h.length = sizeof(msg.open_req);
+
+ strncpy(msg.open_req.destination, u->address,
+ sizeof(msg.open_req.destination));
+ msg.open_req.seid = u->transport == BT_CAPABILITIES_TRANSPORT_A2DP ?
+ u->a2dp.sbc_capabilities.capability.seid :
+ BT_A2DP_SEID_RANGE + 1;
+ msg.open_req.lock = u->transport == BT_CAPABILITIES_TRANSPORT_A2DP ?
+ BT_WRITE_LOCK : BT_READ_LOCK | BT_WRITE_LOCK;
+
+ if (service_send(u, &msg.open_req.h) < 0)
+ return -1;
+
+ msg.open_rsp.h.length = sizeof(msg.open_rsp);
+ if (service_expect(u, &msg.open_rsp.h, BT_OPEN) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int set_conf(struct userdata *u)
+{
+ union {
+ struct bt_set_configuration_req setconf_req;
+ struct bt_set_configuration_rsp setconf_rsp;
+ bt_audio_error_t error;
+ uint8_t buf[BT_SUGGESTED_BUFFER_SIZE];
+ } msg;
+
+ if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
+ if (setup_a2dp(u) < 0)
+ return -1;
+ }
+
+ memset(&msg, 0, sizeof(msg));
+ msg.setconf_req.h.type = BT_REQUEST;
+ msg.setconf_req.h.name = BT_SET_CONFIGURATION;
+ msg.setconf_req.h.length = sizeof(msg.setconf_req);
+
+ if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
+ memcpy(&msg.setconf_req.codec, &u->a2dp.sbc_capabilities,
+ sizeof(u->a2dp.sbc_capabilities));
+ msg.setconf_req.h.length += msg.setconf_req.codec.length -
+ sizeof(msg.setconf_req.codec);
+ } else {
+ msg.setconf_req.codec.transport = BT_CAPABILITIES_TRANSPORT_SCO;
+ msg.setconf_req.codec.seid = BT_A2DP_SEID_RANGE + 1;
+ msg.setconf_req.codec.length = sizeof(pcm_capabilities_t);
+ }
+
+ if (service_send(u, &msg.setconf_req.h) < 0)
+ return -1;
+
+ msg.setconf_rsp.h.length = sizeof(msg.setconf_rsp);
+ if (service_expect(u, &msg.setconf_rsp.h, BT_SET_CONFIGURATION) < 0)
+ return -1;
+
+ u->link_mtu = msg.setconf_rsp.link_mtu;
+
+ /* setup SBC encoder now we agree on parameters */
+ if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
+ setup_sbc(&u->a2dp);
+ u->block_size = u->a2dp.codesize;
+ DBG("SBC parameters:\n\tallocation=%u\n"
+ "\tsubbands=%u\n\tblocks=%u\n\tbitpool=%u\n",
+ u->a2dp.sbc.allocation, u->a2dp.sbc.subbands,
+ u->a2dp.sbc.blocks, u->a2dp.sbc.bitpool);
+ } else
+ u->block_size = u->link_mtu;
+
+ return 0;
+}
+
+static int setup_bt(struct userdata *u)
+{
+ assert(u);
+
+ if (get_caps(u) < 0)
+ return -1;
+
+ DBG("Got device caps");
+
+ if (bt_open(u) < 0)
+ return -1;
+
+ if (set_conf(u) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int init_profile(struct userdata *u)
+{
+ assert(u);
+
+ return setup_bt(u);
+}
+
+static void shutdown_bt(struct userdata *u)
+{
+ assert(u);
+
+ if (u->stream_fd != -1) {
+ stop_stream(u);
+ DBG("close(stream_fd)");
+ close(u->stream_fd);
+ u->stream_fd = -1;
+ }
+
+ if (u->service_fd != -1) {
+ DBG("bt_audio_service_close");
+ bt_audio_service_close(u->service_fd);
+ u->service_fd = -1;
+ }
+}
+
+static void make_fd_nonblock(int fd)
+{
+ int v;
+
+ assert(fd >= 0);
+ assert((v = fcntl(fd, F_GETFL)) >= 0);
+
+ if (!(v & O_NONBLOCK))
+ assert(fcntl(fd, F_SETFL, v|O_NONBLOCK) >= 0);
+}
+
+static void make_socket_low_delay(int fd)
+{
+/* FIXME: is this widely supported? */
+#ifdef SO_PRIORITY
+ int priority;
+ assert(fd >= 0);
+
+ priority = 6;
+ if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, (void*)&priority,
+ sizeof(priority)) < 0)
+ ERR("SO_PRIORITY failed: %s", strerror(errno));
+#endif
+}
+
+static int read_stream(struct userdata *u)
+{
+ int ret = 0;
+ ssize_t l;
+ char *buf;
+
+ assert(u);
+ assert(u->stream_fd >= 0);
+
+ buf = alloca(u->link_mtu);
+
+ for (;;) {
+ l = read(u->stream_fd, buf, u->link_mtu);
+ if (u->debug_stream_read)
+ DBG("read from socket: %lli bytes", (long long) l);
+ if (l <= 0) {
+ if (l < 0 && errno == EINTR)
+ continue;
+ else {
+ ERR("Failed to read date from stream_fd: %s",
+ ret < 0 ? strerror(errno) : "EOF");
+ return -1;
+ }
+ } else {
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/* It's what PulseAudio is doing, not sure it's necessary for this
+ * test */
+static ssize_t pa_write(int fd, const void *buf, size_t count)
+{
+ ssize_t r;
+
+ if ((r = send(fd, buf, count, MSG_NOSIGNAL)) >= 0)
+ return r;
+
+ if (errno != ENOTSOCK)
+ return r;
+
+ return write(fd, buf, count);
+}
+
+static int write_stream(struct userdata *u)
+{
+ int ret = 0;
+ ssize_t l;
+ char *buf;
+
+ assert(u);
+ assert(u->stream_fd >= 0);
+ buf = alloca(u->link_mtu);
+
+ for (;;) {
+ l = pa_write(u->stream_fd, buf, u->link_mtu);
+ if (u->debug_stream_write)
+ DBG("written to socket: %lli bytes", (long long) l);
+ assert(l != 0);
+ if (l < 0) {
+ if (errno == EINTR)
+ continue;
+ else {
+ ERR("Failed to write data: %s", strerror(errno));
+ ret = -1;
+ break;
+ }
+ } else {
+ assert((size_t)l <= u->link_mtu);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static gboolean stream_cb(GIOChannel *gin, GIOCondition condition, gpointer data)
+{
+ struct userdata *u;
+
+ assert(u = data);
+
+ if (condition & G_IO_IN) {
+ if (read_stream(u) < 0)
+ goto fail;
+ } else if (condition & G_IO_OUT) {
+ if (write_stream(u) < 0)
+ goto fail;
+ } else {
+ DBG("Got %d", condition);
+ g_main_loop_quit(main_loop);
+ return FALSE;
+ }
+
+ return TRUE;
+
+fail:
+ stop_stream(u);
+ return FALSE;
+}
+
+static int start_stream(struct userdata *u)
+{
+ union {
+ bt_audio_msg_header_t rsp;
+ struct bt_start_stream_req start_req;
+ struct bt_start_stream_rsp start_rsp;
+ struct bt_new_stream_ind streamfd_ind;
+ bt_audio_error_t error;
+ uint8_t buf[BT_SUGGESTED_BUFFER_SIZE];
+ } msg;
+
+ assert(u);
+
+ if (u->stream_fd >= 0)
+ return 0;
+ if (u->stream_watch != 0) {
+ g_source_remove(u->stream_watch);
+ u->stream_watch = 0;
+ }
+ if (u->stream_channel != 0) {
+ g_io_channel_unref(u->stream_channel);
+ u->stream_channel = NULL;
+ }
+
+ memset(msg.buf, 0, BT_SUGGESTED_BUFFER_SIZE);
+ msg.start_req.h.type = BT_REQUEST;
+ msg.start_req.h.name = BT_START_STREAM;
+ msg.start_req.h.length = sizeof(msg.start_req);
+
+ if (service_send(u, &msg.start_req.h) < 0)
+ return -1;
+
+ msg.rsp.length = sizeof(msg.start_rsp);
+ if (service_expect(u, &msg.rsp, BT_START_STREAM) < 0)
+ return -1;
+
+ msg.rsp.length = sizeof(msg.streamfd_ind);
+ if (service_expect(u, &msg.rsp, BT_NEW_STREAM) < 0)
+ return -1;
+
+ if ((u->stream_fd = bt_audio_service_get_data_fd(u->service_fd)) < 0) {
+ DBG("Failed to get stream fd from audio service.");
+ return -1;
+ }
+
+ make_fd_nonblock(u->stream_fd);
+ make_socket_low_delay(u->stream_fd);
+
+ assert(u->stream_channel = g_io_channel_unix_new(u->stream_fd));
+
+ u->stream_watch = g_io_add_watch(u->stream_channel,
+ G_IO_IN|G_IO_OUT|G_IO_ERR|G_IO_HUP|G_IO_NVAL,
+ stream_cb, u);
+
+ return 0;
+}
+
+static int stop_stream(struct userdata *u)
+{
+ union {
+ bt_audio_msg_header_t rsp;
+ struct bt_stop_stream_req stop_req;
+ struct bt_stop_stream_rsp stop_rsp;
+ bt_audio_error_t error;
+ uint8_t buf[BT_SUGGESTED_BUFFER_SIZE];
+ } msg;
+ int r = 0;
+
+ if (u->stream_fd < 0)
+ return 0;
+
+ assert(u);
+ assert(u->stream_channel);
+
+ g_source_remove(u->stream_watch);
+ u->stream_watch = 0;
+ g_io_channel_unref(u->stream_channel);
+ u->stream_channel = NULL;
+
+ memset(msg.buf, 0, BT_SUGGESTED_BUFFER_SIZE);
+ msg.stop_req.h.type = BT_REQUEST;
+ msg.stop_req.h.name = BT_STOP_STREAM;
+ msg.stop_req.h.length = sizeof(msg.stop_req);
+
+ if (service_send(u, &msg.stop_req.h) < 0) {
+ r = -1;
+ goto done;
+ }
+
+ msg.rsp.length = sizeof(msg.stop_rsp);
+ if (service_expect(u, &msg.rsp, BT_STOP_STREAM) < 0)
+ r = -1;
+
+done:
+ close(u->stream_fd);
+ u->stream_fd = -1;
+
+ return r;
+}
+
+static gboolean sleep_cb(gpointer data)
+{
+ struct userdata *u;
+
+ assert(u = data);
+
+ u->gin_watch = g_io_add_watch(u->gin,
+ G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL, input_cb, data);
+
+ printf(">>> ");
+ fflush(stdout);
+
+ return FALSE;
+}
+
+static gboolean input_cb(GIOChannel *gin, GIOCondition condition, gpointer data)
+{
+ char *line, *tmp;
+ gsize term_pos;
+ GError *error = NULL;
+ struct userdata *u;
+ int success;
+
+ assert(u = data);
+ if (!(condition & G_IO_IN)) {
+ DBG("Got %d", condition);
+ g_main_loop_quit(main_loop);
+ return FALSE;
+ }
+
+ if (g_io_channel_read_line(gin, &line, NULL, &term_pos, &error) !=
+ G_IO_STATUS_NORMAL)
+ return FALSE;
+
+ line[term_pos] = '\0';
+ g_strstrip(line);
+ if ((tmp = strchr(line, '#')))
+ *tmp = '\0';
+ success = FALSE;
+
+#define IF_CMD(cmd) \
+ if (!success && (success = (strncmp(line, #cmd, strlen(#cmd)) == 0)))
+
+ IF_CMD(quit) {
+ g_main_loop_quit(main_loop);
+ return FALSE;
+ }
+
+ IF_CMD(sleep) {
+ unsigned int seconds;
+ if (sscanf(line, "%*s %d", &seconds) != 1)
+ DBG("sleep SECONDS");
+ else {
+ g_source_remove(u->gin_watch);
+ g_timeout_add_seconds(seconds, sleep_cb, u);
+ return FALSE;
+ }
+ }
+
+ IF_CMD(debug) {
+ char *what = NULL;
+ int enable;
+
+ if (sscanf(line, "%*s %as %d", &what, &enable) != 1)
+ DBG("debug [stream_read|stream_write] [0|1]");
+ if (strncmp(what, "stream_read", 12) == 0) {
+ u->debug_stream_read = enable;
+ } else if (strncmp(what, "stream_write", 13) == 0) {
+ u->debug_stream_write = enable;
+ } else {
+ DBG("debug [stream_read|stream_write] [0|1]");
+ }
+ }
+
+ IF_CMD(init_bt) {
+ DBG("%d", init_bt(u));
+ }
+
+ IF_CMD(init_profile) {
+ DBG("%d", init_profile(u));
+ }
+
+ IF_CMD(start_stream) {
+ DBG("%d", start_stream(u));
+ }
+
+ IF_CMD(stop_stream) {
+ DBG("%d", stop_stream(u));
+ }
+
+ IF_CMD(shutdown_bt) {
+ shutdown_bt(u);
+ }
+
+ IF_CMD(rate) {
+ if (sscanf(line, "%*s %d", &u->rate) != 1)
+ DBG("set with rate RATE");
+ DBG("rate %d", u->rate);
+ }
+
+ IF_CMD(bdaddr) {
+ char *address;
+
+ if (sscanf(line, "%*s %as", &address) != 1)
+ DBG("set with bdaddr BDADDR");
+
+ free(u->address);
+
+ u->address = address;
+ DBG("bdaddr %s", u->address);
+ }
+
+ IF_CMD(profile) {
+ char *profile = NULL;
+
+ if (sscanf(line, "%*s %as", &profile) != 1)
+ DBG("set with profile [hsp|a2dp]");
+ if (strncmp(profile, "hsp", 4) == 0) {
+ u->transport = BT_CAPABILITIES_TRANSPORT_SCO;
+ } else if (strncmp(profile, "a2dp", 5) == 0) {
+ u->transport = BT_CAPABILITIES_TRANSPORT_A2DP;
+ } else {
+ DBG("set with profile [hsp|a2dp]");
+ }
+
+ free(profile);
+ DBG("profile %s", u->transport == BT_CAPABILITIES_TRANSPORT_SCO ?
+ "hsp" : "a2dp");
+ }
+
+ if (!success && strlen(line) != 0) {
+ DBG("%s, unknown command", line);
+ }
+
+ printf(">>> ");
+ fflush(stdout);
+ return TRUE;
+}
+
+
+static void show_usage(char* prgname)
+{
+ printf("%s: ipctest [--interactive] BDADDR\n", basename(prgname));
+}
+
+static void sig_term(int sig)
+{
+ g_main_loop_quit(main_loop);
+}
+
+int main(int argc, char *argv[])
+{
+ if (argc < 2) {
+ show_usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ assert(main_loop = g_main_loop_new(NULL, FALSE));
+
+ if (strncmp("--interactive", argv[1], 14) == 0) {
+ if (argc < 3) {
+ show_usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ data.address = strdup(argv[2]);
+
+ signal(SIGTERM, sig_term);
+ signal(SIGINT, sig_term);
+
+ assert(data.gin = g_io_channel_unix_new(fileno(stdin)));
+
+ data.gin_watch = g_io_add_watch(data.gin,
+ G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL, input_cb, &data);
+
+ printf(">>> ");
+ fflush(stdout);
+
+ g_main_loop_run(main_loop);
+
+ } else {
+ data.address = strdup(argv[1]);
+
+ assert(init_bt(&data) == 0);
+
+ assert(init_profile(&data) == 0);
+
+ assert(start_stream(&data) == 0);
+
+ g_main_loop_run(main_loop);
+
+ assert(stop_stream(&data) == 0);
+
+ shutdown_bt(&data);
+ }
+
+ g_main_loop_unref(main_loop);
+
+ printf("\nExiting\n");
+
+ exit(EXIT_SUCCESS);
+
+ return 0;
+}
diff --git a/test/l2test.c b/test/l2test.c
new file mode 100644
index 0000000..3ac332f
--- /dev/null
+++ b/test/l2test.c
@@ -0,0 +1,1379 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2000-2001 Qualcomm Incorporated
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 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,
+ LSENDRECV,
+ CSENDRECV,
+ INFOREQ,
+ PAIRING,
+};
+
+static unsigned char *buf;
+
+/* Default mtu */
+static int imtu = 672;
+static int omtu = 0;
+
+/* Default FCS option */
+static int fcs = 0x01;
+
+/* Default Transmission Window */
+static int txwin_size = 63;
+
+/* Default Max Transmission */
+static int max_transmit = 3;
+
+/* Default data size */
+static long data_size = -1;
+static long buffer_size = 2048;
+
+/* Default addr and psm and cid */
+static bdaddr_t bdaddr;
+static unsigned short psm = 0x1011;
+static unsigned short cid = 0;
+
+/* 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 int defer_setup = 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;
+ opts.mode = rfcmode;
+
+ opts.fcs = fcs;
+ opts.txwin_size = txwin_size;
+ opts.max_tx = max_transmit;
+
+ 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);
+ if (cid)
+ addr.l2_cid = htobs(cid);
+ else if (psm)
+ addr.l2_psm = htobs(psm);
+ else
+ goto error;
+
+ 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]);
+
+ omtu = (opts.omtu > buffer_size) ? buffer_size : opts.omtu;
+ imtu = (opts.imtu > buffer_size) ? buffer_size : opts.imtu;
+
+ 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 */
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ bacpy(&addr.l2_bdaddr, &bdaddr);
+ if (cid)
+ addr.l2_cid = htobs(cid);
+ else if (psm)
+ addr.l2_psm = htobs(psm);
+ else
+ goto error;
+
+ 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;
+
+ opts.fcs = fcs;
+ opts.txwin_size = txwin_size;
+ opts.max_tx = max_transmit;
+
+ 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;
+ }
+
+ /* Enable deferred setup */
+ opt = defer_setup;
+
+ if (opt && setsockopt(sk, SOL_BLUETOOTH, BT_DEFER_SETUP,
+ &opt, sizeof(opt)) < 0) {
+ syslog(LOG_ERR, "Can't enable deferred setup : %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;
+ }
+
+ psm = btohs(addr.l2_psm);
+ cid = btohs(addr.l2_cid);
+
+ 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);
+ if (!defer_setup) {
+ 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);
+ if (!defer_setup) {
+ 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]);
+
+ omtu = (opts.omtu > buffer_size) ? buffer_size : opts.omtu;
+ imtu = (opts.imtu > buffer_size) ? buffer_size : opts.imtu;
+
+#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;
+ }
+ }
+
+ /* Handle deferred setup */
+ if (defer_setup) {
+ syslog(LOG_INFO, "Waiting for %d seconds",
+ abs(defer_setup) - 1);
+ sleep(abs(defer_setup) - 1);
+
+ if (defer_setup < 0) {
+ 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;
+
+ if (data_size < 0)
+ data_size = imtu;
+
+ if (defer_setup) {
+ len = read(sk, buf, sizeof(buf));
+ if (len < 0)
+ syslog(LOG_ERR, "Initial read error: %s (%d)",
+ strerror(errno), errno);
+ else
+ syslog(LOG_INFO, "Initial bytes %d", 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, len;
+
+ if (data_size < 0)
+ data_size = imtu;
+
+ if (defer_setup) {
+ len = read(sk, buf, sizeof(buf));
+ if (len < 0)
+ syslog(LOG_ERR, "Initial read error: %s (%d)",
+ strerror(errno), errno);
+ else
+ syslog(LOG_INFO, "Initial bytes %d", len);
+ }
+
+ 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;
+
+ 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, buflen, size, sent;
+
+ syslog(LOG_INFO, "Sending ...");
+
+ if (data_size < 0)
+ data_size = omtu;
+
+ if (filename) {
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ syslog(LOG_ERR, "Open failed: %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ sent = 0;
+ size = read(fd, buf, data_size);
+ while (size > 0) {
+ buflen = (size > omtu) ? omtu : size;
+
+ len = send(sk, buf + sent, buflen, 0);
+
+ sent += len;
+ size -= len;
+ }
+ 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++;
+
+ sent = 0;
+ size = data_size;
+ while (size > 0) {
+ buflen = (size > omtu) ? omtu : size;
+
+ len = send(sk, buf, buflen, 0);
+ if (len < 0 || len != buflen) {
+ syslog(LOG_ERR, "Send failed: %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ sent += len;
+ size -= len;
+ }
+
+ 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 send_and_recv_mode(int sk)
+{
+ int flags;
+
+ if ((flags = fcntl(sk, F_GETFL, 0)) < 0)
+ flags = 0;
+ fcntl(sk, F_SETFL, flags | O_NONBLOCK);
+
+ /* fork for duplex channel */
+ if (fork())
+ send_mode(sk);
+ else
+ recv_mode(sk);
+ return;
+}
+
+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 channels, mask = 0x0000;
+ 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 = 141;
+ 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:
+ memcpy(&mtu, rsp->data, sizeof(mtu));
+ printf("Connectionless MTU size is %d\n", btohs(mtu));
+ break;
+ case 0x0001:
+ printf("Connectionless MTU is not supported\n");
+ break;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ cmd->code = L2CAP_INFO_REQ;
+ cmd->ident = 142;
+ 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:
+ memcpy(&mask, rsp->data, sizeof(mask));
+ printf("Extended feature mask is 0x%04x\n", btohl(mask));
+ if (mask & 0x01)
+ printf(" Flow control mode\n");
+ if (mask & 0x02)
+ printf(" Retransmission mode\n");
+ if (mask & 0x04)
+ printf(" Bi-directional QoS\n");
+ if (mask & 0x08)
+ printf(" Enhanced Retransmission mode\n");
+ if (mask & 0x10)
+ printf(" Streaming mode\n");
+ if (mask & 0x20)
+ printf(" FCS Option\n");
+ if (mask & 0x40)
+ printf(" Extended Flow Specification\n");
+ if (mask & 0x80)
+ printf(" Fixed Channels\n");
+ if (mask & 0x0100)
+ printf(" Extended Window Size\n");
+ if (mask & 0x0200)
+ printf(" Unicast Connectionless Data Reception\n");
+ break;
+ case 0x0001:
+ printf("Extended feature mask is not supported\n");
+ break;
+ }
+
+ if (!(mask & 0x80))
+ goto failed;
+
+ memset(buf, 0, sizeof(buf));
+ cmd->code = L2CAP_INFO_REQ;
+ cmd->ident = 143;
+ cmd->len = htobs(2);
+ req->type = htobs(0x0003);
+
+ 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 + 8, 0);
+ if (err < 0) {
+ perror("Can't receive info response");
+ goto failed;
+ }
+
+ switch (btohs(rsp->result)) {
+ case 0x0000:
+ memcpy(&channels, rsp->data, sizeof(channels));
+ printf("Fixed channels list is 0x%04x\n", btohl(channels));
+ break;
+ case 0x0001:
+ printf("Fixed channels list is not supported\n");
+ break;
+ }
+
+failed:
+ close(sk);
+}
+
+static void do_pairing(char *svr)
+{
+ struct sockaddr_l2 addr;
+ int sk, opt;
+
+ 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;
+ }
+
+ if (secure)
+ opt = L2CAP_LM_SECURE;
+ else
+ opt = L2CAP_LM_ENCRYPT;
+
+ if (setsockopt(sk, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0) {
+ perror("Can't set link mode");
+ 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;
+ }
+
+ printf("Pairing successful\n");
+
+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-t listen, then send and receive at the same time\n"
+ "\t-q connect, then send and receive at the same time\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-p trigger dedicated bonding\n"
+ "\t-z information request\n");
+
+ printf("Options:\n"
+ "\t[-b bytes] [-i device] [-P psm] [-J cid]\n"
+ "\t[-I imtu] [-O omtu]\n"
+ "\t[-L seconds] enable SO_LINGER\n"
+ "\t[-W seconds] enable deferred setup\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[-F fcs] use CRC16 check (default = 1)\n"
+ "\t[-Q num] Max Transmit value (default = 3)\n"
+ "\t[-Z size] Transmission Window size (default = 63)\n"
+ "\t[-R] reliable mode\n"
+ "\t[-G] use connectionless channel (datagram)\n"
+ "\t[-U] use sock stream\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,"rdscuwmntqxyzpb:i:P:I:O:J:B:N:L:W:C:D:X:F:Q:Z:RUGAESMT")) != 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 't':
+ mode = LSENDRECV;
+ break;
+
+ case 'q':
+ mode = CSENDRECV;
+ need_addr = 1;
+ break;
+
+ case 'x':
+ mode = LSENDDUMP;
+ break;
+
+ case 'y':
+ mode = SENDDUMP;
+ break;
+
+ case 'z':
+ mode = INFOREQ;
+ need_addr = 1;
+ break;
+
+ case 'p':
+ mode = PAIRING;
+ 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 'W':
+ defer_setup = 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':
+ if (strcasecmp(optarg, "ertm") == 0)
+ rfcmode = L2CAP_MODE_ERTM;
+ else
+ rfcmode = atoi(optarg);
+ break;
+
+ case 'F':
+ fcs = 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 'U':
+ socktype = SOCK_STREAM;
+ break;
+
+ case 'T':
+ timestamp = 1;
+ break;
+
+ case 'Q':
+ max_transmit = atoi(optarg);
+ break;
+
+ case 'Z':
+ txwin_size = atoi(optarg);
+ break;
+
+ case 'J':
+ cid = atoi(optarg);
+ break;
+
+ default:
+ usage();
+ exit(1);
+ }
+ }
+
+ if (need_addr && !(argc - optind)) {
+ usage();
+ exit(1);
+ }
+
+ if (data_size < 0)
+ buffer_size = (omtu > imtu) ? omtu : imtu;
+ else
+ buffer_size = data_size;
+
+ if (!(buf = malloc(buffer_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 LSENDRECV:
+ do_listen(send_and_recv_mode);
+ break;
+
+ case CSENDRECV:
+ sk = do_connect(argv[optind]);
+ if (sk < 0)
+ exit(1);
+
+ send_and_recv_mode(sk);
+ break;
+
+ case INFOREQ:
+ info_request(argv[optind]);
+ exit(0);
+
+ case PAIRING:
+ do_pairing(argv[optind]);
+ exit(0);
+ }
+
+ syslog(LOG_INFO, "Exit");
+
+ closelog();
+
+ return 0;
+}
diff --git a/test/list-devices b/test/list-devices
new file mode 100755
index 0000000..9120714
--- /dev/null
+++ b/test/list-devices
@@ -0,0 +1,87 @@
+#!/usr/bin/python
+
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+ "org.bluez.Manager")
+
+def extract_objects(object_list):
+ list = ""
+ for object in object_list:
+ val = str(object)
+ list = list + val[val.rfind("/") + 1:] + " "
+ return list
+
+def extract_uuids(uuid_list):
+ list = ""
+ for uuid in uuid_list:
+ if (uuid.endswith("-0000-1000-8000-00805f9b34fb")):
+ if (uuid.startswith("0000")):
+ val = "0x" + uuid[4:8]
+ else:
+ val = "0x" + uuid[0:8]
+ else:
+ val = str(uuid)
+ list = list + val + " "
+ return list
+
+adapter_list = manager.ListAdapters()
+
+for i in adapter_list:
+ adapter = dbus.Interface(bus.get_object("org.bluez", i),
+ "org.bluez.Adapter")
+ print "[ " + i + " ]"
+
+ properties = adapter.GetProperties()
+ for key in properties.keys():
+ value = properties[key]
+ if (key == "Devices"):
+ list = extract_objects(value)
+ print " %s = %s" % (key, list)
+ elif (key == "UUIDs"):
+ list = extract_uuids(value)
+ print " %s = %s" % (key, list)
+ else:
+ print " %s = %s" % (key, value)
+
+ try:
+ device_list = properties["Devices"]
+ except:
+ device_list = []
+
+ for n in device_list:
+ device = dbus.Interface(bus.get_object("org.bluez", n),
+ "org.bluez.Device")
+ print " [ " + n + " ]"
+
+ properties = device.GetProperties()
+ for key in properties.keys():
+ value = properties[key]
+ if (key == "Nodes"):
+ list = extract_objects(value)
+ print " %s = %s" % (key, list)
+ elif (key == "UUIDs"):
+ list = extract_uuids(value)
+ print " %s = %s" % (key, list)
+ elif (key == "Class"):
+ print " %s = 0x%06x" % (key, value)
+ else:
+ print " %s = %s" % (key, value)
+
+ try:
+ node_list = properties["Nodes"]
+ except:
+ node_list = []
+
+ for x in node_list:
+ node = dbus.Interface(bus.get_object("org.bluez", x),
+ "org.bluez.Node")
+ print " [ " + x + " ]"
+
+ properties = node.GetProperties()
+ for key in properties.keys():
+ print " %s = %s" % (key, properties[key])
+
+ print
diff --git a/test/lmptest.c b/test/lmptest.c
new file mode 100644
index 0000000..549ae12
--- /dev/null
+++ b/test/lmptest.c
@@ -0,0 +1,175 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2005-2010 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/monitor-bluetooth b/test/monitor-bluetooth
new file mode 100755
index 0000000..a5e5300
--- /dev/null
+++ b/test/monitor-bluetooth
@@ -0,0 +1,56 @@
+#!/usr/bin/python
+
+import gobject
+
+import dbus
+import dbus.mainloop.glib
+
+def property_changed(name, value, path, interface):
+ iface = interface[interface.rfind(".") + 1:]
+ val = str(value)
+ print "{%s.PropertyChanged} [%s] %s = %s" % (iface, path, name, val)
+
+def object_signal(value, path, interface, member):
+ iface = interface[interface.rfind(".") + 1:]
+ val = str(value)
+ print "{%s.%s} [%s] Path = %s" % (iface, member, path, val)
+
+if __name__ == '__main__':
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+ bus = dbus.SystemBus()
+
+ bus.add_signal_receiver(property_changed, bus_name="org.bluez",
+ signal_name = "PropertyChanged",
+ path_keyword="path",
+ interface_keyword="interface")
+
+ bus.add_signal_receiver(object_signal, bus_name="org.bluez",
+ signal_name = "AdapterAdded",
+ path_keyword="path",
+ member_keyword="member",
+ interface_keyword="interface")
+ bus.add_signal_receiver(object_signal, bus_name="org.bluez",
+ signal_name = "AdapterRemoved",
+ path_keyword="path",
+ member_keyword="member",
+ interface_keyword="interface")
+ bus.add_signal_receiver(object_signal, bus_name="org.bluez",
+ signal_name = "DefaultAdapterChanged",
+ path_keyword="path",
+ member_keyword="member",
+ interface_keyword="interface")
+
+ bus.add_signal_receiver(object_signal, bus_name="org.bluez",
+ signal_name = "DeviceCreated",
+ path_keyword="path",
+ member_keyword="member",
+ interface_keyword="interface")
+ bus.add_signal_receiver(object_signal, bus_name="org.bluez",
+ signal_name = "DeviceRemoved",
+ path_keyword="path",
+ member_keyword="member",
+ interface_keyword="interface")
+
+ mainloop = gobject.MainLoop()
+ mainloop.run()
diff --git a/test/rctest.1 b/test/rctest.1
new file mode 100644
index 0000000..dfedbef
--- /dev/null
+++ b/test/rctest.1
@@ -0,0 +1,90 @@
+.TH RCTEST 1 "Jul 6 2009" BlueZ ""
+.SH NAME
+rctest \- RFCOMM testing
+.SH SYNOPSIS
+.B rctest
+<\fImode\fR> [\fIoptions\fR] [\fIbdaddr\fR]
+
+.SH DESCRIPTION
+.LP
+.B
+rctest
+is used to test RFCOMM communications on the BlueZ stack
+
+.SH MODES
+.TP
+.B -r
+listen and receive
+.TP
+.B -w
+listen and send
+.TP
+.B -d
+listen and dump incoming data
+.TP
+.B -s
+connect and send
+.TP
+.B -u
+connect and receive
+.TP
+.B -n
+connect and be silent
+.TP
+.B -c
+connect, disconnect, connect, ...
+.TP
+.B -m
+multiple connects
+
+.SH OPTIONS
+.TP
+.BI -b\ bytes
+send/receive \fIbytes\fR bytes
+.TP
+.BI -i\ device
+select the specified \fIdevice\fR
+.TP
+.BI -P\ channel
+select the specified \fIchannel\fR
+.TP
+.BI -U\ uuid
+select the specified \fIuuid\fR
+.TP
+.BI -L\ seconds
+enable SO_LINGER options for \fIseconds\fR
+.TP
+.BI -W\ seconds
+enable deferred setup for \fIseconds\fR
+.TP
+.BI -B\ filename
+use data packets from \fIfilename\fR
+.TP
+.BI -N\ num
+send \fInum\fR frames
+.TP
+.BI -C\ num
+send \fInum\fR frames before delay (default: 1)
+.TP
+.BI -D\ milliseconds
+delay \fImilliseconds\fR after sending \fInum\fR frames (default: 0)
+.TP
+.B -A
+request authentication
+.TP
+.B -E
+request encryption
+.TP
+.B -S
+secure connection
+.TP
+.B -M
+become master
+.TP
+.B -T
+enable timestamps
+
+.SH AUTHORS
+Written by Marcel Holtmann <marcel@holtmann.org> and Maxim Krasnyansky
+<maxk@qualcomm.com>, man page by Filippo Giunchedi <filippo@debian.org>
+.PP
diff --git a/test/rctest.c b/test/rctest.c
new file mode 100644
index 0000000..b3804f5
--- /dev/null
+++ b/test/rctest.c
@@ -0,0 +1,781 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 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>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.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 uint16_t uuid = 0x0000;
+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 int defer_setup = 0;
+
+static float tv2fl(struct timeval tv)
+{
+ return (float)tv.tv_sec + (float)(tv.tv_usec/1000000.0);
+}
+
+static uint8_t get_channel(const char *svr, uint16_t uuid)
+{
+ sdp_session_t *sdp;
+ sdp_list_t *srch, *attrs, *rsp;
+ uuid_t svclass;
+ uint16_t attr;
+ bdaddr_t dst;
+ uint8_t channel = 0;
+ int err;
+
+ str2ba(svr, &dst);
+
+ sdp = sdp_connect(&bdaddr, &dst, SDP_RETRY_IF_BUSY);
+ if (!sdp)
+ return 0;
+
+ sdp_uuid16_create(&svclass, uuid);
+ srch = sdp_list_append(NULL, &svclass);
+
+ attr = SDP_ATTR_PROTO_DESC_LIST;
+ attrs = sdp_list_append(NULL, &attr);
+
+ err = sdp_service_search_attr_req(sdp, srch,
+ SDP_ATTR_REQ_INDIVIDUAL, attrs, &rsp);
+ if (err)
+ goto done;
+
+ for (; rsp; rsp = rsp->next) {
+ sdp_record_t *rec = (sdp_record_t *) rsp->data;
+ sdp_list_t *protos;
+
+ if (!sdp_get_access_protos(rec, &protos)) {
+ channel = sdp_get_proto_port(protos, RFCOMM_UUID);
+ if (channel > 0)
+ break;
+ }
+ }
+
+done:
+ sdp_close(sdp);
+
+ return channel;
+}
+
+static int do_connect(const char *svr)
+{
+ struct sockaddr_rc addr;
+ struct rfcomm_conninfo conn;
+ socklen_t optlen;
+ int sk, opt;
+
+ if (uuid != 0x0000)
+ channel = get_channel(svr, uuid);
+
+ if (channel == 0) {
+ syslog(LOG_ERR, "Can't get channel number");
+ return -1;
+ }
+
+ /* 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 */
+ memset(&addr, 0, sizeof(addr));
+ 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;
+ }
+
+ /* Enable deferred setup */
+ opt = defer_setup;
+
+ if (opt && setsockopt(sk, SOL_BLUETOOTH, BT_DEFER_SETUP,
+ &opt, sizeof(opt)) < 0) {
+ syslog(LOG_ERR, "Can't enable deferred setup : %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;
+ }
+ }
+
+ /* Handle deferred setup */
+ if (defer_setup) {
+ syslog(LOG_INFO, "Waiting for %d seconds",
+ abs(defer_setup) - 1);
+ sleep(abs(defer_setup) - 1);
+
+ if (defer_setup < 0) {
+ 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] [-U uuid]\n"
+ "\t[-L seconds] enabled SO_LINGER option\n"
+ "\t[-W seconds] enable deferred setup\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:U:B:N:MAESL:W: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 'U':
+ if (!strcasecmp(optarg, "spp"))
+ uuid = SERIAL_PORT_SVCLASS_ID;
+ else if (!strncasecmp(optarg, "0x", 2))
+ uuid = strtoul(optarg + 2, NULL, 16);
+ else
+ uuid = 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 'W':
+ defer_setup = 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/sap-client b/test/sap-client
new file mode 100644
index 0000000..b12d455
--- /dev/null
+++ b/test/sap-client
@@ -0,0 +1,943 @@
+""" Copyright (C) 2010-2011 ST-Ericsson SA """
+
+""" Author: Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson. """
+
+""" 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 """
+
+from array import array
+from bluetooth import *
+import time
+import re
+
+class SAPParam:
+ """ SAP Parameter Class """
+
+ MaxMsgSize = 0x00
+ ConnectionStatus = 0x01
+ ResultCode = 0x02
+ DisconnectionType = 0x03
+ CommandAPDU = 0x04
+ ResponseAPDU = 0x05
+ ATR = 0x06
+ CardReaderStatus = 0x07
+ StatusChange = 0x08
+ TransportProtocol = 0x09
+ CommandAPDU7816 = 0x10
+
+ def __init__(self, name, id, value = None):
+ self.name = name
+ self.id = id
+ self.value = value
+
+ def _padding(self, buf):
+ pad = array('B')
+ while ( (len(buf) + len(pad)) % 4 ) != 0:
+ pad.append(0)
+ return pad
+
+ def _basicCheck(self, buf):
+ if len(buf) < 4 or (len(buf) % 4) != 0 or buf[1] != 0:
+ return (-1, -1)
+ if buf[0] != self.id:
+ return (-1, -1)
+ plen = buf[2] * 256 + buf[3] + 4
+ if plen > len(buf):
+ return (-1, -1)
+ pad = plen
+ while (pad % 4) != 0:
+ if buf[pad] != 0:
+ return (-1, -1)
+ pad+=1
+ return (plen, pad)
+
+ def getID(self):
+ return self.id
+
+ def getValue(self):
+ return self.value
+
+ def getContent(self):
+ return "%s(id=0x%.2X), value=%s \n" % (self.name, self.id, self.value)
+
+ def serialize(self):
+ a = array('B', '\00\00\00\00')
+ a[0] = self.id
+ a[1] = 0 # reserved
+ a[2] = 0 # length
+ a[3] = 1 # length
+ a.append(self.value)
+ a.extend(self._padding(a))
+ return a
+
+ def deserialize(self, buf):
+ p = self._basicCheck(buf)
+ if p[0] == -1:
+ return -1
+ self.id = buf[0]
+ self.value = buf[4]
+ return p[1]
+
+
+class SAPParam_MaxMsgSize(SAPParam):
+ """MaxMsgSize Param """
+
+ def __init__(self, value = None):
+ SAPParam.__init__(self,"MaxMsgSize", SAPParam.MaxMsgSize, value)
+ self.__validate()
+
+ def __validate(self):
+ if self.value > 0xFFFF:
+ self.value = 0xFFFF
+
+ def serialize(self):
+ a = array('B', '\00\00\00\00')
+ a[0] = self.id
+ a[3] = 2
+ a.append(self.value / 256)
+ a.append(self.value % 256)
+ a.extend(self._padding(a))
+ return a
+
+ def deserialize(self, buf):
+ p = self._basicCheck(buf)
+ if p[0] == -1 :
+ return -1
+ self.value = buf[4] * 256 + buf[5]
+ return p[1]
+
+class SAPParam_CommandAPDU(SAPParam):
+ def __init__(self, value = None):
+ if value is None:
+ SAPParam.__init__(self, "CommandAPDU", SAPParam.CommandAPDU, array('B'))
+ else:
+ SAPParam.__init__(self, "CommandAPDU", SAPParam.CommandAPDU, array('B', value))
+
+ def serialize(self):
+ a = array('B', '\00\00\00\00')
+ a[0] = self.id
+ plen = len(self.value)
+ a[2] = plen / 256
+ a[3] = plen % 256
+ a.extend(self.value)
+ a.extend(self._padding(a))
+ return a
+
+ def deserialize(self, buf):
+ p = self._basicCheck(buf)
+ if p[0] == -1:
+ return -1
+ self.value = buf[4:p[0]]
+ return p[1]
+
+class SAPParam_ResponseAPDU(SAPParam_CommandAPDU):
+ """ResponseAPDU Param """
+
+ def __init__(self, value = None):
+ if value is None:
+ SAPParam.__init__(self, "ResponseAPDU", SAPParam.ResponseAPDU, array('B'))
+ else:
+ SAPParam.__init__(self, "ResponseAPDU", SAPParam.ResponseAPDU, array('B', value))
+
+class SAPParam_ATR(SAPParam_CommandAPDU):
+ """ATR Param """
+
+ def __init__(self, value = None):
+ if value is None:
+ SAPParam.__init__(self, "ATR", SAPParam.ATR, array('B'))
+ else:
+ SAPParam.__init__(self, "ATR", SAPParam.ATR, array('B', value))
+
+class SAPParam_CommandAPDU7816(SAPParam_CommandAPDU):
+ """Command APDU7816 Param."""
+
+ def __init__(self, value = None):
+ if value is None:
+ SAPParam.__init__(self, "CommandAPDU7816", SAPParam.CommandAPDU7816, array('B'))
+ else:
+ SAPParam.__init__(self, "CommandAPDU7816", SAPParam.CommandAPDU7816, array('B', value))
+
+
+class SAPParam_ConnectionStatus(SAPParam):
+ """Connection status Param."""
+
+ def __init__(self, value = None):
+ SAPParam.__init__(self,"ConnectionStatus", SAPParam.ConnectionStatus, value)
+ self.__validate()
+
+ def __validate(self):
+ if self.value is not None and self.value not in (0x00, 0x01, 0x02, 0x03, 0x04):
+ print "Warning. ConnectionStatus value in reserved range (0x%x)" % self.value
+
+ def deserialize(self, buf):
+ ret = SAPParam.deserialize(self, buf)
+ if ret == -1:
+ return -1
+ self.__validate()
+ return ret
+
+class SAPParam_ResultCode(SAPParam):
+ """ Result Code Param """
+
+ def __init__(self, value = None):
+ SAPParam.__init__(self,"ResultCode", SAPParam.ResultCode, value)
+ self.__validate()
+
+ def __validate(self):
+ if self.value is not None and self.value not in (0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07):
+ print "Warning. ResultCode value in reserved range (0x%x)" % self.value
+
+ def deserialize(self, buf):
+ ret = SAPParam.deserialize(self, buf)
+ if ret == -1:
+ return -1
+ self.__validate()
+ return ret
+
+class SAPParam_DisconnectionType(SAPParam):
+ """Disconnection Type Param."""
+
+ def __init__(self, value = None):
+ SAPParam.__init__(self,"DisconnectionType", SAPParam.DisconnectionType, value)
+ self.__validate()
+
+ def __validate(self):
+ if self.value is not None and self.value not in (0x00, 0x01):
+ print "Warning. DisconnectionType value in reserved range (0x%x)" % self.value
+
+ def deserialize(self, buf):
+ ret = SAPParam.deserialize(self, buf)
+ if ret == -1:
+ return -1
+ self.__validate()
+ return ret
+
+class SAPParam_CardReaderStatus(SAPParam_CommandAPDU):
+ """Card reader Status Param."""
+
+ def __init__(self, value = None):
+ if value is None:
+ SAPParam.__init__(self, "CardReaderStatus", SAPParam.CardReaderStatus, array('B'))
+ else:
+ SAPParam.__init__(self, "CardReaderStatus", SAPParam.CardReaderStatus, array('B', value))
+
+class SAPParam_StatusChange(SAPParam):
+ """Status Change Param """
+
+ def __init__(self, value = None):
+ SAPParam.__init__(self,"StatusChange", SAPParam.StatusChange, value)
+
+ def __validate(self):
+ if self.value is not None and self.value not in (0x00, 0x01, 0x02, 0x03, 0x04, 0x05):
+ print "Warning. StatusChange value in reserved range (0x%x)" % self.value
+
+ def deserialize(self, buf):
+ ret = SAPParam.deserialize(self, buf)
+ if ret == -1:
+ return -1
+ self.__validate()
+ return ret
+
+class SAPParam_TransportProtocol(SAPParam):
+ """Transport Protocol Param """
+
+ def __init__(self, value = None):
+ SAPParam.__init__(self,"TransportProtocol", SAPParam.TransportProtocol, value)
+ self.__validate()
+
+ def __validate(self):
+ if self.value is not None and self.value not in (0x00, 0x01):
+ print "Warning. TransportProtoco value in reserved range (0x%x)" % self.value
+
+ def deserialize(self, buf):
+ ret = SAPParam.deserialize(self, buf)
+ if ret == -1:
+ return -1
+ self.__validate()
+ return ret
+
+class SAPMessage:
+
+ CONNECT_REQ = 0x00
+ CONNECT_RESP = 0x01
+ DISCONNECT_REQ = 0x02
+ DISCONNECT_RESP =0x03
+ DISCONNECT_IND = 0x04
+ TRANSFER_APDU_REQ = 0x05
+ TRANSFER_APDU_RESP = 0x06
+ TRANSFER_ATR_REQ = 0x07
+ TRANSFER_ATR_RESP = 0x08
+ POWER_SIM_OFF_REQ = 0x09
+ POWER_SIM_OFF_RESP = 0x0A
+ POWER_SIM_ON_REQ = 0x0B
+ POWER_SIM_ON_RESP = 0x0C
+ RESET_SIM_REQ = 0x0D
+ RESET_SIM_RESP = 0x0E
+ TRANSFER_CARD_READER_STATUS_REQ = 0x0F
+ TRANSFER_CARD_READER_STATUS_RESP = 0x10
+ STATUS_IND = 0x11
+ ERROR_RESP = 0x12
+ SET_TRANSPORT_PROTOCOL_REQ = 0x13
+ SET_TRANSPORT_PROTOCOL_RESP = 0x14
+
+ def __init__(self, name, id):
+ self.name = name
+ self.id = id
+ self.params = []
+ self.buf = array('B')
+
+ def _basicCheck(self, buf):
+ if len(buf) < 4 or (len(buf) % 4) != 0 :
+ return False
+
+ if buf[0] != self.id:
+ return False
+
+ return True
+
+ def getID(self):
+ return self.id
+
+ def getContent(self):
+ s = "%s(id=0x%.2X) " % (self.name, self.id)
+ if len( self.buf): s = s + "[%s]" % re.sub("(.{2})", "0x\\1 " , self.buf.tostring().encode("hex").upper(), re.DOTALL)
+ s = s + "\n\t"
+ for p in self.params:
+ s = s + "\t" + p.getContent()
+ return s
+
+ def getParams(self):
+ return self.params
+
+ def addParam(self, param):
+ self.params.append(param)
+
+ def serialize(self):
+ ret = array('B', '\00\00\00\00')
+ ret[0] = self.id
+ ret[1] = len(self.params)
+ ret[2] = 0 # reserved
+ ret[3] = 0 # reserved
+ for p in self.params:
+ ret.extend(p.serialize())
+
+ self.buf = ret
+ return ret
+
+ def deserialize(self, buf):
+ self.buf = buf
+ return len(buf) == 4 and buf[1] == 0 and self._basicCheck(buf)
+
+
+class SAPMessage_CONNECT_REQ(SAPMessage):
+ def __init__(self, MaxMsgSize = None):
+ SAPMessage.__init__(self,"CONNECT_REQ", SAPMessage.CONNECT_REQ)
+ if MaxMsgSize is not None:
+ self.addParam(SAPParam_MaxMsgSize(MaxMsgSize))
+
+ def _validate(self):
+ if len(self.params) == 1:
+ if self.params[0].getID() == SAPParam.MaxMsgSize:
+ return True
+ return False
+
+ def deserialize(self, buf):
+ self.buf = buf
+ self.params[:] = []
+ if SAPMessage._basicCheck(self, buf):
+ p = SAPParam_MaxMsgSize()
+ if p.deserialize(buf[4:]) == len(buf[4:]):
+ self.addParam(p)
+ return self._validate()
+
+ return False
+
+class SAPMessage_CONNECT_RESP(SAPMessage):
+ def __init__(self, ConnectionStatus = None, MaxMsgSize = None):
+ SAPMessage.__init__(self,"CONNECT_RESP", SAPMessage.CONNECT_RESP)
+ if ConnectionStatus is not None:
+ self.addParam(SAPParam_ConnectionStatus(ConnectionStatus))
+ if MaxMsgSize is not None:
+ self.addParam(SAPParam_MaxMsgSize(MaxMsgSize))
+
+ def _validate(self):
+ if len(self.params) > 0:
+ if self.params[0] .getID() == SAPParam.ConnectionStatus:
+ if self.params[0].getValue() == 0x02:
+ if len(self.params) == 2:
+ return True
+ else:
+ if len(self.params) == 1:
+ return True
+ return False
+
+ def deserialize(self, buf):
+ self.buf = buf
+ self.params[:] = []
+
+ if SAPMessage._basicCheck(self, buf):
+ p = SAPParam_ConnectionStatus()
+ r = p.deserialize(buf[4:])
+ if r != -1:
+ self.addParam(p)
+ if buf[1] == 2:
+ p = SAPParam_MaxMsgSize()
+ r = p.deserialize(buf[4+r:])
+ if r != -1:
+ self.addParam(p)
+
+ return self._validate()
+
+ return False
+
+class SAPMessage_DISCONNECT_REQ(SAPMessage):
+ def __init__(self):
+ SAPMessage.__init__(self,"DISCONNECT_REQ", SAPMessage.DISCONNECT_REQ)
+
+class SAPMessage_DISCONNECT_RESP(SAPMessage):
+ def __init__(self):
+ SAPMessage.__init__(self,"DISCONNECT_RESP", SAPMessage.DISCONNECT_RESP)
+
+class SAPMessage_DISCONNECT_IND(SAPMessage):
+ def __init__(self, Type = None):
+ SAPMessage.__init__(self,"DISCONNECT_IND", SAPMessage.DISCONNECT_IND)
+ if Type is not None:
+ self.addParam(SAPParam_DisconnectionType(Type))
+
+ def _validate(self):
+ if len(self.params) == 1:
+ if self.params[0].getID() == SAPParam.DisconnectionType:
+ return True
+ return False
+
+ def deserialize(self, buf):
+ self.buf = buf
+ self.params[:] = []
+ if SAPMessage._basicCheck(self, buf):
+ p = SAPParam_DisconnectionType()
+ if p.deserialize(buf[4:]) == len(buf[4:]):
+ self.addParam(p)
+ return self._validate()
+
+ return False
+
+
+class SAPMessage_TRANSFER_APDU_REQ(SAPMessage):
+ def __init__(self, APDU = None, T = False):
+ SAPMessage.__init__(self,"TRANSFER_APDU_REQ", SAPMessage.TRANSFER_APDU_REQ)
+ if APDU is not None:
+ if T :
+ self.addParam(SAPParam_CommandAPDU(APDU))
+ else:
+ self.addParam(SAPParam_CommandAPDU7816(APDU))
+
+ def _validate(self):
+ if len(self.params) == 1:
+ if self.params[0].getID() == SAPParam.CommandAPDU or self.params[0].getID() == SAPParam.CommandAPDU7816:
+ return True
+ return False
+
+ def deserialize(self, buf):
+ self.buf = buf
+ self.params[:] = []
+ if SAPMessage._basicCheck(self, buf):
+
+ p = SAPParam_CommandAPDU()
+ p2 = SAPParam_CommandAPDU7816()
+ if p.deserialize(buf[4:]) == len(buf[4:]):
+ self.addParam(p)
+ return self._validate()
+ elif p2.deserialize(buf[4:]) == len(buf[4:]):
+ self.addParam(p2)
+ return self._validate()
+
+ return False
+
+class SAPMessage_TRANSFER_APDU_RESP(SAPMessage):
+ def __init__(self, ResultCode = None, Response = None):
+ SAPMessage.__init__(self,"TRANSFER_APDU_RESP", SAPMessage.TRANSFER_APDU_RESP)
+ if ResultCode is not None:
+ self.addParam(SAPParam_ResultCode(ResultCode))
+ if Response is not None:
+ self.addParam(SAPParam_ResponseAPDU(Response))
+
+ def _validate(self):
+ if len(self.params) > 0:
+ if self.params[0] .getID() == SAPParam.ResultCode:
+ if self.params[0].getValue() == 0x00:
+ if len(self.params) == 2:
+ return True
+ else:
+ if len(self.params) == 1:
+ return True
+ return False
+
+ def deserialize(self, buf):
+ self.buf = buf
+ self.params[:] = []
+
+ if SAPMessage._basicCheck(self, buf):
+ p = SAPParam_ResultCode()
+ r = p.deserialize(buf[4:])
+ if r != -1:
+ self.addParam(p)
+ if buf[1] == 2:
+ p = SAPParam_ResponseAPDU()
+ r = p.deserialize(buf[4+r:])
+ if r != -1:
+ self.addParam(p)
+
+ return self._validate()
+
+ return False
+
+class SAPMessage_TRANSFER_ATR_REQ(SAPMessage):
+ def __init__(self):
+ SAPMessage.__init__(self,"TRANSFER_ATR_REQ", SAPMessage.TRANSFER_ATR_REQ)
+
+class SAPMessage_TRANSFER_ATR_RESP(SAPMessage):
+ def __init__(self, ResultCode = None, ATR = None):
+ SAPMessage.__init__(self,"TRANSFER_ATR_RESP", SAPMessage.TRANSFER_ATR_RESP)
+ if ResultCode is not None:
+ self.addParam(SAPParam_ResultCode(ResultCode))
+ if ATR is not None:
+ self.addParam(SAPParam_ATR(ATR))
+
+ def _validate(self):
+ if len(self.params) > 0:
+ if self.params[0] .getID() == SAPParam.ResultCode:
+ if self.params[0].getValue() == 0x00:
+ if len(self.params) == 2:
+ return True
+ else:
+ if len(self.params) == 1:
+ return True
+ return False
+
+ def deserialize(self, buf):
+ self.buf = buf
+ self.params[:] = []
+
+ if SAPMessage._basicCheck(self, buf):
+
+ p = SAPParam_ResultCode()
+ r = p.deserialize(buf[4:])
+
+ if r != -1:
+
+ self.addParam(p)
+ if buf[1] == 2:
+
+ p = SAPParam_ATR()
+ r = p.deserialize(buf[4+r:])
+ if r != -1:
+ self.addParam(p)
+
+ return self._validate()
+
+ return False
+
+class SAPMessage_POWER_SIM_OFF_REQ(SAPMessage):
+ def __init__(self):
+ SAPMessage.__init__(self,"POWER_SIM_OFF_REQ", SAPMessage.POWER_SIM_OFF_REQ)
+
+class SAPMessage_POWER_SIM_OFF_RESP(SAPMessage):
+ def __init__(self, ResultCode = None):
+ SAPMessage.__init__(self,"POWER_SIM_OFF_RESP", SAPMessage.POWER_SIM_OFF_RESP)
+ if ResultCode is not None:
+ self.addParam(SAPParam_ResultCode(ResultCode))
+
+ def _validate(self):
+ if len(self.params) == 1:
+ if self.params[0].getID() == SAPParam.ResultCode:
+ return True
+ return False
+
+ def deserialize(self, buf):
+ self.buf = buf
+ self.params[:] = []
+ if SAPMessage._basicCheck(self, buf):
+ p = SAPParam_ResultCode()
+ if p.deserialize(buf[4:]) == len(buf[4:]):
+ self.addParam(p)
+ return self._validate()
+
+ return False
+
+class SAPMessage_POWER_SIM_ON_REQ(SAPMessage):
+ def __init__(self):
+ SAPMessage.__init__(self,"POWER_SIM_ON_REQ", SAPMessage.POWER_SIM_ON_REQ)
+
+class SAPMessage_POWER_SIM_ON_RESP(SAPMessage_POWER_SIM_OFF_RESP):
+ def __init__(self, ResultCode = None):
+ SAPMessage.__init__(self,"POWER_SIM_ON_RESP", SAPMessage.POWER_SIM_ON_RESP)
+ if ResultCode is not None:
+ self.addParam(SAPParam_ResultCode(ResultCode))
+
+class SAPMessage_RESET_SIM_REQ(SAPMessage):
+ def __init__(self):
+ SAPMessage.__init__(self,"RESET_SIM_REQ", SAPMessage.RESET_SIM_REQ)
+
+class SAPMessage_RESET_SIM_RESP(SAPMessage_POWER_SIM_OFF_RESP):
+ def __init__(self, ResultCode = None):
+ SAPMessage.__init__(self,"RESET_SIM_RESP", SAPMessage.RESET_SIM_RESP)
+ if ResultCode is not None:
+ self.addParam(SAPParam_ResultCode(ResultCode))
+
+class SAPMessage_STATUS_IND(SAPMessage):
+ def __init__(self, StatusChange = None):
+ SAPMessage.__init__(self,"STATUS_IND", SAPMessage.STATUS_IND)
+ if StatusChange is not None:
+ self.addParam(SAPParam_StatusChange(StatusChange))
+
+ def _validate(self):
+ if len(self.params) == 1:
+ if self.params[0].getID() == SAPParam.StatusChange:
+ return True
+ return False
+
+ def deserialize(self, buf):
+ self.buf = buf
+ self.params[:] = []
+ if SAPMessage._basicCheck(self, buf):
+ p = SAPParam_StatusChange()
+ if p.deserialize(buf[4:]) == len(buf[4:]):
+ self.addParam(p)
+ return self._validate()
+
+ return False
+
+class SAPMessage_TRANSFER_CARD_READER_STATUS_REQ(SAPMessage):
+ def __init__(self):
+ SAPMessage.__init__(self,"TRANSFER_CARD_READER_STATUS_REQ", SAPMessage.TRANSFER_CARD_READER_STATUS_REQ)
+
+class SAPMessage_TRANSFER_CARD_READER_STATUS_RESP(SAPMessage):
+ def __init__(self, ResultCode = None, Status = None):
+ SAPMessage.__init__(self,"TRANSFER_CARD_READER_STATUS_RESP", SAPMessage.TRANSFER_CARD_READER_STATUS_RESP)
+ if ResultCode is not None:
+ self.addParam(SAPParam_ResultCode(ResultCode))
+ if Status is not None:
+ self.addParam(SAPParam_CardReaderStatus(Status))
+
+ def _validate(self):
+ if len(self.params) > 0:
+ if self.params[0] .getID() == SAPParam.ResultCode:
+ if self.params[0].getValue() == 0x00:
+ if len(self.params) == 2:
+ return True
+ else:
+ if len(self.params) == 1:
+ return True
+ return False
+
+ def deserialize(self, buf):
+ self.buf = buf
+ self.params[:] = []
+
+ if SAPMessage._basicCheck(self, buf):
+ p = SAPParam_ResultCode()
+ r = p.deserialize(buf[4:])
+ if r != -1:
+ self.addParam(p)
+ if buf[1] == 2:
+ p = SAPParam_CardReaderStatus()
+ r = p.deserialize(buf[4+r:])
+ if r != -1:
+ self.addParam(p)
+
+ return self._validate()
+
+ return False
+
+class SAPMessage_ERROR_RESP(SAPMessage):
+ def __init__(self):
+ SAPMessage.__init__(self,"ERROR_RESP", SAPMessage.ERROR_RESP)
+
+
+class SAPMessage_SET_TRANSPORT_PROTOCOL_REQ(SAPMessage):
+ def __init__(self, protocol = None):
+ SAPMessage.__init__(self,"SET_TRANSPORT_PROTOCOL_REQ", SAPMessage.SET_TRANSPORT_PROTOCOL_REQ)
+ if protocol is not None:
+ self.addParam(SAPParam_TransportProtocol(protocol))
+
+ def _validate(self):
+ if len(self.params) == 1:
+ if self.params[0].getID() == SAPParam.TransportProtocol:
+ return True
+ return False
+
+ def deserialize(self, buf):
+ self.buf = buf
+ self.params[:] = []
+ if SAPMessage._basicCheck(self, buf):
+ p = SAPParam_TransportProtocol()
+ if p.deserialize(buf[4:]) == len(buf[4:]):
+ self.addParam(p)
+ return self._validate()
+
+ return False
+
+class SAPMessage_SET_TRANSPORT_PROTOCOL_RESP(SAPMessage_POWER_SIM_OFF_RESP):
+ def __init__(self, ResultCode = None):
+ SAPMessage.__init__(self,"SET_TRANSPORT_PROTOCOL_RESP", SAPMessage.SET_TRANSPORT_PROTOCOL_RESP)
+ if ResultCode is not None:
+ self.addParam(SAPParam_ResultCode(ResultCode))
+
+
+class SAPClient:
+
+ CONNECTED = 1
+ DISCONNECTED = 0
+
+ uuid = "0000112D-0000-1000-8000-00805F9B34FB"
+ bufsize = 1024
+ timeout = 20
+ state = DISCONNECTED
+
+ def __init__(self, host = None, port = None):
+ self.sock = None
+
+ if host is None or is_valid_address(host):
+ self.host = host
+ else:
+ raise BluetoothError ("%s is not a valid BT address." % host)
+ self.host = None
+ return
+
+ if port is None:
+ self.__discover()
+ else:
+ self.port = port
+
+ self.__connectRFCOMM()
+
+ def __del__(self):
+ self.__disconnectRFCOMM()
+
+ def __disconnectRFCOMM(self):
+ if self.sock is not None:
+ self.sock.close()
+ self.state = self.DISCONNECTED
+
+ def __discover(self):
+ service_matches = find_service(self.uuid, self.host)
+
+ if len(service_matches) == 0:
+ raise BluetoothError ("No SAP service found")
+ return
+
+ first_match = service_matches[0]
+ self.port = first_match["port"]
+ self.host = first_match["host"]
+
+ print "SAP Service found on %s(%s)" % first_match["name"] % self.host
+
+ def __connectRFCOMM(self):
+ self.sock=BluetoothSocket( RFCOMM )
+ self.sock.connect((self.host, self.port))
+ self.sock.settimeout(self.timeout)
+ self.state = self.CONNECTED
+
+ def __sendMsg(self, msg):
+ if isinstance(msg, SAPMessage):
+ s = msg.serialize()
+ print "\tTX: " + msg.getContent()
+ return self.sock.send(s.tostring())
+
+ def __rcvMsg(self, msg):
+ if isinstance(msg, SAPMessage):
+ print "\tRX Wait: %s(id = 0x%.2x)" % (msg.name, msg.id)
+ data = self.sock.recv(self.bufsize)
+ if data:
+ if msg.deserialize(array('B',data)):
+ print "\tRX: len(%d) %s" % (len(data), msg.getContent())
+ return msg
+ else:
+ print "msg: %s" % array('B',data)
+ raise BluetoothError ("Message deserialization failed.")
+ else:
+ raise BluetoothError ("Timeout. No data received.")
+
+ def connect(self):
+ self.__connectRFCOMM()
+
+ def disconnect(self):
+ self.__disconnectRFCOMM()
+
+ def isConnected(self):
+ return self.state
+
+ def proc_connect(self):
+ try:
+ self.__sendMsg(SAPMessage_CONNECT_REQ(self.bufsize))
+ params = self.__rcvMsg(SAPMessage_CONNECT_RESP()).getParams()
+
+ if params[0].getValue() in (0x00, 0x04):
+ pass
+ elif params[0].getValue() == 0x02:
+ self.bufsize = params[1].getValue()
+
+ self.__sendMsg(SAPMessage_CONNECT_REQ(self.bufsize))
+ params = self.__rcvMsg(SAPMessage_CONNECT_RESP()).getParams()
+
+ if params[0].getValue() not in (0x00, 0x04):
+ return False
+ else:
+ return False
+
+ params = self.__rcvMsg(SAPMessage_STATUS_IND()).getParams()
+ if params[0].getValue() == 0x00:
+ return False
+ elif params[0].getValue() == 0x01:
+ """OK, Card reset"""
+ return self.proc_transferATR()
+ elif params[0].getValue() == 0x02:
+ """T0 not supported"""
+ if self.proc_transferATR():
+ return self.proc_setTransportProtocol(1)
+ else:
+ return False
+ else:
+ return False
+ except BluetoothError , e:
+ print "Error. " +str(e)
+ return False
+
+ def proc_disconnectByClient(self, timeout=0):
+ try:
+ self.__sendMsg(SAPMessage_DISCONNECT_REQ())
+ self.__rcvMsg(SAPMessage_DISCONNECT_RESP())
+ time.sleep(timeout) # let srv to close rfcomm
+ self.__disconnectRFCOMM()
+ return True
+ except BluetoothError , e:
+ print "Error. " +str(e)
+ return False
+
+ def proc_disconnectByServer(self, timeout=0):
+ try:
+ params = self.__rcvMsg(SAPMessage_DISCONNECT_IND()).getParams()
+
+ """gracefull"""
+ if params[0].getValue() == 0x00:
+ if not self.proc_transferAPDU():
+ return False
+
+ return self.proc_disconnectByClient(timeout)
+
+ except BluetoothError , e:
+ print "Error. " +str(e)
+ return False
+
+ def proc_transferAPDU(self, apdu = "Sample APDU command"):
+ try:
+ self.__sendMsg(SAPMessage_TRANSFER_APDU_REQ(apdu))
+ params = self.__rcvMsg(SAPMessage_TRANSFER_APDU_RESP()).getParams()
+ return True
+ except BluetoothError , e:
+ print "Error. " +str(e)
+ return False
+
+ def proc_transferATR(self):
+ try:
+ self.__sendMsg(SAPMessage_TRANSFER_ATR_REQ())
+ params = self.__rcvMsg(SAPMessage_TRANSFER_ATR_RESP()).getParams()
+ return True
+ except BluetoothError , e:
+ print "Error. " +str(e)
+ return False
+
+ def proc_powerSimOff(self):
+ try:
+ self.__sendMsg(SAPMessage_POWER_SIM_OFF_REQ())
+ params = self.__rcvMsg(SAPMessage_POWER_SIM_OFF_RESP()).getParams()
+ return True
+ except BluetoothError , e:
+ print "Error. " +str(e)
+ return False
+
+ def proc_powerSimOn(self):
+ try:
+ self.__sendMsg(SAPMessage_POWER_SIM_ON_REQ())
+ params = self.__rcvMsg(SAPMessage_POWER_SIM_ON_RESP()).getParams()
+ if params[0].getValue() == 0x00:
+ return self.proc_transferATR()
+
+ return True
+ except BluetoothError , e:
+ print "Error. " +str(e)
+ return False
+
+ def proc_resetSim(self):
+ try:
+ self.__sendMsg(SAPMessage_RESET_SIM_REQ())
+ params = self.__rcvMsg(SAPMessage_RESET_SIM_RESP()).getParams()
+ if params[0].getValue() == 0x00:
+ return self.proc_transferATR()
+
+ return True
+ except BluetoothError , e:
+ print "Error. " +str(e)
+ return False
+
+ def proc_reportStatus(self):
+ try:
+ params = self.__rcvMsg(SAPMessage_STATUS_IND()).getParams()
+ except BluetoothError , e:
+ print "Error. " +str(e)
+ return False
+
+ def proc_transferCardReaderStatus(self):
+ try:
+ self.__sendMsg(SAPMessage_TRANSFER_CARD_READER_STATUS_REQ())
+ params = self.__rcvMsg(SAPMessage_TRANSFER_CARD_READER_STATUS_RESP()).getParams()
+ except BluetoothError , e:
+ print "Error. " +str(e)
+ return False
+
+ def proc_errorResponse(self):
+ try:
+ """ send malformed message, no mandatory maxmsgsize parameter"""
+ self.__sendMsg(SAPMessage_CONNECT_REQ())
+
+ params = self.__rcvMsg(SAPMessage_ERROR_RESP()).getParams()
+ except BluetoothError , e:
+ print "Error. " +str(e)
+ return False
+
+ def proc_setTransportProtocol(self, protocol = 0):
+ try:
+ self.__sendMsg(SAPMessage_SET_TRANSPORT_PROTOCOL_REQ(protocol))
+ params = self.__rcvMsg(SAPMessage_SET_TRANSPORT_PROTOCOL_RESP()).getParams()
+
+ if params[0].getValue() == 0x00:
+ params = self.__rcvMsg(SAPMessage_STATUS_IND()).getParams()
+ if params[0].getValue() in (0x01, 0x02):
+ return self.proc_transferATR()
+ else:
+ return True
+ """return False ???"""
+ elif params[0].getValue == 0x07:
+ """not supported"""
+ return True
+ """return False ???"""
+ else:
+ return False
+
+ except BluetoothError , e:
+ print "Error. " +str(e)
+ return False
+
+if __name__ == "__main__":
+ pass
diff --git a/test/scotest.c b/test/scotest.c
new file mode 100644
index 0000000..50b622a
--- /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-2010 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 0000000..480a468
--- /dev/null
+++ b/test/sdptest.c
@@ -0,0 +1,146 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2005-2010 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)
+{
+ unsigned 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;
+}
diff --git a/test/service-did.xml b/test/service-did.xml
new file mode 100644
index 0000000..52eb68c
--- /dev/null
+++ b/test/service-did.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<record>
+ <attribute id="0x0001">
+ <sequence>
+ <uuid value="0x1200"/>
+ </sequence>
+ </attribute>
+
+ <attribute id="0x0200">
+ <uint16 value="0x0102" name="id"/>
+ </attribute>
+
+ <attribute id="0x0201">
+ <uint16 value="0x0a12" name="vendor"/>
+ </attribute>
+
+ <attribute id="0x0202">
+ <uint16 value="0x4711" name="product"/>
+ </attribute>
+
+ <attribute id="0x0203">
+ <uint16 value="0x0000" name="version"/>
+ </attribute>
+
+ <attribute id="0x0204">
+ <boolean value="true"/>
+ </attribute>
+
+ <attribute id="0x0205">
+ <uint16 value="0x0002" name="source"/>
+ </attribute>
+</record>
diff --git a/test/service-ftp.xml b/test/service-ftp.xml
new file mode 100644
index 0000000..1bda885
--- /dev/null
+++ b/test/service-ftp.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<record>
+ <attribute id="0x0001">
+ <sequence>
+ <uuid value="0x1106"/>
+ </sequence>
+ </attribute>
+
+ <attribute id="0x0004">
+ <sequence>
+ <sequence>
+ <uuid value="0x0100"/>
+ </sequence>
+ <sequence>
+ <uuid value="0x0003"/>
+ <uint8 value="23" name="channel"/>
+ </sequence>
+ <sequence>
+ <uuid value="0x0008"/>
+ </sequence>
+ </sequence>
+ </attribute>
+
+ <attribute id="0x0009">
+ <sequence>
+ <sequence>
+ <uuid value="0x1106"/>
+ <uint16 value="0x0100" name="version"/>
+ </sequence>
+ </sequence>
+ </attribute>
+
+ <attribute id="0x0100">
+ <text value="OBEX File Transfer" name="name"/>
+ </attribute>
+</record>
diff --git a/test/service-opp.xml b/test/service-opp.xml
new file mode 100644
index 0000000..351b4a4
--- /dev/null
+++ b/test/service-opp.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<record>
+ <attribute id="0x0001">
+ <sequence>
+ <uuid value="0x1105"/>
+ </sequence>
+ </attribute>
+
+ <attribute id="0x0004">
+ <sequence>
+ <sequence>
+ <uuid value="0x0100"/>
+ </sequence>
+ <sequence>
+ <uuid value="0x0003"/>
+ <uint8 value="23" name="channel"/>
+ </sequence>
+ <sequence>
+ <uuid value="0x0008"/>
+ </sequence>
+ </sequence>
+ </attribute>
+
+ <attribute id="0x0009">
+ <sequence>
+ <sequence>
+ <uuid value="0x1105"/>
+ <uint16 value="0x0100" name="version"/>
+ </sequence>
+ </sequence>
+ </attribute>
+
+ <attribute id="0x0100">
+ <text value="OBEX Object Push" name="name"/>
+ </attribute>
+
+ <attribute id="0x0303">
+ <sequence>
+ <uint8 value="0x01"/>
+ <uint8 value="0x01"/>
+ <uint8 value="0x02"/>
+ <uint8 value="0x03"/>
+ <uint8 value="0x04"/>
+ <uint8 value="0x05"/>
+ <uint8 value="0x06"/>
+ <uint8 value="0xff"/>
+ </sequence>
+ </attribute>
+</record>
diff --git a/test/service-record.dtd b/test/service-record.dtd
new file mode 100644
index 0000000..f53be5d
--- /dev/null
+++ b/test/service-record.dtd
@@ -0,0 +1,66 @@
+<!ELEMENT record (attribute)*>
+
+<!ELEMENT attribute (sequence|alternate|text|url|uuid|boolean|uint8|uint16|uint32|uint64|nil)+>
+<!ATTLIST attribute id CDATA #REQUIRED>
+
+<!ELEMENT sequence (sequence|alternate|text|url|uuid|boolean|uint8|uint16|uint32|uint64|uint128|int8|int16|int32|int64|int128|nil)+>
+
+<!ELEMENT alternate (sequence|alternate|text|url|uuid|boolean|uint8|uint16|uint32|uint64|uint128|int8|int16|int32|int64|int128|nil)+>
+
+<!ELEMENT text EMPTY>
+<!ATTLIST text value CDATA #REQUIRED>
+<!ATTLIST text name CDATA>
+<!ATTLIST text encoding (normal|hex) "normal">
+
+<!ELEMENT url EMPTY>
+<!ATTLIST url value CDATA #REQUIRED>
+<!ATTLIST url name CDATA>
+
+<!ELEMENT uuid EMPTY>
+<!ATTLIST uuid value CDATA #REQUIRED>
+
+<!ELEMENT boolean EMPTY>
+<!ATTLIST boolean value CDATA #REQUIRED>
+<!ATTLIST boolean name CDATA>
+
+<!ELEMENT uint8 EMPTY>
+<!ATTLIST uint8 value CDATA #REQUIRED>
+<!ATTLIST uint8 name CDATA>
+
+<!ELEMENT uint16 EMPTY>
+<!ATTLIST uint16 value CDATA #REQUIRED>
+<!ATTLIST uint16 name CDATA>
+
+<!ELEMENT uint32 EMPTY>
+<!ATTLIST uint32 value CDATA #REQUIRED>
+<!ATTLIST uint32 name CDATA>
+
+<!ELEMENT uint64 EMPTY>
+<!ATTLIST uint64 value CDATA #REQUIRED>
+<!ATTLIST uint64 name CDATA>
+
+<!ELEMENT uint128 EMPTY>
+<!ATTLIST uint128 value CDATA #REQUIRED>
+<!ATTLIST uint128 name CDATA>
+
+<!ELEMENT int8 EMPTY>
+<!ATTLIST int8 value CDATA #REQUIRED>
+<!ATTLIST int8 name CDATA>
+
+<!ELEMENT int16 EMPTY>
+<!ATTLIST int16 value CDATA #REQUIRED>
+<!ATTLIST int16 name CDATA>
+
+<!ELEMENT int32 EMPTY>
+<!ATTLIST int32 value CDATA #REQUIRED>
+<!ATTLIST int32 name CDATA>
+
+<!ELEMENT int64 EMPTY>
+<!ATTLIST int64 value CDATA #REQUIRED>
+<!ATTLIST int64 name CDATA>
+
+<!ELEMENT int128 EMPTY>
+<!ATTLIST int128 value CDATA #REQUIRED>
+<!ATTLIST int128 name CDATA>
+
+<!ELEMENT nil EMPTY>
diff --git a/test/service-spp.xml b/test/service-spp.xml
new file mode 100644
index 0000000..2b156c3
--- /dev/null
+++ b/test/service-spp.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<record>
+ <attribute id="0x0001">
+ <sequence>
+ <uuid value="0x1101"/>
+ </sequence>
+ </attribute>
+
+ <attribute id="0x0004">
+ <sequence>
+ <sequence>
+ <uuid value="0x0100"/>
+ </sequence>
+ <sequence>
+ <uuid value="0x0003"/>
+ <uint8 value="23" name="channel"/>
+ </sequence>
+ </sequence>
+ </attribute>
+
+ <attribute id="0x0100">
+ <text value="COM5" name="name"/>
+ </attribute>
+</record>
diff --git a/test/simple-agent b/test/simple-agent
new file mode 100755
index 0000000..f2cc3dd
--- /dev/null
+++ b/test/simple-agent
@@ -0,0 +1,120 @@
+#!/usr/bin/python
+
+import gobject
+
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+
+class Rejected(dbus.DBusException):
+ _dbus_error_name = "org.bluez.Error.Rejected"
+
+class Agent(dbus.service.Object):
+ exit_on_release = True
+
+ def set_exit_on_release(self, exit_on_release):
+ self.exit_on_release = exit_on_release
+
+ @dbus.service.method("org.bluez.Agent",
+ in_signature="", out_signature="")
+ def Release(self):
+ print "Release"
+ if self.exit_on_release:
+ mainloop.quit()
+
+ @dbus.service.method("org.bluez.Agent",
+ in_signature="os", out_signature="")
+ def Authorize(self, device, uuid):
+ print "Authorize (%s, %s)" % (device, uuid)
+ authorize = raw_input("Authorize connection (yes/no): ")
+ if (authorize == "yes"):
+ return
+ raise Rejected("Connection rejected by user")
+
+ @dbus.service.method("org.bluez.Agent",
+ in_signature="o", out_signature="s")
+ def RequestPinCode(self, device):
+ print "RequestPinCode (%s)" % (device)
+ return raw_input("Enter PIN Code: ")
+
+ @dbus.service.method("org.bluez.Agent",
+ in_signature="o", out_signature="u")
+ def RequestPasskey(self, device):
+ print "RequestPasskey (%s)" % (device)
+ passkey = raw_input("Enter passkey: ")
+ return dbus.UInt32(passkey)
+
+ @dbus.service.method("org.bluez.Agent",
+ in_signature="ou", out_signature="")
+ def DisplayPasskey(self, device, passkey):
+ print "DisplayPasskey (%s, %d)" % (device, passkey)
+
+ @dbus.service.method("org.bluez.Agent",
+ in_signature="ou", out_signature="")
+ def RequestConfirmation(self, device, passkey):
+ print "RequestConfirmation (%s, %d)" % (device, passkey)
+ confirm = raw_input("Confirm passkey (yes/no): ")
+ if (confirm == "yes"):
+ return
+ raise Rejected("Passkey doesn't match")
+
+ @dbus.service.method("org.bluez.Agent",
+ in_signature="s", out_signature="")
+ def ConfirmModeChange(self, mode):
+ print "ConfirmModeChange (%s)" % (mode)
+ authorize = raw_input("Authorize mode change (yes/no): ")
+ if (authorize == "yes"):
+ return
+ raise Rejected("Mode change by user")
+
+ @dbus.service.method("org.bluez.Agent",
+ in_signature="", out_signature="")
+ def Cancel(self):
+ print "Cancel"
+
+def create_device_reply(device):
+ print "New device (%s)" % (device)
+ mainloop.quit()
+
+def create_device_error(error):
+ print "Creating device failed: %s" % (error)
+ mainloop.quit()
+
+if __name__ == '__main__':
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+ bus = dbus.SystemBus()
+ manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+ "org.bluez.Manager")
+
+ if len(sys.argv) > 1:
+ path = manager.FindAdapter(sys.argv[1])
+ else:
+ path = manager.DefaultAdapter()
+
+ adapter = dbus.Interface(bus.get_object("org.bluez", path),
+ "org.bluez.Adapter")
+
+ path = "/test/agent"
+ agent = Agent(bus, path)
+
+ mainloop = gobject.MainLoop()
+
+ if len(sys.argv) > 2:
+ if len(sys.argv) > 3:
+ device = adapter.FindDevice(sys.argv[2])
+ adapter.RemoveDevice(device)
+
+ agent.set_exit_on_release(False)
+ adapter.CreatePairedDevice(sys.argv[2], path, "DisplayYesNo",
+ reply_handler=create_device_reply,
+ error_handler=create_device_error)
+ else:
+ adapter.RegisterAgent(path, "DisplayYesNo")
+ print "Agent registered"
+
+ mainloop.run()
+
+ #adapter.UnregisterAgent(path)
+ #print "Agent unregistered"
diff --git a/test/simple-endpoint b/test/simple-endpoint
new file mode 100755
index 0000000..e09a528
--- /dev/null
+++ b/test/simple-endpoint
@@ -0,0 +1,126 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+import gobject
+
+A2DP_SOURCE_UUID = "0000110A-0000-1000-8000-00805F9B34FB"
+A2DP_SINK_UUID = "0000110B-0000-1000-8000-00805F9B34FB"
+HFP_AG_UUID = "0000111F-0000-1000-8000-00805F9B34FB"
+HSP_AG_UUID = "00001112-0000-1000-8000-00805F9B34FB"
+
+SBC_CODEC = dbus.Byte(0x00)
+#Channel Modes: Mono DualChannel Stereo JointStereo
+#Frequencies: 16Khz 32Khz 44.1Khz 48Khz
+#Subbands: 4 8
+#Blocks: 4 8 12 16
+#Bitpool Range: 2-64
+SBC_CAPABILITIES = dbus.Array([dbus.Byte(0xff), dbus.Byte(0xff), dbus.Byte(2), dbus.Byte(64)])
+# JointStereo 44.1Khz Subbands: Blocks: 16 Bitpool Range: 2-32
+SBC_CONFIGURATION = dbus.Array([dbus.Byte(0x21), dbus.Byte(0x15), dbus.Byte(2), dbus.Byte(32)])
+
+MP3_CODEC = dbus.Byte(0x01)
+#Channel Modes: Mono DualChannel Stereo JointStereo
+#Frequencies: 32Khz 44.1Khz 48Khz
+#CRC: YES
+#Layer: 3
+#Bit Rate: All except Free format
+#VBR: Yes
+#Payload Format: RFC-2250
+MP3_CAPABILITIES = dbus.Array([dbus.Byte(0x3f), dbus.Byte(0x07), dbus.Byte(0xff), dbus.Byte(0xfe)])
+# JointStereo 44.1Khz Layer: 3 Bit Rate: VBR Format: RFC-2250
+MP3_CONFIGURATION = dbus.Array([dbus.Byte(0x21), dbus.Byte(0x02), dbus.Byte(0x00), dbus.Byte(0x80)])
+
+PCM_CODEC = dbus.Byte(0x00)
+PCM_CONFIGURATION = dbus.Array([], signature="ay")
+
+class Rejected(dbus.DBusException):
+ _dbus_error_name = "org.bluez.Error.Rejected"
+
+class Endpoint(dbus.service.Object):
+ exit_on_release = True
+ configuration = SBC_CONFIGURATION
+
+ def set_exit_on_release(self, exit_on_release):
+ self.exit_on_release = exit_on_release
+
+ def default_configuration(self, configuration):
+ self.configuration = configuration
+
+ @dbus.service.method("org.bluez.MediaEndpoint",
+ in_signature="", out_signature="")
+ def Release(self):
+ print "Release"
+ if self.exit_on_release:
+ mainloop.quit()
+
+ @dbus.service.method("org.bluez.MediaEndpoint",
+ in_signature="", out_signature="")
+ def ClearConfiguration(self):
+ print "ClearConfiguration"
+
+ @dbus.service.method("org.bluez.MediaEndpoint",
+ in_signature="oay", out_signature="")
+ def SetConfiguration(self, transport, config):
+ print "SetConfiguration (%s, %s)" % (transport, config)
+ return
+
+ @dbus.service.method("org.bluez.MediaEndpoint",
+ in_signature="ay", out_signature="ay")
+ def SelectConfiguration(self, caps):
+ print "SelectConfiguration (%s)" % (caps)
+ return self.configuration
+
+if __name__ == '__main__':
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+ bus = dbus.SystemBus()
+ manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+ "org.bluez.Manager")
+
+ if len(sys.argv) > 1:
+ path = manager.FindAdapter(sys.argv[1])
+ else:
+ path = manager.DefaultAdapter()
+
+ media = dbus.Interface(bus.get_object("org.bluez", path),
+ "org.bluez.Media")
+
+ path = "/test/endpoint"
+ endpoint = Endpoint(bus, path)
+ mainloop = gobject.MainLoop()
+
+ properties = dbus.Dictionary({ "UUID" : A2DP_SOURCE_UUID,
+ "Codec" : SBC_CODEC,
+ "DelayReporting" : True,
+ "Capabilities" : SBC_CAPABILITIES })
+
+ if len(sys.argv) > 2:
+ if sys.argv[2] == "sbcsink":
+ properties = dbus.Dictionary({ "UUID" : A2DP_SINK_UUID,
+ "Codec" : SBC_CODEC,
+ "DelayReporting" : True,
+ "Capabilities" : SBC_CAPABILITIES })
+ if sys.argv[2] == "mp3source":
+ properties = dbus.Dictionary({ "UUID" : A2DP_SOURCE_UUID,
+ "Codec" : MP3_CODEC,
+ "Capabilities" : MP3_CAPABILITIES })
+ endpoint.default_configuration(MP3_CONFIGURATION)
+ if sys.argv[2] == "mp3sink":
+ properties = dbus.Dictionary({ "UUID" : A2DP_SINK_UUID,
+ "Codec" : MP3_CODEC,
+ "Capabilities" : MP3_CAPABILITIES })
+ endpoint.default_configuration(MP3_CONFIGURATION)
+ if sys.argv[2] == "hfpag" or sys.argv[2] == "hspag":
+ properties = dbus.Dictionary({ "UUID" : HFP_AG_UUID,
+ "Codec" : PCM_CODEC,
+ "Capabilities" : PCM_CONFIGURATION })
+ endpoint.default_configuration(dbus.Array([]))
+
+ print properties
+
+ media.RegisterEndpoint(path, properties)
+
+ mainloop.run()
diff --git a/test/simple-service b/test/simple-service
new file mode 100755
index 0000000..d03ec3d
--- /dev/null
+++ b/test/simple-service
@@ -0,0 +1,127 @@
+#!/usr/bin/python
+
+import sys
+import time
+import dbus
+
+xml = ' \
+<?xml version="1.0" encoding="UTF-8" ?> \
+<record> \
+ <attribute id="0x0001"> \
+ <sequence> \
+ <uuid value="0x1101"/> \
+ </sequence> \
+ </attribute> \
+ \
+ <attribute id="0x0002"> \
+ <uint32 value="0"/> \
+ </attribute> \
+ \
+ <attribute id="0x0003"> \
+ <uuid value="00001101-0000-1000-8000-00805f9b34fb"/> \
+ </attribute> \
+ \
+ <attribute id="0x0004"> \
+ <sequence> \
+ <sequence> \
+ <uuid value="0x0100"/> \
+ </sequence> \
+ <sequence> \
+ <uuid value="0x0003"/> \
+ <uint8 value="23"/> \
+ </sequence> \
+ </sequence> \
+ </attribute> \
+ \
+ <attribute id="0x0005"> \
+ <sequence> \
+ <uuid value="0x1002"/> \
+ </sequence> \
+ </attribute> \
+ \
+ <attribute id="0x0006"> \
+ <sequence> \
+ <uint16 value="0x656e"/> \
+ <uint16 value="0x006a"/> \
+ <uint16 value="0x0100"/> \
+ </sequence> \
+ </attribute> \
+ \
+ <attribute id="0x0007"> \
+ <uint32 value="0"/> \
+ </attribute> \
+ \
+ <attribute id="0x0008"> \
+ <uint8 value="0xff"/> \
+ </attribute> \
+ \
+ <attribute id="0x0009"> \
+ <sequence> \
+ <sequence> \
+ <uuid value="0x1101"/> \
+ <uint16 value="0x0100"/> \
+ </sequence> \
+ </sequence> \
+ </attribute> \
+ \
+ <attribute id="0x000a"> \
+ <url value="http://www.bluez.org/"/> \
+ </attribute> \
+ \
+ <attribute id="0x000b"> \
+ <url value="http://www.bluez.org/"/> \
+ </attribute> \
+ \
+ <attribute id="0x000c"> \
+ <url value="http://www.bluez.org/"/> \
+ </attribute> \
+ \
+ <attribute id="0x0100"> \
+ <text value="Serial Port"/> \
+ </attribute> \
+ \
+ <attribute id="0x0101"> \
+ <text value="Serial Port Service"/> \
+ </attribute> \
+ \
+ <attribute id="0x0102"> \
+ <text value="BlueZ"/> \
+ </attribute> \
+ \
+ <attribute id="0x0200"> \
+ <sequence> \
+ <uint16 value="0x0100"/> \
+ </sequence> \
+ </attribute> \
+ \
+ <attribute id="0x0201"> \
+ <uint32 value="0"/> \
+ </attribute> \
+</record> \
+'
+
+bus = dbus.SystemBus()
+manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+ "org.bluez.Manager")
+
+if len(sys.argv) > 1:
+ path = manager.FindAdapter(sys.argv[1])
+else:
+ path = manager.DefaultAdapter()
+
+service = dbus.Interface(bus.get_object("org.bluez", path),
+ "org.bluez.Service")
+
+handle = service.AddRecord(xml)
+
+print "Service record with handle 0x%04x added" % (handle)
+
+print "Press CTRL-C to remove service record"
+
+try:
+ time.sleep(1000)
+ print "Terminating session"
+except:
+ pass
+
+service.RemoveRecord(dbus.UInt32(handle))
diff --git a/test/test-adapter b/test/test-adapter
new file mode 100755
index 0000000..00ef6f5
--- /dev/null
+++ b/test/test-adapter
@@ -0,0 +1,120 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+import time
+from optparse import OptionParser, make_option
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.bluez", "/"), "org.bluez.Manager")
+
+option_list = [
+ make_option("-i", "--device", action="store",
+ type="string", dest="dev_id"),
+ ]
+parser = OptionParser(option_list=option_list)
+
+(options, args) = parser.parse_args()
+
+if options.dev_id:
+ adapter_path = manager.FindAdapter(options.dev_id)
+else:
+ adapter_path = manager.DefaultAdapter()
+
+adapter = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+ "org.bluez.Adapter")
+
+if (len(args) < 1):
+ print "Usage: %s <command>" % (sys.argv[0])
+ print ""
+ print " address"
+ print " name [name]"
+ print " powered [on/off]"
+ print " pairable [on/off]"
+ print " pairabletimeout [timeout]"
+ print " discoverable [on/off]"
+ print " discoverabletimeout [timeout]"
+ print " discovering"
+ sys.exit(1)
+
+if (args[0] == "address"):
+ properties = adapter.GetProperties()
+ print properties["Address"]
+ sys.exit(0)
+
+if (args[0] == "name"):
+ if (len(args) < 2):
+ properties = adapter.GetProperties()
+ print properties["Name"]
+ else:
+ adapter.SetProperty("Name", args[1])
+ sys.exit(0)
+
+if (args[0] == "powered"):
+ if (len(args) < 2):
+ properties = adapter.GetProperties()
+ print properties["Powered"]
+ else:
+ if (args[1] == "on"):
+ value = dbus.Boolean(1)
+ elif (args[1] == "off"):
+ value = dbus.Boolean(0)
+ else:
+ value = dbus.Boolean(args[1])
+ adapter.SetProperty("Powered", value)
+ sys.exit(0)
+
+if (args[0] == "pairable"):
+ if (len(args) < 2):
+ properties = adapter.GetProperties()
+ print properties["Pairable"]
+ else:
+ if (args[1] == "on"):
+ value = dbus.Boolean(1)
+ elif (args[1] == "off"):
+ value = dbus.Boolean(0)
+ else:
+ value = dbus.Boolean(args[1])
+ adapter.SetProperty("Pairable", value)
+ sys.exit(0)
+
+if (args[0] == "pairabletimeout"):
+ if (len(args) < 2):
+ properties = adapter.GetProperties()
+ print properties["PairableTimeout"]
+ else:
+ timeout = dbus.UInt32(args[1])
+ adapter.SetProperty("PairableTimeout", timeout)
+ sys.exit(0)
+
+if (args[0] == "discoverable"):
+ if (len(args) < 2):
+ properties = adapter.GetProperties()
+ print properties["Discoverable"]
+ else:
+ if (args[1] == "on"):
+ value = dbus.Boolean(1)
+ elif (args[1] == "off"):
+ value = dbus.Boolean(0)
+ else:
+ value = dbus.Boolean(args[1])
+ adapter.SetProperty("Discoverable", value)
+ sys.exit(0)
+
+if (args[0] == "discoverabletimeout"):
+ if (len(args) < 2):
+ properties = adapter.GetProperties()
+ print properties["DiscoverableTimeout"]
+ else:
+ timeout = dbus.UInt32(args[1])
+ adapter.SetProperty("DiscoverableTimeout", timeout)
+ sys.exit(0)
+
+if (args[0] == "discovering"):
+ properties = adapter.GetProperties()
+ print properties["Discovering"]
+ sys.exit(0)
+
+print "Unknown command"
+sys.exit(1)
diff --git a/test/test-attrib b/test/test-attrib
new file mode 100755
index 0000000..b9e83c5
--- /dev/null
+++ b/test/test-attrib
@@ -0,0 +1,108 @@
+#!/usr/bin/python
+# Script for testing the Attribute D-Bus API
+
+import sys
+from optparse import OptionParser, OptionValueError
+from binascii import hexlify, unhexlify
+
+import gobject
+
+import sys
+import dbus
+import dbus.mainloop.glib
+from optparse import OptionParser, make_option
+
+dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+bus = dbus.SystemBus()
+mainloop = gobject.MainLoop()
+
+manager = dbus.Interface(bus.get_object("org.bluez", "/"), "org.bluez.Manager")
+
+option_list = [
+ make_option("-i", "--device", action="store",
+ type="string", dest="dev_id"),
+ ]
+parser = OptionParser(option_list=option_list)
+
+(options, args) = parser.parse_args()
+
+if options.dev_id:
+ adapter_path = manager.FindAdapter(options.dev_id)
+else:
+ adapter_path = manager.DefaultAdapter()
+
+adapter = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+ "org.bluez.Adapter")
+
+if (len(args) < 1):
+ print "Usage: %s <command>" % (sys.argv[0])
+ print ""
+ print " list"
+ print " services <address>"
+ print " discover <service path>"
+ print " chars <service path>"
+ sys.exit(1)
+
+if (args[0] == "list"):
+ for path in adapter.ListDevices():
+ device = dbus.Interface(bus.get_object("org.bluez", path),
+ "org.bluez.Device")
+ devprop = device.GetProperties()
+ print "[ %s ]" % devprop["Address"]
+ for path in devprop["Services"]:
+
+ service = dbus.Interface(bus.get_object("org.bluez", path),
+ "org.bluez.Characteristic")
+ srvprop = service.GetProperties()
+ print " * %s" % (path)
+ print " UUID: %s" % srvprop["UUID"]
+ print " Chars: ",
+ for char in srvprop["Characteristics"]:
+ print "%s " % char,
+ print
+ print
+ print
+ sys.exit(0)
+
+if (args[0] == "services"):
+ if (len(args) < 2):
+ print "Need address parameter"
+ else:
+ path = adapter.FindDevice(args[1])
+ device = dbus.Interface(bus.get_object("org.bluez", path),
+ "org.bluez.Device")
+ properties = device.GetProperties()
+ for path in properties["Services"]:
+ print path
+ sys.exit(0)
+
+if (args[0] == "discover"):
+ if (len(args) < 2):
+ print "Need service path parameter"
+ else:
+ service = dbus.Interface(bus.get_object("org.bluez", args[1]),
+ "org.bluez.Characteristic")
+ for path in service.DiscoverCharacteristics():
+ print path
+ sys.exit(0)
+
+if (args[0] == "chars"):
+ if (len(args) < 2):
+ print "Need service path parameter"
+ else:
+ service = dbus.Interface(bus.get_object("org.bluez", args[1]),
+ "org.bluez.Characteristic")
+ srvprop = service.GetProperties()
+ for path in srvprop["Characteristics"]:
+ print "[ %s ]" % (path)
+ char = dbus.Interface(bus.get_object("org.bluez", path),
+ "org.bluez.Characteristic")
+ charprop = char.GetProperties()
+ print " Name: %s" % charprop["Name"]
+ print " UUID: %s" % charprop["UUID"]
+ print
+ print
+ sys.exit(0)
+
+print "Unknown command"
+sys.exit(1)
diff --git a/test/test-audio b/test/test-audio
new file mode 100755
index 0000000..8b7a62d
--- /dev/null
+++ b/test/test-audio
@@ -0,0 +1,45 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+from optparse import OptionParser, make_option
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.bluez", "/"), "org.bluez.Manager")
+
+option_list = [
+ make_option("-i", "--device", action="store",
+ type="string", dest="dev_id"),
+ ]
+parser = OptionParser(option_list=option_list)
+
+(options, args) = parser.parse_args()
+
+if options.dev_id:
+ adapter_path = manager.FindAdapter(options.dev_id)
+else:
+ adapter_path = manager.DefaultAdapter()
+
+adapter = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+ "org.bluez.Adapter")
+
+if len(args) < 2:
+ print """Usage: %s <command>
+
+ connect <bdaddr>
+ disconnect <bdaddr>
+ """ % sys.argv[0]
+ sys.exit(1)
+
+device = adapter.FindDevice(args[1])
+audio = dbus.Interface(bus.get_object("org.bluez", device),
+ "org.bluez.Audio")
+
+if args[0] == "connect":
+ audio.Connect()
+elif args[0] == "disconnect":
+ audio.Disconnect()
+else:
+ print "Unknown command"
+ sys.exit(1)
diff --git a/test/test-device b/test/test-device
new file mode 100755
index 0000000..154af19
--- /dev/null
+++ b/test/test-device
@@ -0,0 +1,207 @@
+#!/usr/bin/python
+
+import gobject
+
+import sys
+import dbus
+import dbus.mainloop.glib
+import re
+from optparse import OptionParser, make_option
+
+dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+bus = dbus.SystemBus()
+mainloop = gobject.MainLoop()
+
+manager = dbus.Interface(bus.get_object("org.bluez", "/"), "org.bluez.Manager")
+
+option_list = [
+ make_option("-i", "--device", action="store",
+ type="string", dest="dev_id"),
+ ]
+parser = OptionParser(option_list=option_list)
+
+(options, args) = parser.parse_args()
+
+if options.dev_id:
+ adapter_path = manager.FindAdapter(options.dev_id)
+else:
+ adapter_path = manager.DefaultAdapter()
+
+adapter = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+ "org.bluez.Adapter")
+
+if (len(args) < 1):
+ print "Usage: %s <command>" % (sys.argv[0])
+ print ""
+ print " list"
+ print " services <address>"
+ print " create <address>"
+ print " remove <address|path>"
+ print " disconnect <address>"
+ print " discover <address> [pattern]"
+ print " class <address>"
+ print " name <address>"
+ print " alias <address> [alias]"
+ print " trusted <address> [yes/no]"
+ print " blocked <address> [yes/no]"
+ sys.exit(1)
+
+if (args[0] == "list"):
+ for path in adapter.ListDevices():
+ device = dbus.Interface(bus.get_object("org.bluez", path),
+ "org.bluez.Device")
+ properties = device.GetProperties()
+ print "%s %s" % (properties["Address"], properties["Alias"])
+
+ sys.exit(0)
+
+def create_device_reply(device):
+ print "New device (%s)" % device
+ mainloop.quit()
+ sys.exit(0)
+
+def create_device_error(error):
+ print "Creating device failed: %s" % error
+ mainloop.quit()
+ sys.exit(1)
+
+if (args[0] == "create"):
+ if (len(args) < 2):
+ print "Need address parameter"
+ else:
+ adapter.CreateDevice(args[1],
+ reply_handler=create_device_reply,
+ error_handler=create_device_error)
+ mainloop.run()
+
+if (args[0] == "remove"):
+ if (len(args) < 2):
+ print "Need address or object path parameter"
+ else:
+ try:
+ path = adapter.FindDevice(args[1])
+ except:
+ path = args[1]
+ adapter.RemoveDevice(path)
+ sys.exit(0)
+
+if (args[0] == "disconnect"):
+ if (len(args) < 2):
+ print "Need address parameter"
+ else:
+ path = adapter.FindDevice(args[1])
+ device = dbus.Interface(bus.get_object("org.bluez", path),
+ "org.bluez.Device")
+ device.Disconnect()
+ sys.exit(0)
+
+if (args[0] == "discover"):
+ if (len(args) < 2):
+ print "Need address parameter"
+ else:
+ path = adapter.FindDevice(args[1])
+ device = dbus.Interface(bus.get_object("org.bluez", path),
+ "org.bluez.Device")
+ if (len(args) < 3):
+ pattern = ""
+ else:
+ pattern = args[2]
+ services = device.DiscoverServices(pattern);
+ for key in services.keys():
+ p = re.compile(">.*?<")
+ xml = p.sub("><", services[key].replace("\n", ""))
+ print "[ 0x%5x ]" % (key)
+ print xml
+ print
+ sys.exit(0)
+
+if (args[0] == "class"):
+ if (len(args) < 2):
+ print "Need address parameter"
+ else:
+ path = adapter.FindDevice(args[1])
+ device = dbus.Interface(bus.get_object("org.bluez", path),
+ "org.bluez.Device")
+ properties = device.GetProperties()
+ print "0x%06x" % (properties["Class"])
+ sys.exit(0)
+
+if (args[0] == "name"):
+ if (len(args) < 2):
+ print "Need address parameter"
+ else:
+ path = adapter.FindDevice(args[1])
+ device = dbus.Interface(bus.get_object("org.bluez", path),
+ "org.bluez.Device")
+ properties = device.GetProperties()
+ print properties["Name"]
+ sys.exit(0)
+
+if (args[0] == "alias"):
+ if (len(args) < 2):
+ print "Need address parameter"
+ else:
+ path = adapter.FindDevice(args[1])
+ device = dbus.Interface(bus.get_object("org.bluez", path),
+ "org.bluez.Device")
+ if (len(args) < 3):
+ properties = device.GetProperties()
+ print properties["Alias"]
+ else:
+ device.SetProperty("Alias", args[2])
+ sys.exit(0)
+
+if (args[0] == "trusted"):
+ if (len(args) < 2):
+ print "Need address parameter"
+ else:
+ path = adapter.FindDevice(args[1])
+ device = dbus.Interface(bus.get_object("org.bluez", path),
+ "org.bluez.Device")
+ if (len(args) < 3):
+ properties = device.GetProperties()
+ print properties["Trusted"]
+ else:
+ if (args[2] == "yes"):
+ value = dbus.Boolean(1)
+ elif (args[2] == "no"):
+ value = dbus.Boolean(0)
+ else:
+ value = dbus.Boolean(args[2])
+ device.SetProperty("Trusted", value)
+ sys.exit(0)
+
+if (args[0] == "blocked"):
+ if (len(args) < 2):
+ print "Need address parameter"
+ else:
+ path = adapter.FindDevice(args[1])
+ device = dbus.Interface(bus.get_object("org.bluez", path),
+ "org.bluez.Device")
+ if (len(args) < 3):
+ properties = device.GetProperties()
+ print properties["Blocked"]
+ else:
+ if (args[2] == "yes"):
+ value = dbus.Boolean(1)
+ elif (args[2] == "no"):
+ value = dbus.Boolean(0)
+ else:
+ value = dbus.Boolean(args[2])
+ device.SetProperty("Blocked", value)
+ sys.exit(0)
+
+if (args[0] == "services"):
+ if (len(args) < 2):
+ print "Need address parameter"
+ else:
+ path = adapter.FindDevice(args[1])
+ device = dbus.Interface(bus.get_object("org.bluez", path),
+ "org.bluez.Device")
+ properties = device.GetProperties()
+ for path in properties["Services"]:
+ print path
+ sys.exit(0)
+
+print "Unknown command"
+sys.exit(1)
diff --git a/test/test-discovery b/test/test-discovery
new file mode 100755
index 0000000..22c88c3
--- /dev/null
+++ b/test/test-discovery
@@ -0,0 +1,57 @@
+#!/usr/bin/python
+
+import gobject
+
+import dbus
+import dbus.mainloop.glib
+from optparse import OptionParser, make_option
+
+def device_found(address, properties):
+ print "[ " + address + " ]"
+
+ for key in properties.keys():
+ value = properties[key]
+ if (key == "Class"):
+ print " %s = 0x%06x" % (key, value)
+ else:
+ print " %s = %s" % (key, value)
+
+def property_changed(name, value):
+ if (name == "Discovering" and not value):
+ mainloop.quit()
+
+if __name__ == '__main__':
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+ bus = dbus.SystemBus()
+ manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+ "org.bluez.Manager")
+
+ option_list = [
+ make_option("-i", "--device", action="store",
+ type="string", dest="dev_id"),
+ ]
+ parser = OptionParser(option_list=option_list)
+
+ (options, args) = parser.parse_args()
+
+ if options.dev_id:
+ adapter_path = manager.FindAdapter(options.dev_id)
+ else:
+ adapter_path = manager.DefaultAdapter()
+
+ adapter = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+ "org.bluez.Adapter")
+
+ bus.add_signal_receiver(device_found,
+ dbus_interface = "org.bluez.Adapter",
+ signal_name = "DeviceFound")
+
+ bus.add_signal_receiver(property_changed,
+ dbus_interface = "org.bluez.Adapter",
+ signal_name = "PropertyChanged")
+
+ adapter.StartDiscovery()
+
+ mainloop = gobject.MainLoop()
+ mainloop.run()
diff --git a/test/test-input b/test/test-input
new file mode 100755
index 0000000..405bb59
--- /dev/null
+++ b/test/test-input
@@ -0,0 +1,45 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+from optparse import OptionParser, make_option
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.bluez", "/"), "org.bluez.Manager")
+
+option_list = [
+ make_option("-i", "--device", action="store",
+ type="string", dest="dev_id"),
+ ]
+parser = OptionParser(option_list=option_list)
+
+(options, args) = parser.parse_args()
+
+if options.dev_id:
+ adapter_path = manager.FindAdapter(options.dev_id)
+else:
+ adapter_path = manager.DefaultAdapter()
+
+adapter = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+ "org.bluez.Adapter")
+
+if len(args) < 2:
+ print """Usage: %s <command>
+
+ connect <bdaddr>
+ disconnect <bdaddr>
+ """ % sys.argv[0]
+ sys.exit(1)
+
+device = adapter.FindDevice(args[1])
+input = dbus.Interface(bus.get_object("org.bluez", device),
+ "org.bluez.Input")
+
+if args[0] == "connect":
+ input.Connect()
+elif args[0] == "disconnect":
+ input.Disconnect()
+else:
+ print "Unknown command"
+ sys.exit(1)
diff --git a/test/test-manager b/test/test-manager
new file mode 100755
index 0000000..c6cf560
--- /dev/null
+++ b/test/test-manager
@@ -0,0 +1,38 @@
+#!/usr/bin/python
+
+import gobject
+
+import dbus
+import dbus.mainloop.glib
+
+def adapter_added(path):
+ print "Adapter with path %s added" % (path)
+
+def adapter_removed(path):
+ print "Adapter with path %s removed" % (path)
+
+def default_changed(path):
+ print "Default adapter is now at path %s" % (path)
+
+if __name__ == "__main__":
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+ bus = dbus.SystemBus()
+
+ manager = dbus.Interface(bus.get_object('org.bluez', '/'),
+ 'org.bluez.Manager')
+
+ manager.connect_to_signal("AdapterAdded", adapter_added)
+
+ manager.connect_to_signal("AdapterRemoved", adapter_removed)
+
+ manager.connect_to_signal("DefaultAdapterChanged", default_changed)
+
+ try:
+ path = manager.DefaultAdapter()
+ default_changed(path)
+ except:
+ pass
+
+ mainloop = gobject.MainLoop()
+ mainloop.run()
diff --git a/test/test-network b/test/test-network
new file mode 100755
index 0000000..676fb30
--- /dev/null
+++ b/test/test-network
@@ -0,0 +1,57 @@
+#!/usr/bin/python
+
+import sys
+import time
+import dbus
+from optparse import OptionParser, make_option
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+ "org.bluez.Manager")
+
+option_list = [
+ make_option("-i", "--device", action="store",
+ type="string", dest="dev_id"),
+ ]
+parser = OptionParser(option_list=option_list)
+
+(options, args) = parser.parse_args()
+
+if options.dev_id:
+ adapter_path = manager.FindAdapter(options.dev_id)
+else:
+ adapter_path = manager.DefaultAdapter()
+
+adapter = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+ "org.bluez.Adapter")
+
+if (len(args) < 1):
+ print "Usage: %s <address> [service]" % (sys.argv[0])
+ sys.exit(1)
+
+address = args[0]
+
+if (len(args) < 2):
+ service = "panu"
+else:
+ service = args[1]
+
+device = adapter.FindDevice(address)
+
+network = dbus.Interface(bus.get_object("org.bluez", device),
+ "org.bluez.Network")
+
+iface = network.Connect(service)
+
+print "Connected %s to %s" % (device, address)
+
+print "Press CTRL-C to disconnect"
+
+try:
+ time.sleep(1000)
+ print "Terminating connection"
+except:
+ pass
+
+network.Disconnect()
diff --git a/test/test-sap-server b/test/test-sap-server
new file mode 100755
index 0000000..bea6ca9
--- /dev/null
+++ b/test/test-sap-server
@@ -0,0 +1,138 @@
+#!/usr/bin/python
+
+from sap import *
+import time
+
+def connect_disconnect_by_client(sap):
+
+ print "[Test] Connect - Disconnect by client \n"
+
+ try:
+ if not sap.isConnected():
+ sap.connect()
+
+ if sap.proc_connect():
+ if sap.proc_disconnectByClient():
+ print "OK"
+ return 0
+
+ print "NOT OK"
+ return 1
+
+ except BluetoothError , e:
+ print "Error " + str(e)
+
+
+def connect_disconnect_by_server_gracefully(sap, timeout=0):
+
+ print "[Test] Connect - Disconnect by server with timer \n"
+
+ try:
+ if not sap.isConnected():
+ sap.connect()
+
+ if sap.proc_connect():
+ if sap.proc_disconnectByServer(timeout):
+ print "OK"
+ return 0
+
+ print "NOT OK"
+ return 1
+
+ except BluetoothError , e:
+ print "Error " + str(e)
+
+
+def connect_txAPDU_disconnect_by_client(sap):
+
+ print "[Test] Connect - TX APDU - Disconnect by client \n"
+
+ try:
+ if not sap.isConnected():
+ sap.connect()
+
+ if sap.proc_connect():
+ if not sap.proc_transferAPDU():
+ print "NOT OK 1"
+ return 1
+
+ if not sap.proc_transferAPDU():
+ print "NOT OK 2"
+ return 1
+
+ if not sap.proc_transferAPDU():
+ print "NOT OK 3"
+ return 1
+
+ if not sap.proc_transferAPDU():
+ print "NOT OK 4"
+ return 1
+
+ if sap.proc_disconnectByClient():
+ print "OK"
+ return 0
+
+ print "NOT OK"
+ return 1
+
+ except BluetoothError , e:
+ print "Error " + str(e)
+
+def connect_rfcomm_only_and_wait_for_close_by_server(sap):
+
+ print "[Test] Connect rfcomm only - Disconnect by server timeout \n"
+
+ if not sap.isConnected():
+ sap.connect()
+
+ time.sleep(40)
+ print "OK"
+
+def power_sim_off_on(sap):
+
+ print "[Test] Powe sim off \n"
+
+ try:
+ if not sap.isConnected():
+ sap.connect()
+
+ if sap.proc_connect():
+ if not sap.proc_resetSim():
+ print "NOT OK"
+ return 1
+
+ if not sap.proc_powerSimOff():
+ print "NOT OK"
+ return 1
+
+ if not sap.proc_powerSimOn():
+ print "NOT OK"
+ return 1
+
+ if sap.proc_disconnectByClient():
+ print "OK"
+ return 0
+
+ print "NOT OK"
+ return 1
+
+ except BluetoothError , e:
+ print "Error " + str(e)
+
+
+if __name__ == "__main__":
+
+ host = "00:00:00:00:00:0" # server bd_addr
+ port = 8 # sap server port
+
+ try:
+ s = SAPClient(host, port)
+ except BluetoothError , e:
+ print "Error " + str(e)
+
+ connect_disconnect_by_client(s)
+ connect_disconnect_by_server_gracefully(s)
+ connect_disconnect_by_server_gracefully(s, 40) # wait 40 sec for srv to close rfcomm sock
+ connect_rfcomm_only_and_wait_for_close_by_server(s)
+ connect_txAPDU_disconnect_by_client(s)
+ power_sim_off_on(s)
diff --git a/test/test-serial b/test/test-serial
new file mode 100755
index 0000000..cc496df
--- /dev/null
+++ b/test/test-serial
@@ -0,0 +1,56 @@
+#!/usr/bin/python
+
+import sys
+import time
+import dbus
+from optparse import OptionParser, make_option
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+ "org.bluez.Manager")
+option_list = [
+ make_option("-i", "--device", action="store",
+ type="string", dest="dev_id"),
+ ]
+parser = OptionParser(option_list=option_list)
+
+(options, args) = parser.parse_args()
+
+if options.dev_id:
+ adapter_path = manager.FindAdapter(options.dev_id)
+else:
+ adapter_path = manager.DefaultAdapter()
+
+adapter = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+ "org.bluez.Adapter")
+
+if (len(args) < 1):
+ print "Usage: %s <address> [service]" % (sys.argv[0])
+ sys.exit(1)
+
+address = args[0]
+
+if (len(args) < 2):
+ service = "spp"
+else:
+ service = args[1]
+
+path = adapter.FindDevice(address)
+
+serial = dbus.Interface(bus.get_object("org.bluez", path),
+ "org.bluez.Serial")
+
+node = serial.Connect(service)
+
+print "Connected %s to %s" % (node, address)
+
+print "Press CTRL-C to disconnect"
+
+try:
+ time.sleep(1000)
+ print "Terminating connection"
+except:
+ pass
+
+serial.Disconnect(node)
diff --git a/test/test-service b/test/test-service
new file mode 100755
index 0000000..8958201
--- /dev/null
+++ b/test/test-service
@@ -0,0 +1,47 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+import time
+from optparse import OptionParser, make_option
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.bluez", "/"), "org.bluez.Manager")
+
+option_list = [
+ make_option("-i", "--device", action="store",
+ type="string", dest="dev_id"),
+ ]
+parser = OptionParser(option_list=option_list)
+
+(options, args) = parser.parse_args()
+
+if options.dev_id:
+ adapter_path = manager.FindAdapter(options.dev_id)
+else:
+ adapter_path = manager.DefaultAdapter()
+
+service = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+ "org.bluez.Service")
+
+if (len(args) < 1):
+ print "Usage: %s <command>" % (sys.argv[0])
+ print ""
+ print " addrecord <file>"
+ sys.exit(1)
+
+if (args[0] == "addrecord"):
+ if (len(args) < 2):
+ print "Need file parameter"
+ else:
+ f = open(args[1])
+ record = f.read()
+ f.close()
+ handle = service.AddRecord(record)
+ print "0x%x" % (handle)
+ time.sleep(120)
+ sys.exit(0)
+
+print "Unknown command"
+sys.exit(1)
diff --git a/test/test-telephony b/test/test-telephony
new file mode 100755
index 0000000..5ef0ac8
--- /dev/null
+++ b/test/test-telephony
@@ -0,0 +1,176 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+from optparse import OptionParser, make_option
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.bluez", "/"), "org.bluez.Manager")
+
+option_list = [
+ make_option("-i", "--device", action="store",
+ type="string", dest="dev_id"),
+ ]
+parser = OptionParser(option_list=option_list)
+
+(options, args) = parser.parse_args()
+
+if options.dev_id:
+ adapter_path = manager.FindAdapter(options.dev_id)
+else:
+ adapter_path = manager.DefaultAdapter()
+
+adapter = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+ "org.bluez.Adapter")
+
+test = dbus.Interface(bus.get_object("org.bluez", "/org/bluez/test"),
+ "org.bluez.TelephonyTest")
+
+if len(args) < 1:
+ print """Usage: %s <command>
+
+ connect <bdaddr>
+ disconnect <bdaddr>
+ outgoing <number>
+ incoming <number>
+ cancel
+ signal <level>
+ battery <level>
+ roaming <yes|no>
+ registration <status>
+ subscriber <number>
+ speakergain <bdaddr> [level]
+ microphonegain <bdaddr> [level]
+ play <bdaddr>
+ stop <bdaddr>
+ """ % sys.argv[0]
+ sys.exit(1)
+
+if args[0] == "connect":
+ if len(args) < 2:
+ print "Need device address parameter"
+ sys.exit(1)
+ device = adapter.FindDevice(args[1])
+ headset = dbus.Interface(bus.get_object("org.bluez", device),
+ "org.bluez.Headset")
+ headset.Connect()
+ sys.exit(0)
+
+if args[0] == "disconnect":
+ if len(args) < 2:
+ print "Need device address parameter"
+ sys.exit(1)
+ device = adapter.FindDevice(args[1])
+ headset = dbus.Interface(bus.get_object("org.bluez", device),
+ "org.bluez.Headset")
+ headset.Disconnect()
+ sys.exit(0)
+
+if args[0] == "speakergain":
+ if len(args) < 2:
+ print "Need device address parameter"
+ sys.exit(1)
+ device = adapter.FindDevice(args[1])
+ headset = dbus.Interface(bus.get_object("org.bluez", device),
+ "org.bluez.Headset")
+ if len(args) > 2:
+ headset.SetProperty('SpeakerGain', dbus.UInt16(args[2]))
+ else:
+ props = headset.GetProperties()
+ print props['SpeakerGain']
+
+ sys.exit(0)
+
+if args[0] == "microphonegain":
+ if len(args) < 2:
+ print "Need device address parameter"
+ sys.exit(1)
+ device = adapter.FindDevice(args[1])
+ headset = dbus.Interface(bus.get_object("org.bluez", device),
+ "org.bluez.Headset")
+ if len(args) > 2:
+ headset.SetProperty('MicrophoneGain', dbus.UInt16(args[2]))
+ else:
+ props = headset.GetProperties()
+ print props['MicrophoneGain']
+
+ sys.exit(0)
+
+if args[0] == "play":
+ if len(args) < 2:
+ print "Need device address parameter"
+ sys.exit(1)
+ device = adapter.FindDevice(args[1])
+ headset = dbus.Interface(bus.get_object("org.bluez", device),
+ "org.bluez.Headset")
+ headset.Play()
+
+ sys.exit(0)
+
+if args[0] == "stop":
+ if len(args) < 2:
+ print "Need device address parameter"
+ sys.exit(1)
+ device = adapter.FindDevice(args[1])
+ headset = dbus.Interface(bus.get_object("org.bluez", device),
+ "org.bluez.Headset")
+ headset.Stop()
+
+ sys.exit(0)
+
+if args[0] == "outgoing":
+ if len(args) > 1:
+ test.OutgoingCall(args[1])
+ else:
+ print "Need number parameter"
+ sys.exit(0)
+
+if args[0] == "incoming":
+ if len(args) > 1:
+ test.IncomingCall(args[1])
+ else:
+ print "Need number parameter"
+ sys.exit(0)
+
+if args[0] == "cancel":
+ test.CancelCall()
+ sys.exit(0)
+
+if args[0] == "signal":
+ if len(args) > 1:
+ test.SignalStrength(args[1])
+ else:
+ print "Need signal strength parameter"
+ sys.exit(0)
+
+if args[0] == "battery":
+ if len(args) > 1:
+ test.BatteryLevel(args[1])
+ else:
+ print "Need battery level parameter"
+ sys.exit(0)
+
+if args[0] == "roaming":
+ if len(args) > 1:
+ test.RoamingStatus(args[1] == "yes" or False)
+ else:
+ print "Need yes/no parameter"
+ sys.exit(0)
+
+if args[0] == "registration":
+ if len(args) > 1:
+ test.RegistrationStatus(args[1] == "yes" or False)
+ else:
+ print "Need yes/no parameter"
+ sys.exit(0)
+
+if args[0] == "subscriber":
+ if len(args) > 1:
+ test.SetSubscriberNumber(args[1])
+ else:
+ print "Need number parameter"
+ sys.exit(0)
+
+print "Unknown command"
+sys.exit(1)
diff --git a/test/test-textfile.c b/test/test-textfile.c
new file mode 100644
index 0000000..970e9e7
--- /dev/null
+++ b/test/test-textfile.c
@@ -0,0 +1,188 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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 "textfile.h"
+
+static void print_entry(char *key, char *value, void *data)
+{
+ printf("%s %s\n", key, value);
+}
+
+int main(int argc, char *argv[])
+{
+ char filename[] = "/tmp/textfile";
+ char key[18], value[512], *str;
+ unsigned int i, j, size, max = 10;
+ int fd, err;
+
+ size = getpagesize();
+ printf("System uses a page size of %d bytes\n\n", size);
+
+ fd = creat(filename, 0644);
+ err = ftruncate(fd, 0);
+
+ memset(value, 0, sizeof(value));
+ for (i = 0; i < (size / sizeof(value)); i++)
+ err = write(fd, value, sizeof(value));
+
+ close(fd);
+
+ sprintf(key, "11:11:11:11:11:11");
+ str = textfile_get(filename, key);
+
+ err = truncate(filename, 0);
+
+
+ sprintf(key, "00:00:00:00:00:00");
+ if (textfile_del(filename, key) < 0)
+ fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+ memset(value, 0, sizeof(value));
+ if (textfile_put(filename, key, value) < 0)
+ fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+ str = textfile_get(filename, key);
+ if (!str)
+ fprintf(stderr, "No value for %s\n", key);
+ else
+ free(str);
+
+ snprintf(value, sizeof(value), "Test");
+ if (textfile_put(filename, key, value) < 0)
+ fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+ if (textfile_put(filename, key, value) < 0)
+ fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+ if (textfile_put(filename, key, value) < 0)
+ fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+ if (textfile_del(filename, key) < 0)
+ fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+ str = textfile_get(filename, key);
+ if (str) {
+ fprintf(stderr, "Found value for %s\n", key);
+ free(str);
+ }
+
+ for (i = 1; i < max + 1; i++) {
+ sprintf(key, "00:00:00:00:00:%02X", i);
+
+ memset(value, 0, sizeof(value));
+ for (j = 0; j < i; j++)
+ value[j] = 'x';
+
+ printf("%s %s\n", key, value);
+
+ if (textfile_put(filename, key, value) < 0) {
+ fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+ break;
+ }
+
+ str = textfile_get(filename, key);
+ if (!str)
+ fprintf(stderr, "No value for %s\n", key);
+ else
+ free(str);
+ }
+
+
+ sprintf(key, "00:00:00:00:00:%02X", max);
+
+ memset(value, 0, sizeof(value));
+ for (j = 0; j < max; j++)
+ value[j] = 'y';
+
+ if (textfile_put(filename, key, value) < 0)
+ fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+ sprintf(key, "00:00:00:00:00:%02X", 1);
+
+ memset(value, 0, sizeof(value));
+ for (j = 0; j < max; j++)
+ value[j] = 'z';
+
+ if (textfile_put(filename, key, value) < 0)
+ fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+ printf("\n");
+
+ for (i = 1; i < max + 1; i++) {
+ sprintf(key, "00:00:00:00:00:%02X", i);
+
+ str = textfile_get(filename, key);
+ if (str) {
+ printf("%s %s\n", key, str);
+ free(str);
+ }
+ }
+
+
+ sprintf(key, "00:00:00:00:00:%02X", 2);
+
+ if (textfile_del(filename, key) < 0)
+ fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+ sprintf(key, "00:00:00:00:00:%02X", max - 3);
+
+ if (textfile_del(filename, key) < 0)
+ fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+ printf("\n");
+
+ textfile_foreach(filename, print_entry, NULL);
+
+
+ sprintf(key, "00:00:00:00:00:%02X", 1);
+
+ if (textfile_del(filename, key) < 0)
+ fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+ sprintf(key, "00:00:00:00:00:%02X", max);
+
+ if (textfile_del(filename, key) < 0)
+ fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+ sprintf(key, "00:00:00:00:00:%02X", max + 1);
+
+ if (textfile_del(filename, key) < 0)
+ fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+ printf("\n");
+
+ textfile_foreach(filename, print_entry, NULL);
+
+ return 0;
+}
diff --git a/test/uuidtest.c b/test/uuidtest.c
new file mode 100644
index 0000000..a8b46d7
--- /dev/null
+++ b/test/uuidtest.c
@@ -0,0 +1,319 @@
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/uuid.h>
+
+const char *base = "00000000-0000-1000-8000-00805F9B34FB";
+
+uint8_t xbase[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb};
+
+uint16_t sixteen = 0x1234;
+const char *uuidsixteen128 = "00001234-0000-1000-8000-00805F9B34FB";
+const char *uuidsixteen16 = "0x1234";
+const char *uuidsixteen16a = "1234";
+
+uint8_t xuuidsixteen[] = {0x00, 0x00, 0x12, 0x34, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb};
+
+uint32_t thirtytwo = 0x12345678;
+const char *uuidthirtytwo32 = "0x12345678";
+const char *uuidthirtytwo32a = "12345678";
+const char *uuidthirtytwo128 = "12345678-0000-1000-8000-00805F9B34FB";
+
+uint8_t xuuidthirtytwo[] = {0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb};
+
+const char *malformed[] = {
+ "0",
+ "01",
+ "012",
+ "xxxx",
+ "xxxxx",
+ "0xxxxx",
+ "0123456",
+ "012g4567",
+ "012345678",
+ "0x234567u9",
+ "01234567890",
+ "00001234-0000-1000-8000-00805F9B34F",
+ "00001234-0000-1000-8000 00805F9B34FB",
+ "00001234-0000-1000-8000-00805F9B34FBC",
+ "00001234-0000-1000-800G-00805F9B34FB",
+ NULL,
+ };
+
+int main(int argc, char *argv[])
+{
+ bt_uuid_t u, u2, u3, u4, u5, ub, u128;
+ uint128_t n, i;
+ char buf[512];
+ int s;
+
+ memcpy(&n, xbase, 16);
+ ntoh128(&n, &i);
+
+ if (bt_string_to_uuid(&u, base)) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (bt_string_to_uuid(&ub, base)) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (u.type != 128) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (ub.type != 128) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (memcmp(&u.value.u128, &i, 16) != 0) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (memcmp(&ub.value.u128, &i, 16) != 0) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (memcmp(&ub.value.u128, &u.value.u128, 16) != 0) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (bt_uuid_cmp(&u, &ub) != 0) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ bt_uuid_to_string(&u, buf, sizeof(buf));
+ /* printf("%s\n", buf); */
+
+ if (strcasecmp(buf, base) != 0) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ memcpy(&n, xuuidsixteen, 16);
+ ntoh128(&n, &i);
+
+ bt_uuid16_create(&u, sixteen);
+ bt_uuid_to_uuid128(&u, &u128);
+
+ if (bt_string_to_uuid(&u2, uuidsixteen16)) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (bt_string_to_uuid(&u3, uuidsixteen16a)) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (bt_string_to_uuid(&u4, uuidsixteen128)) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ bt_uuid128_create(&u5, i);
+
+ if (u.type != 16) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (u128.type != 128) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (u.value.u16 != sixteen) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (u2.type != 16) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (u3.type != 16) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (u4.type != 128) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (u5.type != 128) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (bt_uuid_cmp(&u, &u2) != 0) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (bt_uuid_cmp(&u2, &u3) != 0) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (bt_uuid_cmp(&u, &u3) != 0) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (bt_uuid_cmp(&u3, &u4) != 0) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (bt_uuid_cmp(&u4, &u5) != 0) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (bt_uuid_cmp(&u5, &u) != 0) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (bt_uuid_cmp(&u, &ub) == 0) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (bt_uuid_cmp(&u5, &ub) == 0) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (bt_uuid_cmp(&u, &u128) != 0) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (bt_uuid_cmp(&ub, &u128) == 0) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ memcpy(&n, xuuidthirtytwo, 16);
+ ntoh128(&n, &i);
+
+ bt_uuid32_create(&u, thirtytwo);
+ bt_uuid_to_uuid128(&u, &u128);
+ bt_string_to_uuid(&u2, uuidthirtytwo32);
+ bt_string_to_uuid(&u3, uuidthirtytwo32a);
+ bt_string_to_uuid(&u4, uuidthirtytwo128);
+ bt_uuid128_create(&u5, i);
+
+ /*
+ bt_uuid_to_string(&u2, buf, sizeof(buf));
+ printf("%s\n", buf);
+
+ bt_uuid_to_string(&u3, buf, sizeof(buf));
+ printf("%s\n", buf);
+
+ bt_uuid_to_string(&u4, buf, sizeof(buf));
+ printf("%s\n", buf);
+ */
+
+ if (u.type != 32) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (u128.type != 128) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (u.value.u32 != thirtytwo) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (u2.type != 32) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (u3.type != 32) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (u4.type != 128) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (u5.type != 128) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (bt_uuid_cmp(&u, &u2) != 0) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (bt_uuid_cmp(&u2, &u3) != 0) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (bt_uuid_cmp(&u3, &u4) != 0) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (bt_uuid_cmp(&u4, &u5) != 0) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (bt_uuid_cmp(&u5, &u) != 0) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (bt_uuid_cmp(&u, &ub) == 0) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (bt_uuid_cmp(&u5, &ub) == 0) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (bt_uuid_cmp(&u, &u128) != 0) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ if (bt_uuid_cmp(&ub, &u128) == 0) {
+ printf("Fail %d\n", __LINE__);
+ return 1;
+ }
+
+ for (s = 0; malformed[s]; ++s) {
+ if (bt_string_to_uuid(&u3, malformed[s]) == 0) {
+ printf("Fail %s %d\n", malformed[s], __LINE__);
+ return 1;
+ }
+ }
+
+ return 0;
+}
diff --git a/tools/avctrl.8 b/tools/avctrl.8
new file mode 100644
index 0000000..7c3759d
--- /dev/null
+++ b/tools/avctrl.8
@@ -0,0 +1,39 @@
+.\"
+.\" 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.TH AVCTRL 8 "JUNE 6, 2005" "" ""
+
+.SH NAME
+avctrl \- Bluetooth Audio/Video control utility
+.SH SYNOPSIS
+.BR "avctrl
+[
+.I options
+]
+<command>
+.SH DESCRIPTION
+.B avctrl
+is used to control the Audio/Video dongles.
+.SH OPTIONS
+.TP
+.BI -h
+Gives a list of possible options.
+.TP
+.BI -q
+Don't display any messages.
+.SH AUTHOR
+Written by Marcel Holtmann <marcel@holtmann.org>.
+.br
diff --git a/tools/avctrl.c b/tools/avctrl.c
new file mode 100644
index 0000000..3621a68
--- /dev/null
+++ b/tools/avctrl.c
@@ -0,0 +1,248 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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 <stdint.h>
+#include <string.h>
+#include <getopt.h>
+
+#include <usb.h>
+
+#ifdef NEED_USB_GET_BUSSES
+static inline struct usb_bus *usb_get_busses(void)
+{
+ return usb_busses;
+}
+#endif
+
+#ifndef USB_DIR_OUT
+#define USB_DIR_OUT 0x00
+#endif
+
+#ifndef USB_DIR_IN
+#define USB_DIR_IN 0x80
+#endif
+
+#define HID_REQ_GET_REPORT 0x01
+#define HID_REQ_GET_IDLE 0x02
+#define HID_REQ_GET_PROTOCOL 0x03
+#define HID_REQ_SET_REPORT 0x09
+#define HID_REQ_SET_IDLE 0x0a
+#define HID_REQ_SET_PROTOCOL 0x0b
+
+struct device_info;
+
+struct device_id {
+ uint16_t vendor;
+ uint16_t product;
+ int (*func)(struct device_info *dev, int argc, char *argv[]);
+};
+
+struct device_info {
+ struct usb_device *dev;
+ struct device_id *id;
+};
+
+#define GET_STATE 0x01
+#define GET_REMOTE_BDADDR 0x02
+#define DISCOVER 0x03
+#define SWITCH_TO_DFU 0x04
+#define READ_CODEC 0x05
+
+static int dongle_csr(struct device_info *devinfo, int argc, char *argv[])
+{
+ char buf[8];
+ struct usb_dev_handle *udev;
+ int err, intf = 2;
+
+ memset(buf, 0, sizeof(buf));
+
+ if (!strncasecmp(argv[0], "discover", 4))
+ buf[0] = DISCOVER;
+ else if (!strncasecmp(argv[0], "switch", 3))
+ buf[0] = SWITCH_TO_DFU;
+ else if (!strncasecmp(argv[0], "dfu", 3))
+ buf[0] = SWITCH_TO_DFU;
+ else
+ return -EINVAL;
+
+ udev = usb_open(devinfo->dev);
+ if (!udev)
+ return -errno;
+
+ if (usb_claim_interface(udev, intf) < 0) {
+ err = -errno;
+ usb_close(udev);
+ return err;
+ }
+
+ err = usb_control_msg(udev, USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ HID_REQ_SET_REPORT, 0x03 << 8, intf, buf, sizeof(buf), 10000);
+
+ if (err == 0) {
+ err = -1;
+ errno = EALREADY;
+ } else {
+ if (errno == ETIMEDOUT)
+ err = 0;
+ }
+
+ usb_release_interface(udev, intf);
+ usb_close(udev);
+
+ return err;
+}
+
+static struct device_id device_list[] = {
+ { 0x0a12, 0x1004, dongle_csr },
+ { -1 }
+};
+
+static struct device_id *match_device(uint16_t vendor, uint16_t product)
+{
+ int i;
+
+ for (i = 0; device_list[i].func; i++) {
+ if (vendor == device_list[i].vendor &&
+ product == device_list[i].product)
+ return &device_list[i];
+ }
+
+ return NULL;
+}
+
+static int find_devices(struct device_info *devinfo, size_t size)
+{
+ struct usb_bus *bus;
+ struct usb_device *dev;
+ struct device_id *id;
+ unsigned int count = 0;
+
+ usb_find_busses();
+ usb_find_devices();
+
+ for (bus = usb_get_busses(); bus; bus = bus->next)
+ for (dev = bus->devices; dev; dev = dev->next) {
+ id = match_device(dev->descriptor.idVendor,
+ dev->descriptor.idProduct);
+ if (!id)
+ continue;
+
+ if (count < size) {
+ devinfo[count].dev = dev;
+ devinfo[count].id = id;
+ count++;
+ }
+ }
+
+ return count;
+}
+
+static void usage(void)
+{
+ printf("avctrl - Bluetooth Audio/Video control utility\n\n");
+
+ printf("Usage:\n"
+ "\tavctrl [options] <command>\n"
+ "\n");
+
+ printf("Options:\n"
+ "\t-h, --help Display help\n"
+ "\t-q, --quiet Don't display any messages\n"
+ "\n");
+
+ printf("Commands:\n"
+ "\tdiscover Simulate pressing the discover button\n"
+ "\tswitch Switch the dongle to DFU mode\n"
+ "\n");
+}
+
+static struct option main_options[] = {
+ { "help", 0, 0, 'h' },
+ { "quiet", 0, 0, 'q' },
+ { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ struct device_info dev[16];
+ int i, opt, num, quiet = 0;
+
+ while ((opt = getopt_long(argc, argv, "+qh", main_options, NULL)) != -1) {
+ switch (opt) {
+ case 'q':
+ quiet = 1;
+ break;
+ case 'h':
+ usage();
+ exit(0);
+ default:
+ exit(0);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ if (argc < 1) {
+ usage();
+ exit(1);
+ }
+
+ usb_init();
+
+ num = find_devices(dev, sizeof(dev) / sizeof(dev[0]));
+ if (num <= 0) {
+ if (!quiet)
+ fprintf(stderr, "No Audio/Video devices found\n");
+ exit(1);
+ }
+
+ for (i = 0; i < num; i++) {
+ struct device_id *id = dev[i].id;
+ int err;
+
+ if (!quiet)
+ printf("Selecting device %04x:%04x ",
+ id->vendor, id->product);
+ fflush(stdout);
+
+ err = id->func(&dev[i], argc, argv);
+ if (err < 0) {
+ if (!quiet)
+ printf("failed (%s)\n", strerror(-err));
+ } else {
+ if (!quiet)
+ printf("was successful\n");
+ }
+ }
+
+ return 0;
+}
diff --git a/tools/avinfo.c b/tools/avinfo.c
new file mode 100644
index 0000000..7f76c03
--- /dev/null
+++ b/tools/avinfo.c
@@ -0,0 +1,672 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2004-2010 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 <string.h>
+#include <getopt.h>
+#include <stdint.h>
+#include <sys/param.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 AVDTP_PSM 25
+
+/* Commands */
+#define AVDTP_DISCOVER 0x01
+#define AVDTP_GET_CAPABILITIES 0x02
+
+#define AVDTP_PKT_TYPE_SINGLE 0x00
+
+#define AVDTP_MSG_TYPE_COMMAND 0x00
+
+/* SEP capability categories */
+#define AVDTP_MEDIA_TRANSPORT 0x01
+#define AVDTP_REPORTING 0x02
+#define AVDTP_RECOVERY 0x03
+#define AVDTP_CONTENT_PROTECTION 0x04
+#define AVDTP_HEADER_COMPRESSION 0x05
+#define AVDTP_MULTIPLEXING 0x06
+#define AVDTP_MEDIA_CODEC 0x07
+
+/* SEP types definitions */
+#define AVDTP_SEP_TYPE_SOURCE 0x00
+#define AVDTP_SEP_TYPE_SINK 0x01
+
+/* Media types definitions */
+#define AVDTP_MEDIA_TYPE_AUDIO 0x00
+#define AVDTP_MEDIA_TYPE_VIDEO 0x01
+#define AVDTP_MEDIA_TYPE_MULTIMEDIA 0x02
+
+#define A2DP_CODEC_SBC 0x00
+#define A2DP_CODEC_MPEG12 0x01
+#define A2DP_CODEC_MPEG24 0x02
+#define A2DP_CODEC_ATRAC 0x03
+
+#define SBC_SAMPLING_FREQ_16000 (1 << 3)
+#define SBC_SAMPLING_FREQ_32000 (1 << 2)
+#define SBC_SAMPLING_FREQ_44100 (1 << 1)
+#define SBC_SAMPLING_FREQ_48000 (1 << 0)
+
+#define SBC_CHANNEL_MODE_MONO (1 << 3)
+#define SBC_CHANNEL_MODE_DUAL_CHANNEL (1 << 2)
+#define SBC_CHANNEL_MODE_STEREO (1 << 1)
+#define SBC_CHANNEL_MODE_JOINT_STEREO (1 << 0)
+
+#define SBC_BLOCK_LENGTH_4 (1 << 3)
+#define SBC_BLOCK_LENGTH_8 (1 << 2)
+#define SBC_BLOCK_LENGTH_12 (1 << 1)
+#define SBC_BLOCK_LENGTH_16 (1 << 0)
+
+#define SBC_SUBBANDS_4 (1 << 1)
+#define SBC_SUBBANDS_8 (1 << 0)
+
+#define SBC_ALLOCATION_SNR (1 << 1)
+#define SBC_ALLOCATION_LOUDNESS (1 << 0)
+
+#define MPEG_CHANNEL_MODE_MONO (1 << 3)
+#define MPEG_CHANNEL_MODE_DUAL_CHANNEL (1 << 2)
+#define MPEG_CHANNEL_MODE_STEREO (1 << 1)
+#define MPEG_CHANNEL_MODE_JOINT_STEREO (1 << 0)
+
+#define MPEG_LAYER_MP1 (1 << 2)
+#define MPEG_LAYER_MP2 (1 << 1)
+#define MPEG_LAYER_MP3 (1 << 0)
+
+#define MPEG_SAMPLING_FREQ_16000 (1 << 5)
+#define MPEG_SAMPLING_FREQ_22050 (1 << 4)
+#define MPEG_SAMPLING_FREQ_24000 (1 << 3)
+#define MPEG_SAMPLING_FREQ_32000 (1 << 2)
+#define MPEG_SAMPLING_FREQ_44100 (1 << 1)
+#define MPEG_SAMPLING_FREQ_48000 (1 << 0)
+
+#define MPEG_BIT_RATE_VBR 0x8000
+#define MPEG_BIT_RATE_320000 0x4000
+#define MPEG_BIT_RATE_256000 0x2000
+#define MPEG_BIT_RATE_224000 0x1000
+#define MPEG_BIT_RATE_192000 0x0800
+#define MPEG_BIT_RATE_160000 0x0400
+#define MPEG_BIT_RATE_128000 0x0200
+#define MPEG_BIT_RATE_112000 0x0100
+#define MPEG_BIT_RATE_96000 0x0080
+#define MPEG_BIT_RATE_80000 0x0040
+#define MPEG_BIT_RATE_64000 0x0020
+#define MPEG_BIT_RATE_56000 0x0010
+#define MPEG_BIT_RATE_48000 0x0008
+#define MPEG_BIT_RATE_40000 0x0004
+#define MPEG_BIT_RATE_32000 0x0002
+#define MPEG_BIT_RATE_FREE 0x0001
+
+struct avdtp_service_capability {
+ uint8_t category;
+ uint8_t length;
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avdtp_header {
+ uint8_t message_type:2;
+ uint8_t packet_type:2;
+ uint8_t transaction:4;
+ uint8_t signal_id:6;
+ uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct seid_info {
+ uint8_t rfa0:1;
+ uint8_t inuse:1;
+ uint8_t seid:6;
+ uint8_t rfa2:3;
+ uint8_t type:1;
+ uint8_t media_type:4;
+} __attribute__ ((packed));
+
+struct seid_req {
+ struct avdtp_header header;
+ uint8_t rfa0:2;
+ uint8_t acp_seid:6;
+} __attribute__ ((packed));
+
+struct avdtp_media_codec_capability {
+ uint8_t rfa0:4;
+ uint8_t media_type:4;
+ uint8_t media_codec_type;
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+struct sbc_codec_cap {
+ struct avdtp_media_codec_capability cap;
+ uint8_t channel_mode:4;
+ uint8_t frequency:4;
+ uint8_t allocation_method:2;
+ uint8_t subbands:2;
+ uint8_t block_length:4;
+ uint8_t min_bitpool;
+ uint8_t max_bitpool;
+} __attribute__ ((packed));
+
+struct mpeg_codec_cap {
+ struct avdtp_media_codec_capability cap;
+ uint8_t channel_mode:4;
+ uint8_t crc:1;
+ uint8_t layer:3;
+ uint8_t frequency:6;
+ uint8_t mpf:1;
+ uint8_t rfa:1;
+ uint16_t bitrate;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct avdtp_header {
+ uint8_t transaction:4;
+ uint8_t packet_type:2;
+ uint8_t message_type:2;
+ uint8_t rfa0:2;
+ uint8_t signal_id:6;
+} __attribute__ ((packed));
+
+struct seid_info {
+ uint8_t seid:6;
+ uint8_t inuse:1;
+ uint8_t rfa0:1;
+ uint8_t media_type:4;
+ uint8_t type:1;
+ uint8_t rfa2:3;
+} __attribute__ ((packed));
+
+struct seid_req {
+ struct avdtp_header header;
+ uint8_t acp_seid:6;
+ uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct avdtp_media_codec_capability {
+ uint8_t media_type:4;
+ uint8_t rfa0:4;
+ uint8_t media_codec_type;
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+struct sbc_codec_cap {
+ struct avdtp_media_codec_capability cap;
+ uint8_t frequency:4;
+ uint8_t channel_mode:4;
+ uint8_t block_length:4;
+ uint8_t subbands:2;
+ uint8_t allocation_method:2;
+ uint8_t min_bitpool;
+ uint8_t max_bitpool;
+} __attribute__ ((packed));
+
+struct mpeg_codec_cap {
+ struct avdtp_media_codec_capability cap;
+ uint8_t layer:3;
+ uint8_t crc:1;
+ uint8_t channel_mode:4;
+ uint8_t rfa:1;
+ uint8_t mpf:1;
+ uint8_t frequency:6;
+ uint16_t bitrate;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+struct discover_resp {
+ struct avdtp_header header;
+ struct seid_info seps[0];
+} __attribute__ ((packed));
+
+struct getcap_resp {
+ struct avdtp_header header;
+ uint8_t caps[0];
+} __attribute__ ((packed));
+
+
+static void print_mpeg12(struct mpeg_codec_cap *mpeg)
+{
+ printf("\tMedia Codec: MPEG12\n\t\tChannel Modes: ");
+
+ if (mpeg->channel_mode & MPEG_CHANNEL_MODE_MONO)
+ printf("Mono ");
+ if (mpeg->channel_mode & MPEG_CHANNEL_MODE_DUAL_CHANNEL)
+ printf("DualChannel ");
+ if (mpeg->channel_mode & MPEG_CHANNEL_MODE_STEREO)
+ printf("Stereo ");
+ if (mpeg->channel_mode & MPEG_CHANNEL_MODE_JOINT_STEREO)
+ printf("JointStereo");
+
+ printf("\n\t\tFrequencies: ");
+ if (mpeg->frequency & MPEG_SAMPLING_FREQ_16000)
+ printf("16Khz ");
+ if (mpeg->frequency & MPEG_SAMPLING_FREQ_22050)
+ printf("22.05Khz ");
+ if (mpeg->frequency & MPEG_SAMPLING_FREQ_24000)
+ printf("24Khz ");
+ if (mpeg->frequency & MPEG_SAMPLING_FREQ_32000)
+ printf("32Khz ");
+ if (mpeg->frequency & MPEG_SAMPLING_FREQ_44100)
+ printf("44.1Khz ");
+ if (mpeg->frequency & MPEG_SAMPLING_FREQ_48000)
+ printf("48Khz ");
+
+ printf("\n\t\tCRC: %s", mpeg->crc ? "Yes" : "No");
+
+ printf("\n\t\tLayer: ");
+ if (mpeg->layer & MPEG_LAYER_MP1)
+ printf("1 ");
+ if (mpeg->layer & MPEG_LAYER_MP2)
+ printf("2 ");
+ if (mpeg->layer & MPEG_LAYER_MP3)
+ printf("3 ");
+
+ printf("\n\t\tBit Rate: ");
+ if (mpeg->bitrate & MPEG_BIT_RATE_FREE)
+ printf("Free format");
+ else {
+ if (mpeg->bitrate & MPEG_BIT_RATE_32000)
+ printf("32kbps ");
+ if (mpeg->bitrate & MPEG_BIT_RATE_40000)
+ printf("40kbps ");
+ if (mpeg->bitrate & MPEG_BIT_RATE_48000)
+ printf("48kbps ");
+ if (mpeg->bitrate & MPEG_BIT_RATE_56000)
+ printf("56kbps ");
+ if (mpeg->bitrate & MPEG_BIT_RATE_64000)
+ printf("64kbps ");
+ if (mpeg->bitrate & MPEG_BIT_RATE_80000)
+ printf("80kbps ");
+ if (mpeg->bitrate & MPEG_BIT_RATE_96000)
+ printf("96kbps ");
+ if (mpeg->bitrate & MPEG_BIT_RATE_112000)
+ printf("112kbps ");
+ if (mpeg->bitrate & MPEG_BIT_RATE_128000)
+ printf("128kbps ");
+ if (mpeg->bitrate & MPEG_BIT_RATE_160000)
+ printf("160kbps ");
+ if (mpeg->bitrate & MPEG_BIT_RATE_192000)
+ printf("192kbps ");
+ if (mpeg->bitrate & MPEG_BIT_RATE_224000)
+ printf("224kbps ");
+ if (mpeg->bitrate & MPEG_BIT_RATE_256000)
+ printf("256kbps ");
+ if (mpeg->bitrate & MPEG_BIT_RATE_320000)
+ printf("320kbps ");
+ }
+
+ printf("\n\t\tVBR: %s", mpeg->bitrate & MPEG_BIT_RATE_VBR ? "Yes" :
+ "No");
+
+ printf("\n\t\tPayload Format: ");
+ if (mpeg->mpf)
+ printf("RFC-2250 RFC-3119\n");
+ else
+ printf("RFC-2250\n");
+}
+
+static void print_sbc(struct sbc_codec_cap *sbc)
+{
+ printf("\tMedia Codec: SBC\n\t\tChannel Modes: ");
+
+ if (sbc->channel_mode & SBC_CHANNEL_MODE_MONO)
+ printf("Mono ");
+ if (sbc->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL)
+ printf("DualChannel ");
+ if (sbc->channel_mode & SBC_CHANNEL_MODE_STEREO)
+ printf("Stereo ");
+ if (sbc->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO)
+ printf("JointStereo");
+
+ printf("\n\t\tFrequencies: ");
+ if (sbc->frequency & SBC_SAMPLING_FREQ_16000)
+ printf("16Khz ");
+ if (sbc->frequency & SBC_SAMPLING_FREQ_32000)
+ printf("32Khz ");
+ if (sbc->frequency & SBC_SAMPLING_FREQ_44100)
+ printf("44.1Khz ");
+ if (sbc->frequency & SBC_SAMPLING_FREQ_48000)
+ printf("48Khz ");
+
+ printf("\n\t\tSubbands: ");
+ if (sbc->allocation_method & SBC_SUBBANDS_4)
+ printf("4 ");
+ if (sbc->allocation_method & SBC_SUBBANDS_8)
+ printf("8");
+
+ printf("\n\t\tBlocks: ");
+ if (sbc->block_length & SBC_BLOCK_LENGTH_4)
+ printf("4 ");
+ if (sbc->block_length & SBC_BLOCK_LENGTH_8)
+ printf("8 ");
+ if (sbc->block_length & SBC_BLOCK_LENGTH_12)
+ printf("12 ");
+ if (sbc->block_length & SBC_BLOCK_LENGTH_16)
+ printf("16 ");
+
+ printf("\n\t\tBitpool Range: %d-%d\n",
+ sbc->min_bitpool, sbc->max_bitpool);
+}
+
+static void print_media_codec(struct avdtp_media_codec_capability *cap)
+{
+ switch (cap->media_codec_type) {
+ case A2DP_CODEC_SBC:
+ print_sbc((void *) cap);
+ break;
+ case A2DP_CODEC_MPEG12:
+ print_mpeg12((void *) cap);
+ break;
+ default:
+ printf("\tMedia Codec: Unknown\n");
+ }
+}
+
+static void print_caps(void *data, int size)
+{
+ int processed;
+
+ for (processed = 0; processed + 2 < size;) {
+ struct avdtp_service_capability *cap;
+
+ cap = data;
+
+ if (processed + 2 + cap->length > size) {
+ printf("Invalid capability data in getcap resp\n");
+ break;
+ }
+
+ switch (cap->category) {
+ case AVDTP_MEDIA_TRANSPORT:
+ case AVDTP_REPORTING:
+ case AVDTP_RECOVERY:
+ case AVDTP_CONTENT_PROTECTION:
+ case AVDTP_MULTIPLEXING:
+ /* FIXME: Add proper functions */
+ break;
+ case AVDTP_MEDIA_CODEC:
+ print_media_codec((void *) cap->data);
+ break;
+ }
+
+ processed += 2 + cap->length;
+ data += 2 + cap->length;
+ }
+}
+
+static void init_request(struct avdtp_header *header, int request_id)
+{
+ static int transaction = 0;
+
+ header->packet_type = AVDTP_PKT_TYPE_SINGLE;
+ header->message_type = AVDTP_MSG_TYPE_COMMAND;
+ header->transaction = transaction;
+ header->signal_id = request_id;
+
+ /* clear rfa bits */
+ header->rfa0 = 0;
+
+ transaction = (transaction + 1) % 16;
+}
+
+static ssize_t avdtp_send(int sk, void *data, int len)
+{
+ ssize_t ret;
+
+ ret = send(sk, data, len, 0);
+
+ if (ret < 0)
+ ret = -errno;
+ else if (ret != len)
+ ret = -EIO;
+
+ if (ret < 0) {
+ printf("Unable to send message: %s (%zd)\n",
+ strerror(-ret), -ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static ssize_t avdtp_receive(int sk, void *data, int len)
+{
+ ssize_t ret;
+
+ ret = recv(sk, data, len, 0);
+
+ if (ret < 0) {
+ printf("Unable to receive message: %s (%d)\n",
+ strerror(errno), errno);
+ return -errno;
+ }
+
+ return ret;
+}
+
+static ssize_t avdtp_get_caps(int sk, int seid)
+{
+ struct seid_req req;
+ char buffer[1024];
+ struct getcap_resp *caps = (void *) buffer;
+ ssize_t ret;
+
+ memset(&req, 0, sizeof(req));
+ init_request(&req.header, AVDTP_GET_CAPABILITIES);
+ req.acp_seid = seid;
+
+ ret = avdtp_send(sk, &req, sizeof(req));
+ if (ret < 0)
+ return ret;
+
+ memset(&buffer, 0, sizeof(buffer));
+ ret = avdtp_receive(sk, caps, sizeof(buffer));
+ if (ret < 0)
+ return ret;
+
+ if ((size_t) ret < (sizeof(struct getcap_resp) + 4 +
+ sizeof(struct avdtp_media_codec_capability))) {
+ printf("Invalid capabilities\n");
+ return -1;
+ }
+
+ print_caps(caps, ret);
+
+ return 0;
+}
+
+static ssize_t avdtp_discover(int sk)
+{
+ struct avdtp_header req;
+ char buffer[256];
+ struct discover_resp *discover = (void *) buffer;
+ int seps, i;
+ ssize_t ret;
+
+ memset(&req, 0, sizeof(req));
+ init_request(&req, AVDTP_DISCOVER);
+
+ ret = avdtp_send(sk, &req, sizeof(req));
+ if (ret < 0)
+ return ret;
+
+ memset(&buffer, 0, sizeof(buffer));
+ ret = avdtp_receive(sk, discover, sizeof(buffer));
+ if (ret < 0)
+ return ret;
+
+ seps = (ret - sizeof(struct avdtp_header)) / sizeof(struct seid_info);
+ for (i = 0; i < seps; i++) {
+ const char *type, *media;
+
+ switch (discover->seps[i].type) {
+ case AVDTP_SEP_TYPE_SOURCE:
+ type = "Source";
+ break;
+ case AVDTP_SEP_TYPE_SINK:
+ type = "Sink";
+ break;
+ default:
+ type = "Invalid";
+ }
+
+ switch (discover->seps[i].media_type) {
+ case AVDTP_MEDIA_TYPE_AUDIO:
+ media = "Audio";
+ break;
+ case AVDTP_MEDIA_TYPE_VIDEO:
+ media = "Video";
+ break;
+ case AVDTP_MEDIA_TYPE_MULTIMEDIA:
+ media = "Multimedia";
+ break;
+ default:
+ media = "Invalid";
+ }
+
+ printf("Stream End-Point #%d: %s %s %s\n",
+ discover->seps[i].seid, media, type,
+ discover->seps[i].inuse ? "*" : "");
+
+ avdtp_get_caps(sk, discover->seps[i].seid);
+ }
+
+ return 0;
+}
+
+static int l2cap_connect(bdaddr_t *src, bdaddr_t *dst)
+{
+ struct sockaddr_l2 l2a;
+ int sk;
+
+ memset(&l2a, 0, sizeof(l2a));
+ l2a.l2_family = AF_BLUETOOTH;
+ bacpy(&l2a.l2_bdaddr, src);
+
+ sk = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+ if (sk < 0) {
+ printf("Cannot create L2CAP socket. %s(%d)\n", strerror(errno),
+ errno);
+ return -errno;
+ }
+
+ if (bind(sk, (struct sockaddr *) &l2a, sizeof(l2a)) < 0) {
+ printf("Bind failed. %s (%d)\n", strerror(errno), errno);
+ return -errno;
+ }
+
+ memset(&l2a, 0, sizeof(l2a));
+ l2a.l2_family = AF_BLUETOOTH;
+ bacpy(&l2a.l2_bdaddr, dst);
+ l2a.l2_psm = htobs(AVDTP_PSM);
+
+ if (connect(sk, (struct sockaddr *) &l2a, sizeof(l2a)) < 0) {
+ printf("Connect failed. %s(%d)\n", strerror(errno), errno);
+ return -errno;
+ }
+
+ return sk;
+}
+
+static void usage()
+{
+ printf("avinfo - Audio/Video Info Tool ver %s\n", VERSION);
+ printf("Usage:\n"
+ "\tavinfo [options] <remote address>\n");
+ printf("Options:\n"
+ "\t-h\t\tDisplay help\n"
+ "\t-i\t\tSpecify source interface\n");
+}
+
+static struct option main_options[] = {
+ { "help", 0, 0, 'h' },
+ { "device", 1, 0, 'i' },
+ { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ bdaddr_t src, dst;
+ int opt, sk, dev_id;
+
+ if (argc < 2) {
+ usage();
+ exit(0);
+ }
+
+ bacpy(&src, BDADDR_ANY);
+ dev_id = hci_get_route(&src);
+ if ((dev_id < 0) || (hci_devba(dev_id, &src) < 0)) {
+ printf("Cannot find any local adapter\n");
+ exit(-1);
+ }
+
+ while ((opt = getopt_long(argc, argv, "+i:h", main_options, NULL)) != -1) {
+ switch (opt) {
+ case 'i':
+ if (!strncmp(optarg, "hci", 3))
+ hci_devba(atoi(optarg + 3), &src);
+ else
+ str2ba(optarg, &src);
+ break;
+
+ case 'h':
+ default:
+ usage();
+ exit(0);
+ }
+ }
+
+ printf("Connecting ... \n");
+
+ if (bachk(argv[optind]) < 0) {
+ printf("Invalid argument\n");
+ exit(1);
+ }
+
+ str2ba(argv[optind], &dst);
+ sk = l2cap_connect(&src, &dst);
+ if (sk < 0)
+ exit(1);
+
+ if (avdtp_discover(sk) < 0)
+ exit(1);
+
+ return 0;
+}
diff --git a/tools/bccmd.8 b/tools/bccmd.8
new file mode 100644
index 0000000..28cbe88
--- /dev/null
+++ b/tools/bccmd.8
@@ -0,0 +1,130 @@
+.TH BCCMD 8 "Jun 20 2006" BlueZ "Linux System Administration"
+.SH NAME
+bccmd \- Utility for the CSR BCCMD interface
+.SH SYNOPSIS
+.B bccmd
+.br
+.B bccmd [-t <transport>] [-d <device>] <command> [<args>]
+.br
+.B bccmd [-h --help]
+.br
+.SH DESCRIPTION
+.B
+bccmd
+issues BlueCore commands to
+.B
+Cambridge Silicon Radio
+devices. If run without the <command> argument, a short help page will be displayed.
+.SH OPTIONS
+.TP
+.BI -t\ <transport>
+Specify the communication transport. Valid options are:
+.RS
+.TP
+.BI HCI
+Local device with Host Controller Interface (default).
+.TP
+.BI USB
+Direct USB connection.
+.TP
+.BI BCSP
+Blue Core Serial Protocol.
+.TP
+.BI H4
+H4 serial protocol.
+.TP
+.BI 3WIRE
+3WIRE protocol (not implemented).
+.SH
+.TP
+.BI -d\ <dev>
+Specify a particular device to operate on. If not specified, default is the first available HCI device
+or /dev/ttyS0 for serial transports.
+.SH COMMANDS
+.TP
+.BI builddef
+Get build definitions
+.TP
+.BI keylen\ <handle>
+Get current crypt key length
+.TP
+.BI clock
+Get local Bluetooth clock
+.TP
+.BI rand
+Get random number
+.TP
+.BI chiprev
+Get chip revision
+.TP
+.BI buildname
+Get the full build name
+.TP
+.BI panicarg
+Get panic code argument
+.TP
+.BI faultarg
+Get fault code argument
+.TP
+.BI coldreset
+Perform cold reset
+.TP
+.BI warmreset
+Perform warm reset
+.TP
+.BI disabletx
+Disable TX on the device
+.TP
+.BI enabletx
+Enable TX on the device
+.TP
+.BI singlechan\ <channel>
+Lock radio on specific channel
+.TP
+.BI hoppingon
+Revert to channel hopping
+.TP
+.BI rttxdata1\ <decimal\ freq\ MHz>\ <level>
+TXData1 radio test
+.TP
+.BI radiotest\ <decimal\ freq\ MHz>\ <level>\ <id>
+Run radio tests, tests 4, 6 and 7 are transmit tests
+.TP
+.BI memtypes
+Get memory types
+.TP
+.BI psget\ [-r]\ [-s\ <stores>]\ <key>
+Get value for PS key.
+-r sends a warm reset afterwards
+.TP
+.BI psset\ [-r]\ [-s\ <stores>]\ <key>\ <value>
+Set value for PS key.
+-r sends a warm reset afterwards
+.TP
+.BI psclr\ [-r]\ [-s\ <stores>]\ <key>
+Clear value for PS key.
+-r sends a warm reset afterwards
+.TP
+.BI pslist\ [-r]\ [-s\ <stores>]
+List all PS keys.
+-r sends a warm reset afterwards
+.TP
+.BI psread\ [-r]\ [-s\ <stores>]
+Read all PS keys.
+-r sends a warm reset afterwards
+.TP
+.BI psload\ [-r]\ [-s\ <stores>]\ <file>
+Load all PS keys from PSR file.
+-r sends a warm reset afterwards
+.TP
+.BI pscheck\ [-r]\ [-s\ <stores>]\ <file>
+Check syntax of PSR file.
+-r sends a warm reset afterwards
+.SH KEYS
+bdaddr country devclass keymin keymax features commands version
+remver hciextn mapsco baudrate hostintf anafreq anaftrim usbvid
+usbpid dfupid bootmode
+.SH AUTHORS
+Written by Marcel Holtmann <marcel@holtmann.org>,
+man page by Adam Laurie <adam@algroup.co.uk>
+.PP
diff --git a/tools/bccmd.c b/tools/bccmd.c
new file mode 100644
index 0000000..5cb9255
--- /dev/null
+++ b/tools/bccmd.c
@@ -0,0 +1,1254 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "csr.h"
+
+#define CSR_TRANSPORT_UNKNOWN 0
+#define CSR_TRANSPORT_HCI 1
+#define CSR_TRANSPORT_USB 2
+#define CSR_TRANSPORT_BCSP 3
+#define CSR_TRANSPORT_H4 4
+#define CSR_TRANSPORT_3WIRE 5
+
+#define CSR_STORES_PSI (0x0001)
+#define CSR_STORES_PSF (0x0002)
+#define CSR_STORES_PSROM (0x0004)
+#define CSR_STORES_PSRAM (0x0008)
+#define CSR_STORES_DEFAULT (CSR_STORES_PSI | CSR_STORES_PSF)
+
+#define CSR_TYPE_NULL 0
+#define CSR_TYPE_COMPLEX 1
+#define CSR_TYPE_UINT8 2
+#define CSR_TYPE_UINT16 3
+#define CSR_TYPE_UINT32 4
+
+#define CSR_TYPE_ARRAY CSR_TYPE_COMPLEX
+#define CSR_TYPE_BDADDR CSR_TYPE_COMPLEX
+
+static inline int transport_open(int transport, char *device, speed_t bcsp_rate)
+{
+ switch (transport) {
+ case CSR_TRANSPORT_HCI:
+ return csr_open_hci(device);
+#ifdef HAVE_LIBUSB
+ case CSR_TRANSPORT_USB:
+ return csr_open_usb(device);
+#endif
+ case CSR_TRANSPORT_BCSP:
+ return csr_open_bcsp(device, bcsp_rate);
+ case CSR_TRANSPORT_H4:
+ return csr_open_h4(device);
+ case CSR_TRANSPORT_3WIRE:
+ return csr_open_3wire(device);
+ default:
+ fprintf(stderr, "Unsupported transport\n");
+ return -1;
+ }
+}
+
+static inline int transport_read(int transport, uint16_t varid, uint8_t *value, uint16_t length)
+{
+ switch (transport) {
+ case CSR_TRANSPORT_HCI:
+ return csr_read_hci(varid, value, length);
+#ifdef HAVE_LIBUSB
+ case CSR_TRANSPORT_USB:
+ return csr_read_usb(varid, value, length);
+#endif
+ case CSR_TRANSPORT_BCSP:
+ return csr_read_bcsp(varid, value, length);
+ case CSR_TRANSPORT_H4:
+ return csr_read_h4(varid, value, length);
+ case CSR_TRANSPORT_3WIRE:
+ return csr_read_3wire(varid, value, length);
+ default:
+ errno = EOPNOTSUPP;
+ return -1;
+ }
+}
+
+static inline int transport_write(int transport, uint16_t varid, uint8_t *value, uint16_t length)
+{
+ switch (transport) {
+ case CSR_TRANSPORT_HCI:
+ return csr_write_hci(varid, value, length);
+#ifdef HAVE_LIBUSB
+ case CSR_TRANSPORT_USB:
+ return csr_write_usb(varid, value, length);
+#endif
+ case CSR_TRANSPORT_BCSP:
+ return csr_write_bcsp(varid, value, length);
+ case CSR_TRANSPORT_H4:
+ return csr_write_h4(varid, value, length);
+ case CSR_TRANSPORT_3WIRE:
+ return csr_write_3wire(varid, value, length);
+ default:
+ errno = EOPNOTSUPP;
+ return -1;
+ }
+}
+
+static inline void transport_close(int transport)
+{
+ switch (transport) {
+ case CSR_TRANSPORT_HCI:
+ csr_close_hci();
+ break;
+#ifdef HAVE_LIBUSB
+ case CSR_TRANSPORT_USB:
+ csr_close_usb();
+ break;
+#endif
+ case CSR_TRANSPORT_BCSP:
+ csr_close_bcsp();
+ break;
+ case CSR_TRANSPORT_H4:
+ csr_close_h4();
+ break;
+ case CSR_TRANSPORT_3WIRE:
+ csr_close_3wire();
+ break;
+ }
+}
+
+static struct {
+ uint16_t pskey;
+ int type;
+ int size;
+ char *str;
+} storage[] = {
+ { CSR_PSKEY_BDADDR, CSR_TYPE_BDADDR, 8, "bdaddr" },
+ { CSR_PSKEY_COUNTRYCODE, CSR_TYPE_UINT16, 0, "country" },
+ { CSR_PSKEY_CLASSOFDEVICE, CSR_TYPE_UINT32, 0, "devclass" },
+ { CSR_PSKEY_ENC_KEY_LMIN, CSR_TYPE_UINT16, 0, "keymin" },
+ { CSR_PSKEY_ENC_KEY_LMAX, CSR_TYPE_UINT16, 0, "keymax" },
+ { CSR_PSKEY_LOCAL_SUPPORTED_FEATURES, CSR_TYPE_ARRAY, 8, "features" },
+ { CSR_PSKEY_LOCAL_SUPPORTED_COMMANDS, CSR_TYPE_ARRAY, 18, "commands" },
+ { CSR_PSKEY_HCI_LMP_LOCAL_VERSION, CSR_TYPE_UINT16, 0, "version" },
+ { CSR_PSKEY_LMP_REMOTE_VERSION, CSR_TYPE_UINT8, 0, "remver" },
+ { CSR_PSKEY_HOSTIO_USE_HCI_EXTN, CSR_TYPE_UINT16, 0, "hciextn" },
+ { CSR_PSKEY_HOSTIO_MAP_SCO_PCM, CSR_TYPE_UINT16, 0, "mapsco" },
+ { CSR_PSKEY_UART_BAUDRATE, CSR_TYPE_UINT16, 0, "baudrate" },
+ { CSR_PSKEY_HOST_INTERFACE, CSR_TYPE_UINT16, 0, "hostintf" },
+ { CSR_PSKEY_ANA_FREQ, CSR_TYPE_UINT16, 0, "anafreq" },
+ { CSR_PSKEY_ANA_FTRIM, CSR_TYPE_UINT16, 0, "anaftrim" },
+ { CSR_PSKEY_USB_VENDOR_ID, CSR_TYPE_UINT16, 0, "usbvid" },
+ { CSR_PSKEY_USB_PRODUCT_ID, CSR_TYPE_UINT16, 0, "usbpid" },
+ { CSR_PSKEY_USB_DFU_PRODUCT_ID, CSR_TYPE_UINT16, 0, "dfupid" },
+ { CSR_PSKEY_INITIAL_BOOTMODE, CSR_TYPE_UINT16, 0, "bootmode" },
+ { 0x0000 },
+};
+
+static char *storestostr(uint16_t stores)
+{
+ switch (stores) {
+ case 0x0000:
+ return "Default";
+ case 0x0001:
+ return "psi";
+ case 0x0002:
+ return "psf";
+ case 0x0004:
+ return "psrom";
+ case 0x0008:
+ return "psram";
+ default:
+ return "Unknown";
+ }
+}
+
+static char *memorytostr(uint16_t type)
+{
+ switch (type) {
+ case 0x0000:
+ return "Flash memory";
+ case 0x0001:
+ return "EEPROM";
+ case 0x0002:
+ return "RAM (transient)";
+ case 0x0003:
+ return "ROM (or \"read-only\" flash memory)";
+ default:
+ return "Unknown";
+ }
+}
+
+#define OPT_RANGE(min, max) \
+ if (argc < (min)) { errno = EINVAL; return -1; } \
+ if (argc > (max)) { errno = E2BIG; return -1; }
+
+static struct option help_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static int opt_help(int argc, char *argv[], int *help)
+{
+ int opt;
+
+ while ((opt=getopt_long(argc, argv, "+h", help_options, NULL)) != EOF) {
+ switch (opt) {
+ case 'h':
+ if (help)
+ *help = 1;
+ break;
+ }
+ }
+
+ return optind;
+}
+
+#define OPT_HELP(range, help) \
+ opt_help(argc, argv, (help)); \
+ argc -= optind; argv += optind; optind = 0; \
+ OPT_RANGE((range), (range))
+
+static int cmd_builddef(int transport, int argc, char *argv[])
+{
+ uint8_t array[8];
+ uint16_t def = 0x0000, nextdef = 0x0000;
+ int err = 0;
+
+ OPT_HELP(0, NULL);
+
+ printf("Build definitions:\n");
+
+ while (1) {
+ memset(array, 0, sizeof(array));
+ array[0] = def & 0xff;
+ array[1] = def >> 8;
+
+ err = transport_read(transport, CSR_VARID_GET_NEXT_BUILDDEF, array, 8);
+ if (err < 0) {
+ errno = -err;
+ break;
+ }
+
+ nextdef = array[2] | (array[3] << 8);
+
+ if (nextdef == 0x0000)
+ break;
+
+ def = nextdef;
+
+ printf("0x%04x - %s\n", def, csr_builddeftostr(def));
+ }
+
+ return err;
+}
+
+static int cmd_keylen(int transport, int argc, char *argv[])
+{
+ uint8_t array[8];
+ uint16_t handle, keylen;
+ int err;
+
+ OPT_HELP(1, NULL);
+
+ handle = atoi(argv[0]);
+
+ memset(array, 0, sizeof(array));
+ array[0] = handle & 0xff;
+ array[1] = handle >> 8;
+
+ err = transport_read(transport, CSR_VARID_CRYPT_KEY_LENGTH, array, 8);
+ if (err < 0) {
+ errno = -err;
+ return -1;
+ }
+
+ handle = array[0] | (array[1] << 8);
+ keylen = array[2] | (array[3] << 8);
+
+ printf("Crypt key length: %d bit\n", keylen * 8);
+
+ return 0;
+}
+
+static int cmd_clock(int transport, int argc, char *argv[])
+{
+ uint8_t array[8];
+ uint32_t clock;
+ int err;
+
+ OPT_HELP(0, NULL);
+
+ memset(array, 0, sizeof(array));
+
+ err = transport_read(transport, CSR_VARID_BT_CLOCK, array, 8);
+ if (err < 0) {
+ errno = -err;
+ return -1;
+ }
+
+ clock = array[2] | (array[3] << 8) | (array[0] << 16) | (array[1] << 24);
+
+ printf("Bluetooth clock: 0x%04x (%d)\n", clock, clock);
+
+ return 0;
+}
+
+static int cmd_rand(int transport, int argc, char *argv[])
+{
+ uint8_t array[8];
+ uint16_t rand;
+ int err;
+
+ OPT_HELP(0, NULL);
+
+ memset(array, 0, sizeof(array));
+
+ err = transport_read(transport, CSR_VARID_RAND, array, 8);
+ if (err < 0) {
+ errno = -err;
+ return -1;
+ }
+
+ rand = array[0] | (array[1] << 8);
+
+ printf("Random number: 0x%02x (%d)\n", rand, rand);
+
+ return 0;
+}
+
+static int cmd_chiprev(int transport, int argc, char *argv[])
+{
+ uint8_t array[8];
+ uint16_t rev;
+ char *str;
+ int err;
+
+ OPT_HELP(0, NULL);
+
+ memset(array, 0, sizeof(array));
+
+ err = transport_read(transport, CSR_VARID_CHIPREV, array, 8);
+ if (err < 0) {
+ errno = -err;
+ return -1;
+ }
+
+ rev = array[0] | (array[1] << 8);
+
+ switch (rev) {
+ case 0x64:
+ str = "BC1 ES";
+ break;
+ case 0x65:
+ str = "BC1";
+ break;
+ case 0x89:
+ str = "BC2-External A";
+ break;
+ case 0x8a:
+ str = "BC2-External B";
+ break;
+ case 0x28:
+ str = "BC2-ROM";
+ break;
+ case 0x43:
+ str = "BC3-Multimedia";
+ break;
+ case 0x15:
+ str = "BC3-ROM";
+ break;
+ case 0xe2:
+ str = "BC3-Flash";
+ break;
+ case 0x26:
+ str = "BC4-External";
+ break;
+ case 0x30:
+ str = "BC4-ROM";
+ break;
+ default:
+ str = "NA";
+ break;
+ }
+
+ printf("Chip revision: 0x%04x (%s)\n", rev, str);
+
+ return 0;
+}
+
+static int cmd_buildname(int transport, int argc, char *argv[])
+{
+ uint8_t array[130];
+ char name[64];
+ unsigned int i;
+ int err;
+
+ OPT_HELP(0, NULL);
+
+ memset(array, 0, sizeof(array));
+
+ err = transport_read(transport, CSR_VARID_READ_BUILD_NAME, array, 128);
+ if (err < 0) {
+ errno = -err;
+ return -1;
+ }
+
+ for (i = 0; i < sizeof(name); i++)
+ name[i] = array[(i * 2) + 4];
+
+ printf("Build name: %s\n", name);
+
+ return 0;
+}
+
+static int cmd_panicarg(int transport, int argc, char *argv[])
+{
+ uint8_t array[8];
+ uint16_t error;
+ int err;
+
+ OPT_HELP(0, NULL);
+
+ memset(array, 0, sizeof(array));
+
+ err = transport_read(transport, CSR_VARID_PANIC_ARG, array, 8);
+ if (err < 0) {
+ errno = -err;
+ return -1;
+ }
+
+ error = array[0] | (array[1] << 8);
+
+ printf("Panic code: 0x%02x (%s)\n", error,
+ error < 0x100 ? "valid" : "invalid");
+
+ return 0;
+}
+
+static int cmd_faultarg(int transport, int argc, char *argv[])
+{
+ uint8_t array[8];
+ uint16_t error;
+ int err;
+
+ OPT_HELP(0, NULL);
+
+ memset(array, 0, sizeof(array));
+
+ err = transport_read(transport, CSR_VARID_FAULT_ARG, array, 8);
+ if (err < 0) {
+ errno = -err;
+ return -1;
+ }
+
+ error = array[0] | (array[1] << 8);
+
+ printf("Fault code: 0x%02x (%s)\n", error,
+ error < 0x100 ? "valid" : "invalid");
+
+ return 0;
+}
+
+static int cmd_coldreset(int transport, int argc, char *argv[])
+{
+ return transport_write(transport, CSR_VARID_COLD_RESET, NULL, 0);
+}
+
+static int cmd_warmreset(int transport, int argc, char *argv[])
+{
+ return transport_write(transport, CSR_VARID_WARM_RESET, NULL, 0);
+}
+
+static int cmd_disabletx(int transport, int argc, char *argv[])
+{
+ return transport_write(transport, CSR_VARID_DISABLE_TX, NULL, 0);
+}
+
+static int cmd_enabletx(int transport, int argc, char *argv[])
+{
+ return transport_write(transport, CSR_VARID_ENABLE_TX, NULL, 0);
+}
+
+static int cmd_singlechan(int transport, int argc, char *argv[])
+{
+ uint8_t array[8];
+ uint16_t channel;
+
+ OPT_HELP(1, NULL);
+
+ channel = atoi(argv[0]);
+
+ if (channel > 2401 && channel < 2481)
+ channel -= 2402;
+
+ if (channel > 78) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ memset(array, 0, sizeof(array));
+ array[0] = channel & 0xff;
+ array[1] = channel >> 8;
+
+ return transport_write(transport, CSR_VARID_SINGLE_CHAN, array, 8);
+}
+
+static int cmd_hoppingon(int transport, int argc, char *argv[])
+{
+ return transport_write(transport, CSR_VARID_HOPPING_ON, NULL, 0);
+}
+
+static int cmd_rttxdata1(int transport, int argc, char *argv[])
+{
+ uint8_t array[8];
+ uint16_t freq, level;
+
+ OPT_HELP(2, NULL);
+
+ freq = atoi(argv[0]);
+
+ if (!strncasecmp(argv[1], "0x", 2))
+ level = strtol(argv[1], NULL, 16);
+ else
+ level = atoi(argv[1]);
+
+ memset(array, 0, sizeof(array));
+ array[0] = 0x04;
+ array[1] = 0x00;
+ array[2] = freq & 0xff;
+ array[3] = freq >> 8;
+ array[4] = level & 0xff;
+ array[5] = level >> 8;
+
+ return transport_write(transport, CSR_VARID_RADIOTEST, array, 8);
+}
+
+static int cmd_radiotest(int transport, int argc, char *argv[])
+{
+ uint8_t array[8];
+ uint16_t freq, level, test;
+
+ OPT_HELP(3, NULL);
+
+ freq = atoi(argv[0]);
+
+ if (!strncasecmp(argv[1], "0x", 2))
+ level = strtol(argv[1], NULL, 16);
+ else
+ level = atoi(argv[1]);
+
+ test = atoi(argv[2]);
+
+ memset(array, 0, sizeof(array));
+ array[0] = test & 0xff;
+ array[1] = test >> 8;
+ array[2] = freq & 0xff;
+ array[3] = freq >> 8;
+ array[4] = level & 0xff;
+ array[5] = level >> 8;
+
+ return transport_write(transport, CSR_VARID_RADIOTEST, array, 8);
+}
+
+static int cmd_memtypes(int transport, int argc, char *argv[])
+{
+ uint8_t array[8];
+ uint16_t type, stores[4] = { 0x0001, 0x0002, 0x0004, 0x0008 };
+ int i, err;
+
+ OPT_HELP(0, NULL);
+
+ for (i = 0; i < 4; i++) {
+ memset(array, 0, sizeof(array));
+ array[0] = stores[i] & 0xff;
+ array[1] = stores[i] >> 8;
+
+ err = transport_read(transport, CSR_VARID_PS_MEMORY_TYPE, array, 8);
+ if (err < 0)
+ continue;
+
+ type = array[2] + (array[3] << 8);
+
+ printf("%s (0x%04x) = %s (%d)\n", storestostr(stores[i]),
+ stores[i], memorytostr(type), type);
+ }
+
+ return 0;
+}
+
+static struct option pskey_options[] = {
+ { "stores", 1, 0, 's' },
+ { "reset", 0, 0, 'r' },
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static int opt_pskey(int argc, char *argv[], uint16_t *stores, int *reset, int *help)
+{
+ int opt;
+
+ while ((opt=getopt_long(argc, argv, "+s:rh", pskey_options, NULL)) != EOF) {
+ switch (opt) {
+ case 's':
+ if (!stores)
+ break;
+ if (!strcasecmp(optarg, "default"))
+ *stores = 0x0000;
+ else if (!strcasecmp(optarg, "implementation"))
+ *stores = 0x0001;
+ else if (!strcasecmp(optarg, "factory"))
+ *stores = 0x0002;
+ else if (!strcasecmp(optarg, "rom"))
+ *stores = 0x0004;
+ else if (!strcasecmp(optarg, "ram"))
+ *stores = 0x0008;
+ else if (!strcasecmp(optarg, "psi"))
+ *stores = 0x0001;
+ else if (!strcasecmp(optarg, "psf"))
+ *stores = 0x0002;
+ else if (!strcasecmp(optarg, "psrom"))
+ *stores = 0x0004;
+ else if (!strcasecmp(optarg, "psram"))
+ *stores = 0x0008;
+ else if (!strncasecmp(optarg, "0x", 2))
+ *stores = strtol(optarg, NULL, 16);
+ else
+ *stores = atoi(optarg);
+ break;
+
+ case 'r':
+ if (reset)
+ *reset = 1;
+ break;
+
+ case 'h':
+ if (help)
+ *help = 1;
+ break;
+ }
+ }
+
+ return optind;
+}
+
+#define OPT_PSKEY(min, max, stores, reset, help) \
+ opt_pskey(argc, argv, (stores), (reset), (help)); \
+ argc -= optind; argv += optind; optind = 0; \
+ OPT_RANGE((min), (max))
+
+static int cmd_psget(int transport, int argc, char *argv[])
+{
+ uint8_t array[128];
+ uint16_t pskey, length, value, stores = CSR_STORES_DEFAULT;
+ uint32_t val32;
+ int i, err, reset = 0;
+
+ memset(array, 0, sizeof(array));
+
+ OPT_PSKEY(1, 1, &stores, &reset, NULL);
+
+ if (strncasecmp(argv[0], "0x", 2)) {
+ pskey = atoi(argv[0]);
+
+ for (i = 0; storage[i].pskey; i++) {
+ if (strcasecmp(storage[i].str, argv[0]))
+ continue;
+
+ pskey = storage[i].pskey;
+ break;
+ }
+ } else
+ pskey = strtol(argv[0] + 2, NULL, 16);
+
+ memset(array, 0, sizeof(array));
+ array[0] = pskey & 0xff;
+ array[1] = pskey >> 8;
+ array[2] = stores & 0xff;
+ array[3] = stores >> 8;
+
+ err = transport_read(transport, CSR_VARID_PS_SIZE, array, 8);
+ if (err < 0)
+ return err;
+
+ length = array[2] + (array[3] << 8);
+ if (length + 6 > (int) sizeof(array) / 2)
+ return -EIO;
+
+ memset(array, 0, sizeof(array));
+ array[0] = pskey & 0xff;
+ array[1] = pskey >> 8;
+ array[2] = length & 0xff;
+ array[3] = length >> 8;
+ array[4] = stores & 0xff;
+ array[5] = stores >> 8;
+
+ err = transport_read(transport, CSR_VARID_PS, array, (length + 3) * 2);
+ if (err < 0)
+ return err;
+
+ switch (length) {
+ case 1:
+ value = array[6] | (array[7] << 8);
+ printf("%s: 0x%04x (%d)\n", csr_pskeytostr(pskey), value, value);
+ break;
+
+ case 2:
+ val32 = array[8] | (array[9] << 8) | (array[6] << 16) | (array[7] << 24);
+ printf("%s: 0x%08x (%d)\n", csr_pskeytostr(pskey), val32, val32);
+ break;
+
+ default:
+ printf("%s:", csr_pskeytostr(pskey));
+ for (i = 0; i < length; i++)
+ printf(" 0x%02x%02x", array[(i * 2) + 6], array[(i * 2) + 7]);
+ printf("\n");
+ break;
+ }
+
+ if (reset)
+ transport_write(transport, CSR_VARID_WARM_RESET, NULL, 0);
+
+ return err;
+}
+
+static int cmd_psset(int transport, int argc, char *argv[])
+{
+ uint8_t array[128];
+ uint16_t pskey, length, value, stores = CSR_STORES_PSRAM;
+ uint32_t val32;
+ int i, err, reset = 0;
+
+ memset(array, 0, sizeof(array));
+
+ OPT_PSKEY(2, 81, &stores, &reset, NULL);
+
+ if (strncasecmp(argv[0], "0x", 2)) {
+ pskey = atoi(argv[0]);
+
+ for (i = 0; storage[i].pskey; i++) {
+ if (strcasecmp(storage[i].str, argv[0]))
+ continue;
+
+ pskey = storage[i].pskey;
+ break;
+ }
+ } else
+ pskey = strtol(argv[0] + 2, NULL, 16);
+
+ memset(array, 0, sizeof(array));
+ array[0] = pskey & 0xff;
+ array[1] = pskey >> 8;
+ array[2] = stores & 0xff;
+ array[3] = stores >> 8;
+
+ err = transport_read(transport, CSR_VARID_PS_SIZE, array, 8);
+ if (err < 0)
+ return err;
+
+ length = array[2] + (array[3] << 8);
+ if (length + 6 > (int) sizeof(array) / 2)
+ return -EIO;
+
+ memset(array, 0, sizeof(array));
+ array[0] = pskey & 0xff;
+ array[1] = pskey >> 8;
+ array[2] = length & 0xff;
+ array[3] = length >> 8;
+ array[4] = stores & 0xff;
+ array[5] = stores >> 8;
+
+ argc--;
+ argv++;
+
+ switch (length) {
+ case 1:
+ if (argc != 1) {
+ errno = E2BIG;
+ return -1;
+ }
+
+ if (!strncasecmp(argv[0], "0x", 2))
+ value = strtol(argv[0] + 2, NULL, 16);
+ else
+ value = atoi(argv[0]);
+
+ array[6] = value & 0xff;
+ array[7] = value >> 8;
+ break;
+
+ case 2:
+ if (argc != 1) {
+ errno = E2BIG;
+ return -1;
+ }
+
+ if (!strncasecmp(argv[0], "0x", 2))
+ val32 = strtol(argv[0] + 2, NULL, 16);
+ else
+ val32 = atoi(argv[0]);
+
+ array[6] = (val32 & 0xff0000) >> 16;
+ array[7] = val32 >> 24;
+ array[8] = val32 & 0xff;
+ array[9] = (val32 & 0xff00) >> 8;
+ break;
+
+ default:
+ if (argc != length * 2) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ for (i = 0; i < length * 2; i++)
+ if (!strncasecmp(argv[0], "0x", 2))
+ array[i + 6] = strtol(argv[i] + 2, NULL, 16);
+ else
+ array[i + 6] = atoi(argv[i]);
+ break;
+ }
+
+ err = transport_write(transport, CSR_VARID_PS, array, (length + 3) * 2);
+ if (err < 0)
+ return err;
+
+ if (reset)
+ transport_write(transport, CSR_VARID_WARM_RESET, NULL, 0);
+
+ return err;
+}
+
+static int cmd_psclr(int transport, int argc, char *argv[])
+{
+ uint8_t array[8];
+ uint16_t pskey, stores = CSR_STORES_PSRAM;
+ int i, err, reset = 0;
+
+ OPT_PSKEY(1, 1, &stores, &reset, NULL);
+
+ if (strncasecmp(argv[0], "0x", 2)) {
+ pskey = atoi(argv[0]);
+
+ for (i = 0; storage[i].pskey; i++) {
+ if (strcasecmp(storage[i].str, argv[0]))
+ continue;
+
+ pskey = storage[i].pskey;
+ break;
+ }
+ } else
+ pskey = strtol(argv[0] + 2, NULL, 16);
+
+ memset(array, 0, sizeof(array));
+ array[0] = pskey & 0xff;
+ array[1] = pskey >> 8;
+ array[2] = stores & 0xff;
+ array[3] = stores >> 8;
+
+ err = transport_write(transport, CSR_VARID_PS_CLR_STORES, array, 8);
+ if (err < 0)
+ return err;
+
+ if (reset)
+ transport_write(transport, CSR_VARID_WARM_RESET, NULL, 0);
+
+ return err;
+}
+
+static int cmd_pslist(int transport, int argc, char *argv[])
+{
+ uint8_t array[8];
+ uint16_t pskey = 0x0000, length, stores = CSR_STORES_DEFAULT;
+ int err, reset = 0;
+
+ OPT_PSKEY(0, 0, &stores, &reset, NULL);
+
+ while (1) {
+ memset(array, 0, sizeof(array));
+ array[0] = pskey & 0xff;
+ array[1] = pskey >> 8;
+ array[2] = stores & 0xff;
+ array[3] = stores >> 8;
+
+ err = transport_read(transport, CSR_VARID_PS_NEXT, array, 8);
+ if (err < 0)
+ break;
+
+ pskey = array[4] + (array[5] << 8);
+ if (pskey == 0x0000)
+ break;
+
+ memset(array, 0, sizeof(array));
+ array[0] = pskey & 0xff;
+ array[1] = pskey >> 8;
+ array[2] = stores & 0xff;
+ array[3] = stores >> 8;
+
+ err = transport_read(transport, CSR_VARID_PS_SIZE, array, 8);
+ if (err < 0)
+ continue;
+
+ length = array[2] + (array[3] << 8);
+
+ printf("0x%04x - %s (%d bytes)\n", pskey,
+ csr_pskeytostr(pskey), length * 2);
+ }
+
+ if (reset)
+ transport_write(transport, CSR_VARID_WARM_RESET, NULL, 0);
+
+ return 0;
+}
+
+static int cmd_psread(int transport, int argc, char *argv[])
+{
+ uint8_t array[256];
+ uint16_t pskey = 0x0000, length, stores = CSR_STORES_DEFAULT;
+ char *str, val[7];
+ int i, err, reset = 0;
+
+ OPT_PSKEY(0, 0, &stores, &reset, NULL);
+
+ while (1) {
+ memset(array, 0, sizeof(array));
+ array[0] = pskey & 0xff;
+ array[1] = pskey >> 8;
+ array[2] = stores & 0xff;
+ array[3] = stores >> 8;
+
+ err = transport_read(transport, CSR_VARID_PS_NEXT, array, 8);
+ if (err < 0)
+ break;
+
+ pskey = array[4] + (array[5] << 8);
+ if (pskey == 0x0000)
+ break;
+
+ memset(array, 0, sizeof(array));
+ array[0] = pskey & 0xff;
+ array[1] = pskey >> 8;
+ array[2] = stores & 0xff;
+ array[3] = stores >> 8;
+
+ err = transport_read(transport, CSR_VARID_PS_SIZE, array, 8);
+ if (err < 0)
+ continue;
+
+ length = array[2] + (array[3] << 8);
+ if (length + 6 > (int) sizeof(array) / 2)
+ continue;
+
+ memset(array, 0, sizeof(array));
+ array[0] = pskey & 0xff;
+ array[1] = pskey >> 8;
+ array[2] = length & 0xff;
+ array[3] = length >> 8;
+ array[4] = stores & 0xff;
+ array[5] = stores >> 8;
+
+ err = transport_read(transport, CSR_VARID_PS, array, (length + 3) * 2);
+ if (err < 0)
+ continue;
+
+ str = csr_pskeytoval(pskey);
+ if (!strcasecmp(str, "UNKNOWN")) {
+ sprintf(val, "0x%04x", pskey);
+ str = NULL;
+ }
+
+ printf("// %s%s\n&%04x =", str ? "PSKEY_" : "",
+ str ? str : val, pskey);
+ for (i = 0; i < length; i++)
+ printf(" %02x%02x", array[(i * 2) + 7], array[(i * 2) + 6]);
+ printf("\n");
+ }
+
+ if (reset)
+ transport_write(transport, CSR_VARID_WARM_RESET, NULL, 0);
+
+ return 0;
+}
+
+static int cmd_psload(int transport, int argc, char *argv[])
+{
+ uint8_t array[256];
+ uint16_t pskey, length, size, stores = CSR_STORES_PSRAM;
+ char *str, val[7];
+ int err, reset = 0;
+
+ OPT_PSKEY(1, 1, &stores, &reset, NULL);
+
+ psr_read(argv[0]);
+
+ memset(array, 0, sizeof(array));
+ size = sizeof(array) - 6;
+
+ while (psr_get(&pskey, array + 6, &size) == 0) {
+ str = csr_pskeytoval(pskey);
+ if (!strcasecmp(str, "UNKNOWN")) {
+ sprintf(val, "0x%04x", pskey);
+ str = NULL;
+ }
+
+ printf("Loading %s%s ... ", str ? "PSKEY_" : "",
+ str ? str : val);
+ fflush(stdout);
+
+ length = size / 2;
+
+ array[0] = pskey & 0xff;
+ array[1] = pskey >> 8;
+ array[2] = length & 0xff;
+ array[3] = length >> 8;
+ array[4] = stores & 0xff;
+ array[5] = stores >> 8;
+
+ err = transport_write(transport, CSR_VARID_PS, array, size + 6);
+
+ printf("%s\n", err < 0 ? "failed" : "done");
+
+ memset(array, 0, sizeof(array));
+ size = sizeof(array) - 6;
+ }
+
+ if (reset)
+ transport_write(transport, CSR_VARID_WARM_RESET, NULL, 0);
+
+ return 0;
+}
+
+static int cmd_pscheck(int transport, int argc, char *argv[])
+{
+ uint8_t array[256];
+ uint16_t pskey, size;
+ int i;
+
+ OPT_HELP(1, NULL);
+
+ psr_read(argv[0]);
+
+ while (psr_get(&pskey, array, &size) == 0) {
+ printf("0x%04x =", pskey);
+ for (i = 0; i < size; i++)
+ printf(" 0x%02x", array[i]);
+ printf("\n");
+ }
+
+ return 0;
+}
+
+static struct {
+ char *str;
+ int (*func)(int transport, int argc, char *argv[]);
+ char *arg;
+ char *doc;
+} commands[] = {
+ { "builddef", cmd_builddef, "", "Get build definitions" },
+ { "keylen", cmd_keylen, "<handle>", "Get current crypt key length" },
+ { "clock", cmd_clock, "", "Get local Bluetooth clock" },
+ { "rand", cmd_rand, "", "Get random number" },
+ { "chiprev", cmd_chiprev, "", "Get chip revision" },
+ { "buildname", cmd_buildname, "", "Get the full build name" },
+ { "panicarg", cmd_panicarg, "", "Get panic code argument" },
+ { "faultarg", cmd_faultarg, "", "Get fault code argument" },
+ { "coldreset", cmd_coldreset, "", "Perform cold reset" },
+ { "warmreset", cmd_warmreset, "", "Perform warm reset" },
+ { "disabletx", cmd_disabletx, "", "Disable TX on the device" },
+ { "enabletx", cmd_enabletx, "", "Enable TX on the device" },
+ { "singlechan",cmd_singlechan,"<channel>", "Lock radio on specific channel" },
+ { "hoppingon", cmd_hoppingon, "", "Revert to channel hopping" },
+ { "rttxdata1", cmd_rttxdata1, "<freq> <level>", "TXData1 radio test" },
+ { "radiotest", cmd_radiotest, "<freq> <level> <id>", "Run radio tests" },
+ { "memtypes", cmd_memtypes, NULL, "Get memory types" },
+ { "psget", cmd_psget, "<key>", "Get value for PS key" },
+ { "psset", cmd_psset, "<key> <value>", "Set value for PS key" },
+ { "psclr", cmd_psclr, "<key>", "Clear value for PS key" },
+ { "pslist", cmd_pslist, NULL, "List all PS keys" },
+ { "psread", cmd_psread, NULL, "Read all PS keys" },
+ { "psload", cmd_psload, "<file>", "Load all PS keys from PSR file" },
+ { "pscheck", cmd_pscheck, "<file>", "Check PSR file" },
+ { NULL }
+};
+
+static void usage(void)
+{
+ int i, pos = 0;
+
+ printf("bccmd - Utility for the CSR BCCMD interface\n\n");
+ printf("Usage:\n"
+ "\tbccmd [options] <command>\n\n");
+
+ printf("Options:\n"
+ "\t-t <transport> Select the transport\n"
+ "\t-d <device> Select the device\n"
+ "\t-b <bcsp rate> Select the bcsp transfer rate\n"
+ "\t-h, --help Display help\n"
+ "\n");
+
+ printf("Transports:\n"
+ "\tHCI USB BCSP H4 3WIRE\n\n");
+
+ printf("Commands:\n");
+ for (i = 0; commands[i].str; i++)
+ printf("\t%-10s %-20s\t%s\n", commands[i].str,
+ commands[i].arg ? commands[i].arg : " ",
+ commands[i].doc);
+ printf("\n");
+
+ printf("Keys:\n\t");
+ for (i = 0; storage[i].pskey; i++) {
+ printf("%s ", storage[i].str);
+ pos += strlen(storage[i].str) + 1;
+ if (pos > 60) {
+ printf("\n\t");
+ pos = 0;
+ }
+ }
+ printf("\n");
+}
+
+static struct option main_options[] = {
+ { "transport", 1, 0, 't' },
+ { "device", 1, 0, 'd' },
+ { "bcsprate", 1, 0, 'b'},
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ char *device = NULL;
+ int i, err, opt, transport = CSR_TRANSPORT_HCI;
+ speed_t bcsp_rate = B38400;
+
+ while ((opt=getopt_long(argc, argv, "+t:d:i:b:h", main_options, NULL)) != EOF) {
+ switch (opt) {
+ case 't':
+ if (!strcasecmp(optarg, "hci"))
+ transport = CSR_TRANSPORT_HCI;
+ else if (!strcasecmp(optarg, "usb"))
+ transport = CSR_TRANSPORT_USB;
+ else if (!strcasecmp(optarg, "bcsp"))
+ transport = CSR_TRANSPORT_BCSP;
+ else if (!strcasecmp(optarg, "h4"))
+ transport = CSR_TRANSPORT_H4;
+ else if (!strcasecmp(optarg, "h5"))
+ transport = CSR_TRANSPORT_3WIRE;
+ else if (!strcasecmp(optarg, "3wire"))
+ transport = CSR_TRANSPORT_3WIRE;
+ else if (!strcasecmp(optarg, "twutl"))
+ transport = CSR_TRANSPORT_3WIRE;
+ else
+ transport = CSR_TRANSPORT_UNKNOWN;
+ break;
+
+ case 'd':
+ case 'i':
+ device = strdup(optarg);
+ break;
+ case 'b':
+ switch (atoi(optarg)) {
+ case 9600: bcsp_rate = B9600; break;
+ case 19200: bcsp_rate = B19200; break;
+ case 38400: bcsp_rate = B38400; break;
+ case 57600: bcsp_rate = B57600; break;
+ case 115200: bcsp_rate = B115200; break;
+ case 230400: bcsp_rate = B230400; break;
+ case 460800: bcsp_rate = B460800; break;
+ case 500000: bcsp_rate = B500000; break;
+ case 576000: bcsp_rate = B576000; break;
+ case 921600: bcsp_rate = B921600; break;
+ case 1000000: bcsp_rate = B1000000; break;
+ case 1152000: bcsp_rate = B1152000; break;
+ case 1500000: bcsp_rate = B1500000; break;
+ case 2000000: bcsp_rate = B2000000; break;
+#ifdef B2500000
+ case 2500000: bcsp_rate = B2500000; break;
+#endif
+#ifdef B3000000
+ case 3000000: bcsp_rate = B3000000; break;
+#endif
+#ifdef B3500000
+ case 3500000: bcsp_rate = B3500000; break;
+#endif
+#ifdef B4000000
+ case 4000000: bcsp_rate = B4000000; break;
+#endif
+ default:
+ printf("Unknown BCSP baud rate specified, defaulting to 38400bps\n");
+ bcsp_rate = B38400;
+ }
+ break;
+ case 'h':
+ default:
+ usage();
+ exit(0);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ if (argc < 1) {
+ usage();
+ exit(1);
+ }
+
+ if (transport_open(transport, device, bcsp_rate) < 0)
+ exit(1);
+
+ if (device)
+ free(device);
+
+ for (i = 0; commands[i].str; i++) {
+ if (strcasecmp(commands[i].str, argv[0]))
+ continue;
+
+ err = commands[i].func(transport, argc, argv);
+
+ transport_close(transport);
+
+ if (err < 0) {
+ fprintf(stderr, "Can't execute command: %s (%d)\n",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ exit(0);
+ }
+
+ fprintf(stderr, "Unsupported command\n");
+
+ transport_close(transport);
+
+ exit(1);
+}
diff --git a/tools/ciptool.1 b/tools/ciptool.1
new file mode 100644
index 0000000..65d903d
--- /dev/null
+++ b/tools/ciptool.1
@@ -0,0 +1,68 @@
+.\"
+.\" 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.TH CIPTOOL 1 "JUNE 6, 2003" "" ""
+
+.SH NAME
+ciptool \- Bluetooth Common ISDN Access Profile (CIP)
+.SH SYNOPSIS
+.BR "ciptool
+[
+.I options
+] <
+.I command
+>
+.SH DESCRIPTION
+.B ciptool
+is used to set up, maintain, and inspect the CIP configuration
+of the Bluetooth subsystem in the Linux kernel.
+.SH OPTIONS
+.TP
+.BI -h
+Gives a list of possible commands.
+.TP
+.BI -i " <hciX> | <bdaddr>"
+The command is applied to device
+.I
+hciX
+, which must be the name or the address of an installed Bluetooth
+device. If not specified, the command will be use the first
+available Bluetooth device.
+.SH COMMANDS
+.TP
+.BI show
+Display information about the connected devices.
+.TP
+.BI search
+Search for Bluetooth devices and connect to first one that
+offers CIP support.
+.TP
+.BI connect " <bdaddr> [psm]"
+Connect the local device to the remote Bluetooth device on the
+specified PSM number. If no PSM is specified, it will use the
+SDP to retrieve it from the remote device.
+.TP
+.BI release " [bdaddr]"
+Release a connection to the specific device. If no address is
+given and only one device is connected this will be released.
+.TP
+.BI loopback " <bdaddr> [psm]"
+Create a connection to the remote device for Bluetooth testing.
+This command will not provide a CAPI controller, because it is
+only for testing the CAPI Message Transport Protocol.
+.SH AUTHOR
+Written by Marcel Holtmann <marcel@holtmann.org>.
+.br
diff --git a/tools/ciptool.c b/tools/ciptool.c
new file mode 100644
index 0000000..edce9da
--- /dev/null
+++ b/tools/ciptool.c
@@ -0,0 +1,498 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2010 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
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <getopt.h>
+#include <signal.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>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+#include <bluetooth/cmtp.h>
+
+#ifdef NEED_PPOLL
+#include "ppoll.h"
+#endif
+
+static volatile sig_atomic_t __io_canceled = 0;
+
+static void sig_hup(int sig)
+{
+ return;
+}
+
+static void sig_term(int sig)
+{
+ __io_canceled = 1;
+}
+
+static char *cmtp_state[] = {
+ "unknown",
+ "connected",
+ "open",
+ "bound",
+ "listening",
+ "connecting",
+ "connecting",
+ "config",
+ "disconnecting",
+ "closed"
+};
+
+static char *cmtp_flagstostr(uint32_t flags)
+{
+ static char str[100] = "";
+
+ strcat(str, "[");
+
+ if (flags & (1 << CMTP_LOOPBACK))
+ strcat(str, "loopback");
+
+ strcat(str, "]");
+
+ return str;
+}
+
+static int get_psm(bdaddr_t *src, bdaddr_t *dst, unsigned short *psm)
+{
+ sdp_session_t *s;
+ sdp_list_t *srch, *attrs, *rsp;
+ uuid_t svclass;
+ uint16_t attr;
+ int err;
+
+ if (!(s = sdp_connect(src, dst, 0)))
+ return -1;
+
+ sdp_uuid16_create(&svclass, CIP_SVCLASS_ID);
+ srch = sdp_list_append(NULL, &svclass);
+
+ attr = SDP_ATTR_PROTO_DESC_LIST;
+ attrs = sdp_list_append(NULL, &attr);
+
+ err = sdp_service_search_attr_req(s, srch, SDP_ATTR_REQ_INDIVIDUAL, attrs, &rsp);
+
+ sdp_close(s);
+
+ if (err)
+ return 0;
+
+ for (; rsp; rsp = rsp->next) {
+ sdp_record_t *rec = (sdp_record_t *) rsp->data;
+ sdp_list_t *protos;
+
+ if (!sdp_get_access_protos(rec, &protos)) {
+ unsigned short p = sdp_get_proto_port(protos, L2CAP_UUID);
+ if (p > 0) {
+ *psm = p;
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int do_connect(int ctl, int dev_id, bdaddr_t *src, bdaddr_t *dst, unsigned short psm, uint32_t flags)
+{
+ struct cmtp_connadd_req req;
+ struct hci_dev_info di;
+ struct sockaddr_l2 addr;
+ struct l2cap_options opts;
+ socklen_t size;
+ int sk;
+
+ hci_devinfo(dev_id, &di);
+ if (!(di.link_policy & HCI_LP_RSWITCH)) {
+ printf("Local device is not accepting role switch\n");
+ }
+
+ if ((sk = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) < 0) {
+ perror("Can't create L2CAP socket");
+ exit(1);
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ bacpy(&addr.l2_bdaddr, src);
+
+ if (bind(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ perror("Can't bind L2CAP socket");
+ close(sk);
+ exit(1);
+ }
+
+ memset(&opts, 0, sizeof(opts));
+ size = sizeof(opts);
+
+ if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &size) < 0) {
+ perror("Can't get L2CAP options");
+ close(sk);
+ exit(1);
+ }
+
+ opts.imtu = CMTP_DEFAULT_MTU;
+ opts.omtu = CMTP_DEFAULT_MTU;
+ opts.flush_to = 0xffff;
+
+ if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts)) < 0) {
+ perror("Can't set L2CAP options");
+ close(sk);
+ exit(1);
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ bacpy(&addr.l2_bdaddr, dst);
+ addr.l2_psm = htobs(psm);
+
+ if (connect(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ perror("Can't connect L2CAP socket");
+ close(sk);
+ exit(1);
+ }
+
+ req.sock = sk;
+ req.flags = flags;
+
+ if (ioctl(ctl, CMTPCONNADD, &req) < 0) {
+ perror("Can't create connection");
+ exit(1);
+ }
+
+ return sk;
+}
+
+static void cmd_show(int ctl, bdaddr_t *bdaddr, int argc, char **argv)
+{
+ struct cmtp_connlist_req req;
+ struct cmtp_conninfo ci[16];
+ char addr[18];
+ unsigned int i;
+
+ req.cnum = 16;
+ req.ci = ci;
+
+ if (ioctl(ctl, CMTPGETCONNLIST, &req) < 0) {
+ perror("Can't get connection list");
+ exit(1);
+ }
+
+ for (i = 0; i < req.cnum; i++) {
+ ba2str(&ci[i].bdaddr, addr);
+ printf("%d %s %s %s\n", ci[i].num, addr,
+ cmtp_state[ci[i].state],
+ ci[i].flags ? cmtp_flagstostr(ci[i].flags) : "");
+ }
+}
+
+static void cmd_search(int ctl, bdaddr_t *bdaddr, int argc, char **argv)
+{
+ inquiry_info *info = NULL;
+ bdaddr_t src, dst;
+ unsigned short psm;
+ int i, dev_id, num_rsp, length, flags;
+ char addr[18];
+ uint8_t class[3];
+
+ ba2str(bdaddr, addr);
+ dev_id = hci_devid(addr);
+ if (dev_id < 0) {
+ dev_id = hci_get_route(NULL);
+ hci_devba(dev_id, &src);
+ } else
+ bacpy(&src, bdaddr);
+
+ length = 8; /* ~10 seconds */
+ num_rsp = 0;
+ flags = 0;
+
+ printf("Searching ...\n");
+
+ num_rsp = hci_inquiry(dev_id, length, num_rsp, NULL, &info, flags);
+
+ for (i = 0; i < num_rsp; i++) {
+ memcpy(class, (info+i)->dev_class, 3);
+ if ((class[1] == 2) && ((class[0] / 4) == 5)) {
+ bacpy(&dst, &(info+i)->bdaddr);
+ ba2str(&dst, addr);
+
+ printf("\tChecking service for %s\n", addr);
+ if (!get_psm(&src, &dst, &psm))
+ continue;
+
+ bt_free(info);
+
+ printf("\tConnecting to device %s\n", addr);
+ do_connect(ctl, dev_id, &src, &dst, psm, 0);
+ return;
+ }
+ }
+
+ bt_free(info);
+ fprintf(stderr, "\tNo devices in range or visible\n");
+ exit(1);
+}
+
+static void cmd_create(int ctl, bdaddr_t *bdaddr, int argc, char **argv)
+{
+ bdaddr_t src, dst;
+ unsigned short psm;
+ int dev_id;
+ char addr[18];
+
+ if (argc < 2)
+ return;
+
+ str2ba(argv[1], &dst);
+
+ ba2str(bdaddr, addr);
+ dev_id = hci_devid(addr);
+ if (dev_id < 0) {
+ dev_id = hci_get_route(&dst);
+ hci_devba(dev_id, &src);
+ } else
+ bacpy(&src, bdaddr);
+
+ if (argc < 3) {
+ if (!get_psm(&src, &dst, &psm))
+ psm = 4099;
+ } else
+ psm = atoi(argv[2]);
+
+ do_connect(ctl, dev_id, &src, &dst, psm, 0);
+}
+
+static void cmd_release(int ctl, bdaddr_t *bdaddr, int argc, char **argv)
+{
+ struct cmtp_conndel_req req;
+ struct cmtp_connlist_req cl;
+ struct cmtp_conninfo ci[16];
+
+ if (argc < 2) {
+ cl.cnum = 16;
+ cl.ci = ci;
+
+ if (ioctl(ctl, CMTPGETCONNLIST, &cl) < 0) {
+ perror("Can't get connection list");
+ exit(1);
+ }
+
+ if (cl.cnum == 0)
+ return;
+
+ if (cl.cnum != 1) {
+ fprintf(stderr, "You have to specifiy the device address.\n");
+ exit(1);
+ }
+
+ bacpy(&req.bdaddr, &ci[0].bdaddr);
+ } else
+ str2ba(argv[1], &req.bdaddr);
+
+ if (ioctl(ctl, CMTPCONNDEL, &req) < 0) {
+ perror("Can't release connection");
+ exit(1);
+ }
+}
+
+static void cmd_loopback(int ctl, bdaddr_t *bdaddr, int argc, char **argv)
+{
+ struct cmtp_conndel_req req;
+ struct sigaction sa;
+ struct pollfd p;
+ sigset_t sigs;
+ bdaddr_t src, dst;
+ unsigned short psm;
+ int dev_id, sk;
+ char addr[18];
+
+ if (argc < 2)
+ return;
+
+ str2ba(argv[1], &dst);
+
+ ba2str(bdaddr, addr);
+ dev_id = hci_devid(addr);
+ if (dev_id < 0) {
+ dev_id = hci_get_route(&dst);
+ hci_devba(dev_id, &src);
+ } else
+ bacpy(&src, bdaddr);
+
+ ba2str(&dst, addr);
+ printf("Connecting to %s in loopback mode\n", addr);
+
+ if (argc < 3) {
+ if (!get_psm(&src, &dst, &psm))
+ psm = 4099;
+ } else
+ psm = atoi(argv[2]);
+
+ sk = do_connect(ctl, dev_id, &src, &dst, psm, (1 << CMTP_LOOPBACK));
+
+ printf("Press CTRL-C for hangup\n");
+
+ 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);
+
+ sa.sa_handler = sig_hup;
+ sigaction(SIGHUP, &sa, NULL);
+
+ sigfillset(&sigs);
+ sigdelset(&sigs, SIGCHLD);
+ sigdelset(&sigs, SIGPIPE);
+ sigdelset(&sigs, SIGTERM);
+ sigdelset(&sigs, SIGINT);
+ sigdelset(&sigs, SIGHUP);
+
+ p.fd = sk;
+ p.events = POLLERR | POLLHUP;
+
+ while (!__io_canceled) {
+ p.revents = 0;
+ if (ppoll(&p, 1, NULL, &sigs) > 0)
+ break;
+ }
+
+ bacpy(&req.bdaddr, &dst);
+ ioctl(ctl, CMTPCONNDEL, &req);
+}
+
+static struct {
+ char *cmd;
+ char *alt;
+ void (*func)(int ctl, bdaddr_t *bdaddr, int argc, char **argv);
+ char *opt;
+ char *doc;
+} command[] = {
+ { "show", "list", cmd_show, 0, "Show remote connections" },
+ { "search", "scan", cmd_search, 0, "Search for a remote device" },
+ { "connect", "create", cmd_create, "<bdaddr>", "Connect a remote device" },
+ { "release", "disconnect", cmd_release, "[bdaddr]", "Disconnect the remote device" },
+ { "loopback", "test", cmd_loopback, "<bdaddr>", "Loopback test of a device" },
+ { NULL, NULL, NULL, 0, 0 }
+};
+
+static void usage(void)
+{
+ int i;
+
+ printf("ciptool - Bluetooth Common ISDN Access Profile (CIP)\n\n");
+
+ printf("Usage:\n"
+ "\tciptool [options] [command]\n"
+ "\n");
+
+ printf("Options:\n"
+ "\t-i [hciX|bdaddr] Local HCI device or BD Address\n"
+ "\t-h, --help Display help\n"
+ "\n");
+
+ printf("Commands:\n");
+ for (i = 0; command[i].cmd; i++)
+ printf("\t%-8s %-10s\t%s\n", command[i].cmd,
+ command[i].opt ? command[i].opt : " ",
+ command[i].doc);
+ printf("\n");
+}
+
+static struct option main_options[] = {
+ { "help", 0, 0, 'h' },
+ { "device", 1, 0, 'i' },
+ { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ bdaddr_t bdaddr;
+ int i, opt, ctl;
+
+ bacpy(&bdaddr, BDADDR_ANY);
+
+ while ((opt = getopt_long(argc, argv, "+i:h", main_options, NULL)) != -1) {
+ switch(opt) {
+ case 'i':
+ if (!strncmp(optarg, "hci", 3))
+ hci_devba(atoi(optarg + 3), &bdaddr);
+ else
+ str2ba(optarg, &bdaddr);
+ break;
+ case 'h':
+ usage();
+ exit(0);
+ default:
+ exit(0);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ if (argc < 1) {
+ usage();
+ return 0;
+ }
+
+ if ((ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_CMTP)) < 0 ) {
+ perror("Can't open CMTP control socket");
+ exit(1);
+ }
+
+ for (i = 0; command[i].cmd; i++) {
+ if (strncmp(command[i].cmd, argv[0], 4) && strncmp(command[i].alt, argv[0], 4))
+ continue;
+ command[i].func(ctl, &bdaddr, argc, argv);
+ close(ctl);
+ exit(0);
+ }
+
+ usage();
+
+ close(ctl);
+
+ return 0;
+}
diff --git a/tools/csr.c b/tools/csr.c
new file mode 100644
index 0000000..b4ea1fb
--- /dev/null
+++ b/tools/csr.c
@@ -0,0 +1,2853 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2003-2010 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 <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "csr.h"
+
+struct psr_data {
+ uint16_t pskey;
+ uint8_t *value;
+ uint8_t size;
+ struct psr_data *next;
+};
+
+static struct psr_data *head = NULL, *tail = NULL;
+
+static struct {
+ uint16_t id;
+ char *str;
+} csr_map[] = {
+ { 66, "HCI 9.8" },
+ { 97, "HCI 10.3" },
+ { 101, "HCI 10.5" },
+ { 111, "HCI 11.0" },
+ { 112, "HCI 11.1" },
+ { 114, "HCI 11.2" },
+ { 115, "HCI 11.3" },
+ { 117, "HCI 12.0" },
+ { 119, "HCI 12.1" },
+ { 133, "HCI 12.2" },
+ { 134, "HCI 12.3" },
+ { 162, "HCI 12.4" },
+ { 165, "HCI 12.5" },
+ { 169, "HCI 12.6" },
+ { 188, "HCI 12.7" },
+ { 218, "HCI 12.8" },
+ { 283, "HCI 12.9" },
+ { 203, "HCI 13.2" },
+ { 204, "HCI 13.2" },
+ { 210, "HCI 13.3" },
+ { 211, "HCI 13.3" },
+ { 213, "HCI 13.4" },
+ { 214, "HCI 13.4" },
+ { 225, "HCI 13.5" },
+ { 226, "HCI 13.5" },
+ { 237, "HCI 13.6" },
+ { 238, "HCI 13.6" },
+ { 242, "HCI 14.0" },
+ { 243, "HCI 14.0" },
+ { 244, "HCI 14.0" },
+ { 245, "HCI 14.0" },
+ { 254, "HCI 13.7" },
+ { 255, "HCI 13.7" },
+ { 264, "HCI 14.1" },
+ { 265, "HCI 14.1" },
+ { 267, "HCI 14.2" },
+ { 268, "HCI 14.2" },
+ { 272, "HCI 14.3" },
+ { 273, "HCI 14.3" },
+ { 274, "HCI 13.8" },
+ { 275, "HCI 13.8" },
+ { 286, "HCI 13.9" },
+ { 287, "HCI 13.9" },
+ { 309, "HCI 13.10" },
+ { 310, "HCI 13.10" },
+ { 313, "HCI 14.4" },
+ { 314, "HCI 14.4" },
+ { 323, "HCI 14.5" },
+ { 324, "HCI 14.5" },
+ { 336, "HCI 14.6" },
+ { 337, "HCI 14.6" },
+ { 351, "HCI 13.11" },
+ { 352, "HCI 13.11" },
+ { 362, "HCI 15.0" },
+ { 363, "HCI 15.0" },
+ { 364, "HCI 15.0" },
+ { 365, "HCI 15.0" },
+ { 373, "HCI 14.7" },
+ { 374, "HCI 14.7" },
+ { 379, "HCI 15.1" },
+ { 380, "HCI 15.1" },
+ { 381, "HCI 15.1" },
+ { 382, "HCI 15.1" },
+ { 392, "HCI 15.2" },
+ { 393, "HCI 15.2" },
+ { 394, "HCI 15.2" },
+ { 395, "HCI 15.2" },
+ { 436, "HCI 16.0" },
+ { 437, "HCI 16.0" },
+ { 438, "HCI 16.0" },
+ { 439, "HCI 16.0" },
+ { 443, "HCI 15.3" },
+ { 444, "HCI 15.3" },
+ { 465, "HCI 16.1" },
+ { 466, "HCI 16.1" },
+ { 467, "HCI 16.1" },
+ { 468, "HCI 16.1" },
+ { 487, "HCI 14.8" },
+ { 488, "HCI 14.8" },
+ { 492, "HCI 16.2" },
+ { 493, "HCI 16.2" },
+ { 495, "HCI 16.2" },
+ { 496, "HCI 16.2" },
+ { 502, "HCI 16.1.1" },
+ { 503, "HCI 16.1.1" },
+ { 504, "HCI 16.1.1" },
+ { 505, "HCI 16.1.1" },
+ { 506, "HCI 16.1.2" },
+ { 507, "HCI 16.1.2" },
+ { 508, "HCI 16.1.2" },
+ { 509, "HCI 16.1.2" },
+ { 516, "HCI 16.3" },
+ { 517, "HCI 16.3" },
+ { 518, "HCI 16.3" },
+ { 519, "HCI 16.3" },
+ { 523, "HCI 16.4" },
+ { 524, "HCI 16.4" },
+ { 525, "HCI 16.4" },
+ { 526, "HCI 16.4" },
+ { 553, "HCI 15.3" },
+ { 554, "HCI 15.3" },
+ { 562, "HCI 16.5" },
+ { 563, "HCI 16.5" },
+ { 564, "HCI 16.5" },
+ { 565, "HCI 16.5" },
+ { 593, "HCI 17.0" },
+ { 594, "HCI 17.0" },
+ { 595, "HCI 17.0" },
+ { 599, "HCI 17.0" },
+ { 600, "HCI 17.0" },
+ { 608, "HCI 13.10.1" },
+ { 609, "HCI 13.10.1" },
+ { 613, "HCI 17.1" },
+ { 614, "HCI 17.1" },
+ { 615, "HCI 17.1" },
+ { 616, "HCI 17.1" },
+ { 618, "HCI 17.1" },
+ { 624, "HCI 17.2" },
+ { 625, "HCI 17.2" },
+ { 626, "HCI 17.2" },
+ { 627, "HCI 17.2" },
+ { 637, "HCI 16.6" },
+ { 638, "HCI 16.6" },
+ { 639, "HCI 16.6" },
+ { 640, "HCI 16.6" },
+ { 642, "HCI 13.10.2" },
+ { 643, "HCI 13.10.2" },
+ { 644, "HCI 13.10.3" },
+ { 645, "HCI 13.10.3" },
+ { 668, "HCI 13.10.4" },
+ { 669, "HCI 13.10.4" },
+ { 681, "HCI 16.7" },
+ { 682, "HCI 16.7" },
+ { 683, "HCI 16.7" },
+ { 684, "HCI 16.7" },
+ { 704, "HCI 16.8" },
+ { 718, "HCI 16.4.1" },
+ { 719, "HCI 16.4.1" },
+ { 720, "HCI 16.4.1" },
+ { 721, "HCI 16.4.1" },
+ { 722, "HCI 16.7.1" },
+ { 723, "HCI 16.7.1" },
+ { 724, "HCI 16.7.1" },
+ { 725, "HCI 16.7.1" },
+ { 731, "HCI 16.7.2" },
+ { 732, "HCI 16.7.2" },
+ { 733, "HCI 16.7.2" },
+ { 734, "HCI 16.7.2" },
+ { 735, "HCI 16.4.2" },
+ { 736, "HCI 16.4.2" },
+ { 737, "HCI 16.4.2" },
+ { 738, "HCI 16.4.2" },
+ { 750, "HCI 16.7.3" },
+ { 751, "HCI 16.7.3" },
+ { 752, "HCI 16.7.3" },
+ { 753, "HCI 16.7.3" },
+ { 760, "HCI 16.7.4" },
+ { 761, "HCI 16.7.4" },
+ { 762, "HCI 16.7.4" },
+ { 763, "HCI 16.7.4" },
+ { 770, "HCI 16.9" },
+ { 771, "HCI 16.9" },
+ { 772, "HCI 16.9" },
+ { 773, "HCI 16.9" },
+ { 774, "HCI 17.3" },
+ { 775, "HCI 17.3" },
+ { 776, "HCI 17.3" },
+ { 777, "HCI 17.3" },
+ { 781, "HCI 16.7.5" },
+ { 786, "HCI 16.10" },
+ { 787, "HCI 16.10" },
+ { 788, "HCI 16.10" },
+ { 789, "HCI 16.10" },
+ { 791, "HCI 16.4.3" },
+ { 792, "HCI 16.4.3" },
+ { 793, "HCI 16.4.3" },
+ { 794, "HCI 16.4.3" },
+ { 798, "HCI 16.11" },
+ { 799, "HCI 16.11" },
+ { 800, "HCI 16.11" },
+ { 801, "HCI 16.11" },
+ { 806, "HCI 16.7.5" },
+ { 807, "HCI 16.12" },
+ { 808, "HCI 16.12" },
+ { 809, "HCI 16.12" },
+ { 810, "HCI 16.12" },
+ { 817, "HCI 16.13" },
+ { 818, "HCI 16.13" },
+ { 819, "HCI 16.13" },
+ { 820, "HCI 16.13" },
+ { 823, "HCI 13.10.5" },
+ { 824, "HCI 13.10.5" },
+ { 826, "HCI 16.14" },
+ { 827, "HCI 16.14" },
+ { 828, "HCI 16.14" },
+ { 829, "HCI 16.14" },
+ { 843, "HCI 17.3.1" },
+ { 856, "HCI 17.3.2" },
+ { 857, "HCI 17.3.2" },
+ { 858, "HCI 17.3.2" },
+ { 1120, "HCI 17.11" },
+ { 1168, "HCI 18.1" },
+ { 1169, "HCI 18.1" },
+ { 1241, "HCI 18.x" },
+ { 1298, "HCI 18.2" },
+ { 1354, "HCI 18.2" },
+ { 1392, "HCI 18.2" },
+ { 1393, "HCI 18.2" },
+ { 1501, "HCI 18.2" },
+ { 1503, "HCI 18.2" },
+ { 1504, "HCI 18.2" },
+ { 1505, "HCI 18.2" },
+ { 1506, "HCI 18.2" },
+ { 1520, "HCI 18.2" },
+ { 1586, "HCI 18.2" },
+ { 1591, "HCI 18.2" },
+ { 1592, "HCI 18.2" },
+ { 1593, "HCI 18.2.1" },
+ { 1733, "HCI 18.3" },
+ { 1734, "HCI 18.3" },
+ { 1735, "HCI 18.3" },
+ { 1737, "HCI 18.3" },
+ { 1915, "HCI 19.2" },
+ { 1916, "HCI 19.2" },
+ { 1958, "HCI 19.2" },
+ { 1981, "Unified 20a" },
+ { 1982, "Unified 20a" },
+ { 1989, "HCI 18.4" },
+ { 2062, "Unified 20a1" },
+ { 2063, "Unified 20a1" },
+ { 2067, "Unified 18f" },
+ { 2068, "Unified 18f" },
+ { 2243, "Unified 18e" },
+ { 2244, "Unified 18e" },
+ { 2258, "Unified 20d" },
+ { 2259, "Unified 20d" },
+ { 2361, "Unified 20e" },
+ { 2362, "Unified 20e" },
+ { 2386, "Unified 21a" },
+ { 2387, "Unified 21a" },
+ { 2423, "Unified 21a" },
+ { 2424, "Unified 21a" },
+ { 2623, "Unified 21c" },
+ { 2624, "Unified 21c" },
+ { 2625, "Unified 21c" },
+ { 2626, "Unified 21c" },
+ { 2627, "Unified 21c" },
+ { 2628, "Unified 21c" },
+ { 2629, "Unified 21c" },
+ { 2630, "Unified 21c" },
+ { 2631, "Unified 21c" },
+ { 2632, "Unified 21c" },
+ { 2633, "Unified 21c" },
+ { 2634, "Unified 21c" },
+ { 2635, "Unified 21c" },
+ { 2636, "Unified 21c" },
+ { 2649, "Unified 21c" },
+ { 2650, "Unified 21c" },
+ { 2651, "Unified 21c" },
+ { 2652, "Unified 21c" },
+ { 2653, "Unified 21c" },
+ { 2654, "Unified 21c" },
+ { 2655, "Unified 21c" },
+ { 2656, "Unified 21c" },
+ { 2658, "Unified 21c" },
+ { 3057, "Unified 21d" },
+ { 3058, "Unified 21d" },
+ { 3059, "Unified 21d" },
+ { 3060, "Unified 21d" },
+ { 3062, "Unified 21d" },
+ { 3063, "Unified 21d" },
+ { 3064, "Unified 21d" },
+ { 3164, "Unified 21e" },
+ { 3413, "Unified 21f" },
+ { 3414, "Unified 21f" },
+ { 3415, "Unified 21f" },
+ { 3424, "Unified 21f" },
+ { 3454, "Unified 21f" },
+ { 3684, "Unified 21f" },
+ { 3764, "Unified 21f" },
+ { 4276, "Unified 22b" },
+ { 4277, "Unified 22b" },
+ { 4279, "Unified 22b" },
+ { 4281, "Unified 22b" },
+ { 4282, "Unified 22b" },
+ { 4283, "Unified 22b" },
+ { 4284, "Unified 22b" },
+ { 4285, "Unified 22b" },
+ { 4289, "Unified 22b" },
+ { 4290, "Unified 22b" },
+ { 4291, "Unified 22b" },
+ { 4292, "Unified 22b" },
+ { 4293, "Unified 22b" },
+ { 4294, "Unified 22b" },
+ { 4295, "Unified 22b" },
+ { 4363, "Unified 22c" },
+ { 4373, "Unified 22c" },
+ { 4374, "Unified 22c" },
+ { 4532, "Unified 22d" },
+ { 4533, "Unified 22d" },
+ { 4698, "Unified 23c" },
+ { 4839, "Unified 23c" },
+ { 4841, "Unified 23c" },
+ { 4866, "Unified 23c" },
+ { 4867, "Unified 23c" },
+ { 4868, "Unified 23c" },
+ { 4869, "Unified 23c" },
+ { 4870, "Unified 23c" },
+ { 4871, "Unified 23c" },
+ { 4872, "Unified 23c" },
+ { 4874, "Unified 23c" },
+ { 4875, "Unified 23c" },
+ { 4876, "Unified 23c" },
+ { 4877, "Unified 23c" },
+ { 2526, "Marcel 1 (2005-09-26)" },
+ { 2543, "Marcel 2 (2005-09-28)" },
+ { 2622, "Marcel 3 (2005-10-27)" },
+ { 3326, "Marcel 4 (2006-06-16)" },
+ { 3612, "Marcel 5 (2006-10-24)" },
+ { 4509, "Marcel 6 (2007-06-11)" },
+ { 5417, "Marcel 7 (2008-08-26)" },
+ { 195, "Sniff 1 (2001-11-27)" },
+ { 220, "Sniff 2 (2002-01-03)" },
+ { 269, "Sniff 3 (2002-02-22)" },
+ { 270, "Sniff 4 (2002-02-26)" },
+ { 284, "Sniff 5 (2002-03-12)" },
+ { 292, "Sniff 6 (2002-03-20)" },
+ { 305, "Sniff 7 (2002-04-12)" },
+ { 306, "Sniff 8 (2002-04-12)" },
+ { 343, "Sniff 9 (2002-05-02)" },
+ { 346, "Sniff 10 (2002-05-03)" },
+ { 355, "Sniff 11 (2002-05-16)" },
+ { 256, "Sniff 11 (2002-05-16)" },
+ { 390, "Sniff 12 (2002-06-26)" },
+ { 450, "Sniff 13 (2002-08-16)" },
+ { 451, "Sniff 13 (2002-08-16)" },
+ { 533, "Sniff 14 (2002-10-11)" },
+ { 580, "Sniff 15 (2002-11-14)" },
+ { 623, "Sniff 16 (2002-12-12)" },
+ { 678, "Sniff 17 (2003-01-29)" },
+ { 847, "Sniff 18 (2003-04-17)" },
+ { 876, "Sniff 19 (2003-06-10)" },
+ { 997, "Sniff 22 (2003-09-05)" },
+ { 1027, "Sniff 23 (2003-10-03)" },
+ { 1029, "Sniff 24 (2003-10-03)" },
+ { 1112, "Sniff 25 (2003-12-03)" },
+ { 1113, "Sniff 25 (2003-12-03)" },
+ { 1133, "Sniff 26 (2003-12-18)" },
+ { 1134, "Sniff 26 (2003-12-18)" },
+ { 1223, "Sniff 27 (2004-03-08)" },
+ { 1224, "Sniff 27 (2004-03-08)" },
+ { 1319, "Sniff 31 (2004-04-22)" },
+ { 1320, "Sniff 31 (2004-04-22)" },
+ { 1427, "Sniff 34 (2004-06-16)" },
+ { 1508, "Sniff 35 (2004-07-19)" },
+ { 1509, "Sniff 35 (2004-07-19)" },
+ { 1587, "Sniff 36 (2004-08-18)" },
+ { 1588, "Sniff 36 (2004-08-18)" },
+ { 1641, "Sniff 37 (2004-09-16)" },
+ { 1642, "Sniff 37 (2004-09-16)" },
+ { 1699, "Sniff 38 (2004-10-07)" },
+ { 1700, "Sniff 38 (2004-10-07)" },
+ { 1752, "Sniff 39 (2004-11-02)" },
+ { 1753, "Sniff 39 (2004-11-02)" },
+ { 1759, "Sniff 40 (2004-11-03)" },
+ { 1760, "Sniff 40 (2004-11-03)" },
+ { 1761, "Sniff 40 (2004-11-03)" },
+ { 2009, "Sniff 41 (2005-04-06)" },
+ { 2010, "Sniff 41 (2005-04-06)" },
+ { 2011, "Sniff 41 (2005-04-06)" },
+ { 2016, "Sniff 42 (2005-04-11)" },
+ { 2017, "Sniff 42 (2005-04-11)" },
+ { 2018, "Sniff 42 (2005-04-11)" },
+ { 2023, "Sniff 43 (2005-04-14)" },
+ { 2024, "Sniff 43 (2005-04-14)" },
+ { 2025, "Sniff 43 (2005-04-14)" },
+ { 2032, "Sniff 44 (2005-04-18)" },
+ { 2033, "Sniff 44 (2005-04-18)" },
+ { 2034, "Sniff 44 (2005-04-18)" },
+ { 2288, "Sniff 45 (2005-07-08)" },
+ { 2289, "Sniff 45 (2005-07-08)" },
+ { 2290, "Sniff 45 (2005-07-08)" },
+ { 2388, "Sniff 46 (2005-08-17)" },
+ { 2389, "Sniff 46 (2005-08-17)" },
+ { 2390, "Sniff 46 (2005-08-17)" },
+ { 2869, "Sniff 47 (2006-02-15)" },
+ { 2870, "Sniff 47 (2006-02-15)" },
+ { 2871, "Sniff 47 (2006-02-15)" },
+ { 3214, "Sniff 48 (2006-05-16)" },
+ { 3215, "Sniff 48 (2006-05-16)" },
+ { 3216, "Sniff 48 (2006-05-16)" },
+ { 3356, "Sniff 49 (2006-07-17)" },
+ { 3529, "Sniff 50 (2006-09-21)" },
+ { 3546, "Sniff 51 (2006-09-29)" },
+ { 3683, "Sniff 52 (2006-11-03)" },
+ { 0, }
+};
+
+char *csr_builddeftostr(uint16_t def)
+{
+ switch (def) {
+ case 0x0000:
+ return "NONE";
+ case 0x0001:
+ return "CHIP_BASE_BC01";
+ case 0x0002:
+ return "CHIP_BASE_BC02";
+ case 0x0003:
+ return "CHIP_BC01B";
+ case 0x0004:
+ return "CHIP_BC02_EXTERNAL";
+ case 0x0005:
+ return "BUILD_HCI";
+ case 0x0006:
+ return "BUILD_RFCOMM";
+ case 0x0007:
+ return "BT_VER_1_1";
+ case 0x0008:
+ return "TRANSPORT_ALL";
+ case 0x0009:
+ return "TRANSPORT_BCSP";
+ case 0x000a:
+ return "TRANSPORT_H4";
+ case 0x000b:
+ return "TRANSPORT_USB";
+ case 0x000c:
+ return "MAX_CRYPT_KEY_LEN_56";
+ case 0x000d:
+ return "MAX_CRYPT_KEY_LEN_128";
+ case 0x000e:
+ return "TRANSPORT_USER";
+ case 0x000f:
+ return "CHIP_BC02_KATO";
+ case 0x0010:
+ return "TRANSPORT_NONE";
+ case 0x0012:
+ return "REQUIRE_8MBIT";
+ case 0x0013:
+ return "RADIOTEST";
+ case 0x0014:
+ return "RADIOTEST_LITE";
+ case 0x0015:
+ return "INSTALL_FLASH";
+ case 0x0016:
+ return "INSTALL_EEPROM";
+ case 0x0017:
+ return "INSTALL_COMBO_DOT11";
+ case 0x0018:
+ return "LOWPOWER_TX";
+ case 0x0019:
+ return "TRANSPORT_TWUTL";
+ case 0x001a:
+ return "COMPILER_GCC";
+ case 0x001b:
+ return "CHIP_BC02_CLOUSEAU";
+ case 0x001c:
+ return "CHIP_BC02_TOULOUSE";
+ case 0x001d:
+ return "CHIP_BASE_BC3";
+ case 0x001e:
+ return "CHIP_BC3_NICKNACK";
+ case 0x001f:
+ return "CHIP_BC3_KALIMBA";
+ case 0x0020:
+ return "INSTALL_HCI_MODULE";
+ case 0x0021:
+ return "INSTALL_L2CAP_MODULE";
+ case 0x0022:
+ return "INSTALL_DM_MODULE";
+ case 0x0023:
+ return "INSTALL_SDP_MODULE";
+ case 0x0024:
+ return "INSTALL_RFCOMM_MODULE";
+ case 0x0025:
+ return "INSTALL_HIDIO_MODULE";
+ case 0x0026:
+ return "INSTALL_PAN_MODULE";
+ case 0x0027:
+ return "INSTALL_IPV4_MODULE";
+ case 0x0028:
+ return "INSTALL_IPV6_MODULE";
+ case 0x0029:
+ return "INSTALL_TCP_MODULE";
+ case 0x002a:
+ return "BT_VER_1_2";
+ case 0x002b:
+ return "INSTALL_UDP_MODULE";
+ case 0x002c:
+ return "REQUIRE_0_WAIT_STATES";
+ case 0x002d:
+ return "CHIP_BC3_PADDYWACK";
+ case 0x002e:
+ return "CHIP_BC4_COYOTE";
+ case 0x002f:
+ return "CHIP_BC4_ODDJOB";
+ case 0x0030:
+ return "TRANSPORT_H4DS";
+ case 0x0031:
+ return "CHIP_BASE_BC4";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+char *csr_buildidtostr(uint16_t id)
+{
+ static char str[12];
+ int i;
+
+ for (i = 0; csr_map[i].id; i++)
+ if (csr_map[i].id == id)
+ return csr_map[i].str;
+
+ snprintf(str, 11, "Build %d", id);
+ return str;
+}
+
+char *csr_chipvertostr(uint16_t ver, uint16_t rev)
+{
+ switch (ver) {
+ case 0x00:
+ return "BlueCore01a";
+ case 0x01:
+ switch (rev) {
+ case 0x64:
+ return "BlueCore01b (ES)";
+ case 0x65:
+ default:
+ return "BlueCore01b";
+ }
+ case 0x02:
+ switch (rev) {
+ case 0x89:
+ return "BlueCore02-External (ES2)";
+ case 0x8a:
+ return "BlueCore02-External";
+ case 0x28:
+ return "BlueCore02-ROM/Audio/Flash";
+ default:
+ return "BlueCore02";
+ }
+ case 0x03:
+ switch (rev) {
+ case 0x43:
+ return "BlueCore3-MM";
+ case 0x15:
+ return "BlueCore3-ROM";
+ case 0xe2:
+ return "BlueCore3-Flash";
+ case 0x26:
+ return "BlueCore4-External";
+ case 0x30:
+ return "BlueCore4-ROM";
+ default:
+ return "BlueCore3 or BlueCore4";
+ }
+ default:
+ return "Unknown";
+ }
+}
+
+char *csr_pskeytostr(uint16_t pskey)
+{
+ switch (pskey) {
+ case CSR_PSKEY_BDADDR:
+ return "Bluetooth address";
+ case CSR_PSKEY_COUNTRYCODE:
+ return "Country code";
+ case CSR_PSKEY_CLASSOFDEVICE:
+ return "Class of device";
+ case CSR_PSKEY_DEVICE_DRIFT:
+ return "Device drift";
+ case CSR_PSKEY_DEVICE_JITTER:
+ return "Device jitter";
+ case CSR_PSKEY_MAX_ACLS:
+ return "Maximum ACL links";
+ case CSR_PSKEY_MAX_SCOS:
+ return "Maximum SCO links";
+ case CSR_PSKEY_MAX_REMOTE_MASTERS:
+ return "Maximum remote masters";
+ case CSR_PSKEY_ENABLE_MASTERY_WITH_SLAVERY:
+ return "Support master and slave roles simultaneously";
+ case CSR_PSKEY_H_HC_FC_MAX_ACL_PKT_LEN:
+ return "Maximum HCI ACL packet length";
+ case CSR_PSKEY_H_HC_FC_MAX_SCO_PKT_LEN:
+ return "Maximum HCI SCO packet length";
+ case CSR_PSKEY_H_HC_FC_MAX_ACL_PKTS:
+ return "Maximum number of HCI ACL packets";
+ case CSR_PSKEY_H_HC_FC_MAX_SCO_PKTS:
+ return "Maximum number of HCI SCO packets";
+ case CSR_PSKEY_LC_FC_BUFFER_LOW_WATER_MARK:
+ return "Flow control low water mark";
+ case CSR_PSKEY_LC_MAX_TX_POWER:
+ return "Maximum transmit power";
+ case CSR_PSKEY_TX_GAIN_RAMP:
+ return "Transmit gain ramp rate";
+ case CSR_PSKEY_LC_POWER_TABLE:
+ return "Radio power table";
+ case CSR_PSKEY_LC_PEER_POWER_PERIOD:
+ return "Peer transmit power control interval";
+ case CSR_PSKEY_LC_FC_POOLS_LOW_WATER_MARK:
+ return "Flow control pool low water mark";
+ case CSR_PSKEY_LC_DEFAULT_TX_POWER:
+ return "Default transmit power";
+ case CSR_PSKEY_LC_RSSI_GOLDEN_RANGE:
+ return "RSSI at bottom of golden receive range";
+ case CSR_PSKEY_LC_COMBO_DISABLE_PIO_MASK:
+ return "Combo: PIO lines and logic to disable transmit";
+ case CSR_PSKEY_LC_COMBO_PRIORITY_PIO_MASK:
+ return "Combo: priority activity PIO lines and logic";
+ case CSR_PSKEY_LC_COMBO_DOT11_CHANNEL_PIO_BASE:
+ return "Combo: 802.11b channel number base PIO line";
+ case CSR_PSKEY_LC_COMBO_DOT11_BLOCK_CHANNELS:
+ return "Combo: channels to block either side of 802.11b";
+ case CSR_PSKEY_LC_MAX_TX_POWER_NO_RSSI:
+ return "Maximum transmit power when peer has no RSSI";
+ case CSR_PSKEY_LC_CONNECTION_RX_WINDOW:
+ return "Receive window size during connections";
+ case CSR_PSKEY_LC_COMBO_DOT11_TX_PROTECTION_MODE:
+ return "Combo: which TX packets shall we protect";
+ case CSR_PSKEY_LC_ENHANCED_POWER_TABLE:
+ return "Radio power table";
+ case CSR_PSKEY_LC_WIDEBAND_RSSI_CONFIG:
+ return "RSSI configuration for use with wideband RSSI";
+ case CSR_PSKEY_LC_COMBO_DOT11_PRIORITY_LEAD:
+ return "Combo: How much notice will we give the Combo Card";
+ case CSR_PSKEY_BT_CLOCK_INIT:
+ return "Initial value of Bluetooth clock";
+ case CSR_PSKEY_TX_MR_MOD_DELAY:
+ return "TX Mod delay";
+ case CSR_PSKEY_RX_MR_SYNC_TIMING:
+ return "RX MR Sync Timing";
+ case CSR_PSKEY_RX_MR_SYNC_CONFIG:
+ return "RX MR Sync Configuration";
+ case CSR_PSKEY_LC_LOST_SYNC_SLOTS:
+ return "Time in ms for lost sync in low power modes";
+ case CSR_PSKEY_RX_MR_SAMP_CONFIG:
+ return "RX MR Sync Configuration";
+ case CSR_PSKEY_AGC_HYST_LEVELS:
+ return "AGC hysteresis levels";
+ case CSR_PSKEY_RX_LEVEL_LOW_SIGNAL:
+ return "ANA_RX_LVL at low signal strengths";
+ case CSR_PSKEY_AGC_IQ_LVL_VALUES:
+ return "ANA_IQ_LVL values for AGC algorithmn";
+ case CSR_PSKEY_MR_FTRIM_OFFSET_12DB:
+ return "ANA_RX_FTRIM offset when using 12 dB IF atten ";
+ case CSR_PSKEY_MR_FTRIM_OFFSET_6DB:
+ return "ANA_RX_FTRIM offset when using 6 dB IF atten ";
+ case CSR_PSKEY_NO_CAL_ON_BOOT:
+ return "Do not calibrate radio on boot";
+ case CSR_PSKEY_RSSI_HI_TARGET:
+ return "RSSI high target";
+ case CSR_PSKEY_PREFERRED_MIN_ATTENUATION:
+ return "Preferred minimum attenuator setting";
+ case CSR_PSKEY_LC_COMBO_DOT11_PRIORITY_OVERRIDE:
+ return "Combo: Treat all packets as high priority";
+ case CSR_PSKEY_LC_MULTISLOT_HOLDOFF:
+ return "Time till single slot packets are used for resync";
+ case CSR_PSKEY_FREE_KEY_PIGEON_HOLE:
+ return "Link key store bitfield";
+ case CSR_PSKEY_LINK_KEY_BD_ADDR0:
+ return "Bluetooth address + link key 0";
+ case CSR_PSKEY_LINK_KEY_BD_ADDR1:
+ return "Bluetooth address + link key 1";
+ case CSR_PSKEY_LINK_KEY_BD_ADDR2:
+ return "Bluetooth address + link key 2";
+ case CSR_PSKEY_LINK_KEY_BD_ADDR3:
+ return "Bluetooth address + link key 3";
+ case CSR_PSKEY_LINK_KEY_BD_ADDR4:
+ return "Bluetooth address + link key 4";
+ case CSR_PSKEY_LINK_KEY_BD_ADDR5:
+ return "Bluetooth address + link key 5";
+ case CSR_PSKEY_LINK_KEY_BD_ADDR6:
+ return "Bluetooth address + link key 6";
+ case CSR_PSKEY_LINK_KEY_BD_ADDR7:
+ return "Bluetooth address + link key 7";
+ case CSR_PSKEY_LINK_KEY_BD_ADDR8:
+ return "Bluetooth address + link key 8";
+ case CSR_PSKEY_LINK_KEY_BD_ADDR9:
+ return "Bluetooth address + link key 9";
+ case CSR_PSKEY_LINK_KEY_BD_ADDR10:
+ return "Bluetooth address + link key 10";
+ case CSR_PSKEY_LINK_KEY_BD_ADDR11:
+ return "Bluetooth address + link key 11";
+ case CSR_PSKEY_LINK_KEY_BD_ADDR12:
+ return "Bluetooth address + link key 12";
+ case CSR_PSKEY_LINK_KEY_BD_ADDR13:
+ return "Bluetooth address + link key 13";
+ case CSR_PSKEY_LINK_KEY_BD_ADDR14:
+ return "Bluetooth address + link key 14";
+ case CSR_PSKEY_LINK_KEY_BD_ADDR15:
+ return "Bluetooth address + link key 15";
+ case CSR_PSKEY_ENC_KEY_LMIN:
+ return "Minimum encryption key length";
+ case CSR_PSKEY_ENC_KEY_LMAX:
+ return "Maximum encryption key length";
+ case CSR_PSKEY_LOCAL_SUPPORTED_FEATURES:
+ return "Local supported features block";
+ case CSR_PSKEY_LM_USE_UNIT_KEY:
+ return "Allow use of unit key for authentication?";
+ case CSR_PSKEY_HCI_NOP_DISABLE:
+ return "Disable the HCI Command_Status event on boot";
+ case CSR_PSKEY_LM_MAX_EVENT_FILTERS:
+ return "Maximum number of event filters";
+ case CSR_PSKEY_LM_USE_ENC_MODE_BROADCAST:
+ return "Allow LM to use enc_mode=2";
+ case CSR_PSKEY_LM_TEST_SEND_ACCEPTED_TWICE:
+ return "LM sends two LMP_accepted messages in test mode";
+ case CSR_PSKEY_LM_MAX_PAGE_HOLD_TIME:
+ return "Maximum time we hold a device around page";
+ case CSR_PSKEY_AFH_ADAPTATION_RESPONSE_TIME:
+ return "LM period for AFH adaption";
+ case CSR_PSKEY_AFH_OPTIONS:
+ return "Options to configure AFH";
+ case CSR_PSKEY_AFH_RSSI_RUN_PERIOD:
+ return "AFH RSSI reading period";
+ case CSR_PSKEY_AFH_REENABLE_CHANNEL_TIME:
+ return "AFH good channel adding time";
+ case CSR_PSKEY_NO_DROP_ON_ACR_MS_FAIL:
+ return "Complete link if acr barge-in role switch refused";
+ case CSR_PSKEY_MAX_PRIVATE_KEYS:
+ return "Max private link keys stored";
+ case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR0:
+ return "Bluetooth address + link key 0";
+ case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR1:
+ return "Bluetooth address + link key 1";
+ case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR2:
+ return "Bluetooth address + link key 2";
+ case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR3:
+ return "Bluetooth address + link key 3";
+ case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR4:
+ return "Bluetooth address + link key 4";
+ case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR5:
+ return "Bluetooth address + link key 5";
+ case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR6:
+ return "Bluetooth address + link key 6";
+ case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR7:
+ return "Bluetooth address + link key 7";
+ case CSR_PSKEY_LOCAL_SUPPORTED_COMMANDS:
+ return "Local supported commands";
+ case CSR_PSKEY_LM_MAX_ABSENCE_INDEX:
+ return "Maximum absence index allowed";
+ case CSR_PSKEY_DEVICE_NAME:
+ return "Local device's \"user friendly\" name";
+ case CSR_PSKEY_AFH_RSSI_THRESHOLD:
+ return "AFH RSSI threshold";
+ case CSR_PSKEY_LM_CASUAL_SCAN_INTERVAL:
+ return "Scan interval in slots for casual scanning";
+ case CSR_PSKEY_AFH_MIN_MAP_CHANGE:
+ return "The minimum amount to change an AFH map by";
+ case CSR_PSKEY_AFH_RSSI_LP_RUN_PERIOD:
+ return "AFH RSSI reading period when in low power mode";
+ case CSR_PSKEY_HCI_LMP_LOCAL_VERSION:
+ return "The HCI and LMP version reported locally";
+ case CSR_PSKEY_LMP_REMOTE_VERSION:
+ return "The LMP version reported remotely";
+ case CSR_PSKEY_HOLD_ERROR_MESSAGE_NUMBER:
+ return "Maximum number of queued HCI Hardware Error Events";
+ case CSR_PSKEY_DFU_ATTRIBUTES:
+ return "DFU attributes";
+ case CSR_PSKEY_DFU_DETACH_TO:
+ return "DFU detach timeout";
+ case CSR_PSKEY_DFU_TRANSFER_SIZE:
+ return "DFU transfer size";
+ case CSR_PSKEY_DFU_ENABLE:
+ return "DFU enable";
+ case CSR_PSKEY_DFU_LIN_REG_ENABLE:
+ return "Linear Regulator enabled at boot in DFU mode";
+ case CSR_PSKEY_DFUENC_VMAPP_PK_MODULUS_MSB:
+ return "DFU encryption VM application public key MSB";
+ case CSR_PSKEY_DFUENC_VMAPP_PK_MODULUS_LSB:
+ return "DFU encryption VM application public key LSB";
+ case CSR_PSKEY_DFUENC_VMAPP_PK_M_DASH:
+ return "DFU encryption VM application M dash";
+ case CSR_PSKEY_DFUENC_VMAPP_PK_R2N_MSB:
+ return "DFU encryption VM application public key R2N MSB";
+ case CSR_PSKEY_DFUENC_VMAPP_PK_R2N_LSB:
+ return "DFU encryption VM application public key R2N LSB";
+ case CSR_PSKEY_BCSP_LM_PS_BLOCK:
+ return "BCSP link establishment block";
+ case CSR_PSKEY_HOSTIO_FC_PS_BLOCK:
+ return "HCI flow control block";
+ case CSR_PSKEY_HOSTIO_PROTOCOL_INFO0:
+ return "Host transport channel 0 settings (BCSP ACK)";
+ case CSR_PSKEY_HOSTIO_PROTOCOL_INFO1:
+ return "Host transport channel 1 settings (BCSP-LE)";
+ case CSR_PSKEY_HOSTIO_PROTOCOL_INFO2:
+ return "Host transport channel 2 settings (BCCMD)";
+ case CSR_PSKEY_HOSTIO_PROTOCOL_INFO3:
+ return "Host transport channel 3 settings (HQ)";
+ case CSR_PSKEY_HOSTIO_PROTOCOL_INFO4:
+ return "Host transport channel 4 settings (DM)";
+ case CSR_PSKEY_HOSTIO_PROTOCOL_INFO5:
+ return "Host transport channel 5 settings (HCI CMD/EVT)";
+ case CSR_PSKEY_HOSTIO_PROTOCOL_INFO6:
+ return "Host transport channel 6 settings (HCI ACL)";
+ case CSR_PSKEY_HOSTIO_PROTOCOL_INFO7:
+ return "Host transport channel 7 settings (HCI SCO)";
+ case CSR_PSKEY_HOSTIO_PROTOCOL_INFO8:
+ return "Host transport channel 8 settings (L2CAP)";
+ case CSR_PSKEY_HOSTIO_PROTOCOL_INFO9:
+ return "Host transport channel 9 settings (RFCOMM)";
+ case CSR_PSKEY_HOSTIO_PROTOCOL_INFO10:
+ return "Host transport channel 10 settings (SDP)";
+ case CSR_PSKEY_HOSTIO_PROTOCOL_INFO11:
+ return "Host transport channel 11 settings (TEST)";
+ case CSR_PSKEY_HOSTIO_PROTOCOL_INFO12:
+ return "Host transport channel 12 settings (DFU)";
+ case CSR_PSKEY_HOSTIO_PROTOCOL_INFO13:
+ return "Host transport channel 13 settings (VM)";
+ case CSR_PSKEY_HOSTIO_PROTOCOL_INFO14:
+ return "Host transport channel 14 settings";
+ case CSR_PSKEY_HOSTIO_PROTOCOL_INFO15:
+ return "Host transport channel 15 settings";
+ case CSR_PSKEY_HOSTIO_UART_RESET_TIMEOUT:
+ return "UART reset counter timeout";
+ case CSR_PSKEY_HOSTIO_USE_HCI_EXTN:
+ return "Use hci_extn to route non-hci channels";
+ case CSR_PSKEY_HOSTIO_USE_HCI_EXTN_CCFC:
+ return "Use command-complete flow control for hci_extn";
+ case CSR_PSKEY_HOSTIO_HCI_EXTN_PAYLOAD_SIZE:
+ return "Maximum hci_extn payload size";
+ case CSR_PSKEY_BCSP_LM_CNF_CNT_LIMIT:
+ return "BCSP link establishment conf message count";
+ case CSR_PSKEY_HOSTIO_MAP_SCO_PCM:
+ return "Map SCO over PCM";
+ case CSR_PSKEY_HOSTIO_AWKWARD_PCM_SYNC:
+ return "PCM interface synchronisation is difficult";
+ case CSR_PSKEY_HOSTIO_BREAK_POLL_PERIOD:
+ return "Break poll period (microseconds)";
+ case CSR_PSKEY_HOSTIO_MIN_UART_HCI_SCO_SIZE:
+ return "Minimum SCO packet size sent to host over UART HCI";
+ case CSR_PSKEY_HOSTIO_MAP_SCO_CODEC:
+ return "Map SCO over the built-in codec";
+ case CSR_PSKEY_PCM_CVSD_TX_HI_FREQ_BOOST:
+ return "High frequency boost for PCM when transmitting CVSD";
+ case CSR_PSKEY_PCM_CVSD_RX_HI_FREQ_BOOST:
+ return "High frequency boost for PCM when receiving CVSD";
+ case CSR_PSKEY_PCM_CONFIG32:
+ return "PCM interface settings bitfields";
+ case CSR_PSKEY_USE_OLD_BCSP_LE:
+ return "Use the old version of BCSP link establishment";
+ case CSR_PSKEY_PCM_CVSD_USE_NEW_FILTER:
+ return "CVSD uses the new filter if available";
+ case CSR_PSKEY_PCM_FORMAT:
+ return "PCM data format";
+ case CSR_PSKEY_CODEC_OUT_GAIN:
+ return "Audio output gain when using built-in codec";
+ case CSR_PSKEY_CODEC_IN_GAIN:
+ return "Audio input gain when using built-in codec";
+ case CSR_PSKEY_CODEC_PIO:
+ return "PIO to enable when built-in codec is enabled";
+ case CSR_PSKEY_PCM_LOW_JITTER_CONFIG:
+ return "PCM interface settings for low jitter master mode";
+ case CSR_PSKEY_HOSTIO_SCO_PCM_THRESHOLDS:
+ return "Thresholds for SCO PCM buffers";
+ case CSR_PSKEY_HOSTIO_SCO_HCI_THRESHOLDS:
+ return "Thresholds for SCO HCI buffers";
+ case CSR_PSKEY_HOSTIO_MAP_SCO_PCM_SLOT:
+ return "Route SCO data to specified slot in pcm frame";
+ case CSR_PSKEY_UART_BAUDRATE:
+ return "UART Baud rate";
+ case CSR_PSKEY_UART_CONFIG_BCSP:
+ return "UART configuration when using BCSP";
+ case CSR_PSKEY_UART_CONFIG_H4:
+ return "UART configuration when using H4";
+ case CSR_PSKEY_UART_CONFIG_H5:
+ return "UART configuration when using H5";
+ case CSR_PSKEY_UART_CONFIG_USR:
+ return "UART configuration when under VM control";
+ case CSR_PSKEY_UART_TX_CRCS:
+ return "Use CRCs for BCSP or H5";
+ case CSR_PSKEY_UART_ACK_TIMEOUT:
+ return "Acknowledgement timeout for BCSP and H5";
+ case CSR_PSKEY_UART_TX_MAX_ATTEMPTS:
+ return "Max times to send reliable BCSP or H5 message";
+ case CSR_PSKEY_UART_TX_WINDOW_SIZE:
+ return "Transmit window size for BCSP and H5";
+ case CSR_PSKEY_UART_HOST_WAKE:
+ return "UART host wakeup";
+ case CSR_PSKEY_HOSTIO_THROTTLE_TIMEOUT:
+ return "Host interface performance control.";
+ case CSR_PSKEY_PCM_ALWAYS_ENABLE:
+ return "PCM port is always enable when chip is running";
+ case CSR_PSKEY_UART_HOST_WAKE_SIGNAL:
+ return "Signal to use for uart host wakeup protocol";
+ case CSR_PSKEY_UART_CONFIG_H4DS:
+ return "UART configuration when using H4DS";
+ case CSR_PSKEY_H4DS_WAKE_DURATION:
+ return "How long to spend waking the host when using H4DS";
+ case CSR_PSKEY_H4DS_MAXWU:
+ return "Maximum number of H4DS Wake-Up messages to send";
+ case CSR_PSKEY_H4DS_LE_TIMER_PERIOD:
+ return "H4DS Link Establishment Tsync and Tconf period";
+ case CSR_PSKEY_H4DS_TWU_TIMER_PERIOD:
+ return "H4DS Twu timer period";
+ case CSR_PSKEY_H4DS_UART_IDLE_TIMER_PERIOD:
+ return "H4DS Tuart_idle timer period";
+ case CSR_PSKEY_ANA_FTRIM:
+ return "Crystal frequency trim";
+ case CSR_PSKEY_WD_TIMEOUT:
+ return "Watchdog timeout (microseconds)";
+ case CSR_PSKEY_WD_PERIOD:
+ return "Watchdog period (microseconds)";
+ case CSR_PSKEY_HOST_INTERFACE:
+ return "Host interface";
+ case CSR_PSKEY_HQ_HOST_TIMEOUT:
+ return "HQ host command timeout";
+ case CSR_PSKEY_HQ_ACTIVE:
+ return "Enable host query task?";
+ case CSR_PSKEY_BCCMD_SECURITY_ACTIVE:
+ return "Enable configuration security";
+ case CSR_PSKEY_ANA_FREQ:
+ return "Crystal frequency";
+ case CSR_PSKEY_PIO_PROTECT_MASK:
+ return "Access to PIO pins";
+ case CSR_PSKEY_PMALLOC_SIZES:
+ return "pmalloc sizes array";
+ case CSR_PSKEY_UART_BAUD_RATE:
+ return "UART Baud rate (pre 18)";
+ case CSR_PSKEY_UART_CONFIG:
+ return "UART configuration bitfield";
+ case CSR_PSKEY_STUB:
+ return "Stub";
+ case CSR_PSKEY_TXRX_PIO_CONTROL:
+ return "TX and RX PIO control";
+ case CSR_PSKEY_ANA_RX_LEVEL:
+ return "ANA_RX_LVL register initial value";
+ case CSR_PSKEY_ANA_RX_FTRIM:
+ return "ANA_RX_FTRIM register initial value";
+ case CSR_PSKEY_PSBC_DATA_VERSION:
+ return "Persistent store version";
+ case CSR_PSKEY_PCM0_ATTENUATION:
+ return "Volume control on PCM channel 0";
+ case CSR_PSKEY_LO_LVL_MAX:
+ return "Maximum value of LO level control register";
+ case CSR_PSKEY_LO_ADC_AMPL_MIN:
+ return "Minimum value of the LO amplitude measured on the ADC";
+ case CSR_PSKEY_LO_ADC_AMPL_MAX:
+ return "Maximum value of the LO amplitude measured on the ADC";
+ case CSR_PSKEY_IQ_TRIM_CHANNEL:
+ return "IQ calibration channel";
+ case CSR_PSKEY_IQ_TRIM_GAIN:
+ return "IQ calibration gain";
+ case CSR_PSKEY_IQ_TRIM_ENABLE:
+ return "IQ calibration enable";
+ case CSR_PSKEY_TX_OFFSET_HALF_MHZ:
+ return "Transmit offset";
+ case CSR_PSKEY_GBL_MISC_ENABLES:
+ return "Global miscellaneous hardware enables";
+ case CSR_PSKEY_UART_SLEEP_TIMEOUT:
+ return "Time in ms to deep sleep if nothing received";
+ case CSR_PSKEY_DEEP_SLEEP_STATE:
+ return "Deep sleep state usage";
+ case CSR_PSKEY_IQ_ENABLE_PHASE_TRIM:
+ return "IQ phase enable";
+ case CSR_PSKEY_HCI_HANDLE_FREEZE_PERIOD:
+ return "Time for which HCI handle is frozen after link removal";
+ case CSR_PSKEY_MAX_FROZEN_HCI_HANDLES:
+ return "Maximum number of frozen HCI handles";
+ case CSR_PSKEY_PAGETABLE_DESTRUCTION_DELAY:
+ return "Delay from freezing buf handle to deleting page table";
+ case CSR_PSKEY_IQ_TRIM_PIO_SETTINGS:
+ return "IQ PIO settings";
+ case CSR_PSKEY_USE_EXTERNAL_CLOCK:
+ return "Device uses an external clock";
+ case CSR_PSKEY_DEEP_SLEEP_WAKE_CTS:
+ return "Exit deep sleep on CTS line activity";
+ case CSR_PSKEY_FC_HC2H_FLUSH_DELAY:
+ return "Delay from disconnect to flushing HC->H FC tokens";
+ case CSR_PSKEY_RX_HIGHSIDE:
+ return "Disable the HIGHSIDE bit in ANA_CONFIG";
+ case CSR_PSKEY_TX_PRE_LVL:
+ return "TX pre-amplifier level";
+ case CSR_PSKEY_RX_SINGLE_ENDED:
+ return "RX single ended";
+ case CSR_PSKEY_TX_FILTER_CONFIG:
+ return "TX filter configuration";
+ case CSR_PSKEY_CLOCK_REQUEST_ENABLE:
+ return "External clock request enable";
+ case CSR_PSKEY_RX_MIN_ATTEN:
+ return "Minimum attenuation allowed for receiver";
+ case CSR_PSKEY_XTAL_TARGET_AMPLITUDE:
+ return "Crystal target amplitude";
+ case CSR_PSKEY_PCM_MIN_CPU_CLOCK:
+ return "Minimum CPU clock speed with PCM port running";
+ case CSR_PSKEY_HOST_INTERFACE_PIO_USB:
+ return "USB host interface selection PIO line";
+ case CSR_PSKEY_CPU_IDLE_MODE:
+ return "CPU idle mode when radio is active";
+ case CSR_PSKEY_DEEP_SLEEP_CLEAR_RTS:
+ return "Deep sleep clears the UART RTS line";
+ case CSR_PSKEY_RF_RESONANCE_TRIM:
+ return "Frequency trim for IQ and LNA resonant circuits";
+ case CSR_PSKEY_DEEP_SLEEP_PIO_WAKE:
+ return "PIO line to wake the chip from deep sleep";
+ case CSR_PSKEY_DRAIN_BORE_TIMERS:
+ return "Energy consumption measurement settings";
+ case CSR_PSKEY_DRAIN_TX_POWER_BASE:
+ return "Energy consumption measurement settings";
+ case CSR_PSKEY_MODULE_ID:
+ return "Module serial number";
+ case CSR_PSKEY_MODULE_DESIGN:
+ return "Module design ID";
+ case CSR_PSKEY_MODULE_SECURITY_CODE:
+ return "Module security code";
+ case CSR_PSKEY_VM_DISABLE:
+ return "VM disable";
+ case CSR_PSKEY_MOD_MANUF0:
+ return "Module manufactuer data 0";
+ case CSR_PSKEY_MOD_MANUF1:
+ return "Module manufactuer data 1";
+ case CSR_PSKEY_MOD_MANUF2:
+ return "Module manufactuer data 2";
+ case CSR_PSKEY_MOD_MANUF3:
+ return "Module manufactuer data 3";
+ case CSR_PSKEY_MOD_MANUF4:
+ return "Module manufactuer data 4";
+ case CSR_PSKEY_MOD_MANUF5:
+ return "Module manufactuer data 5";
+ case CSR_PSKEY_MOD_MANUF6:
+ return "Module manufactuer data 6";
+ case CSR_PSKEY_MOD_MANUF7:
+ return "Module manufactuer data 7";
+ case CSR_PSKEY_MOD_MANUF8:
+ return "Module manufactuer data 8";
+ case CSR_PSKEY_MOD_MANUF9:
+ return "Module manufactuer data 9";
+ case CSR_PSKEY_DUT_VM_DISABLE:
+ return "VM disable when entering radiotest modes";
+ case CSR_PSKEY_USR0:
+ return "User configuration data 0";
+ case CSR_PSKEY_USR1:
+ return "User configuration data 1";
+ case CSR_PSKEY_USR2:
+ return "User configuration data 2";
+ case CSR_PSKEY_USR3:
+ return "User configuration data 3";
+ case CSR_PSKEY_USR4:
+ return "User configuration data 4";
+ case CSR_PSKEY_USR5:
+ return "User configuration data 5";
+ case CSR_PSKEY_USR6:
+ return "User configuration data 6";
+ case CSR_PSKEY_USR7:
+ return "User configuration data 7";
+ case CSR_PSKEY_USR8:
+ return "User configuration data 8";
+ case CSR_PSKEY_USR9:
+ return "User configuration data 9";
+ case CSR_PSKEY_USR10:
+ return "User configuration data 10";
+ case CSR_PSKEY_USR11:
+ return "User configuration data 11";
+ case CSR_PSKEY_USR12:
+ return "User configuration data 12";
+ case CSR_PSKEY_USR13:
+ return "User configuration data 13";
+ case CSR_PSKEY_USR14:
+ return "User configuration data 14";
+ case CSR_PSKEY_USR15:
+ return "User configuration data 15";
+ case CSR_PSKEY_USR16:
+ return "User configuration data 16";
+ case CSR_PSKEY_USR17:
+ return "User configuration data 17";
+ case CSR_PSKEY_USR18:
+ return "User configuration data 18";
+ case CSR_PSKEY_USR19:
+ return "User configuration data 19";
+ case CSR_PSKEY_USR20:
+ return "User configuration data 20";
+ case CSR_PSKEY_USR21:
+ return "User configuration data 21";
+ case CSR_PSKEY_USR22:
+ return "User configuration data 22";
+ case CSR_PSKEY_USR23:
+ return "User configuration data 23";
+ case CSR_PSKEY_USR24:
+ return "User configuration data 24";
+ case CSR_PSKEY_USR25:
+ return "User configuration data 25";
+ case CSR_PSKEY_USR26:
+ return "User configuration data 26";
+ case CSR_PSKEY_USR27:
+ return "User configuration data 27";
+ case CSR_PSKEY_USR28:
+ return "User configuration data 28";
+ case CSR_PSKEY_USR29:
+ return "User configuration data 29";
+ case CSR_PSKEY_USR30:
+ return "User configuration data 30";
+ case CSR_PSKEY_USR31:
+ return "User configuration data 31";
+ case CSR_PSKEY_USR32:
+ return "User configuration data 32";
+ case CSR_PSKEY_USR33:
+ return "User configuration data 33";
+ case CSR_PSKEY_USR34:
+ return "User configuration data 34";
+ case CSR_PSKEY_USR35:
+ return "User configuration data 35";
+ case CSR_PSKEY_USR36:
+ return "User configuration data 36";
+ case CSR_PSKEY_USR37:
+ return "User configuration data 37";
+ case CSR_PSKEY_USR38:
+ return "User configuration data 38";
+ case CSR_PSKEY_USR39:
+ return "User configuration data 39";
+ case CSR_PSKEY_USR40:
+ return "User configuration data 40";
+ case CSR_PSKEY_USR41:
+ return "User configuration data 41";
+ case CSR_PSKEY_USR42:
+ return "User configuration data 42";
+ case CSR_PSKEY_USR43:
+ return "User configuration data 43";
+ case CSR_PSKEY_USR44:
+ return "User configuration data 44";
+ case CSR_PSKEY_USR45:
+ return "User configuration data 45";
+ case CSR_PSKEY_USR46:
+ return "User configuration data 46";
+ case CSR_PSKEY_USR47:
+ return "User configuration data 47";
+ case CSR_PSKEY_USR48:
+ return "User configuration data 48";
+ case CSR_PSKEY_USR49:
+ return "User configuration data 49";
+ case CSR_PSKEY_USB_VERSION:
+ return "USB specification version number";
+ case CSR_PSKEY_USB_DEVICE_CLASS_CODES:
+ return "USB device class codes";
+ case CSR_PSKEY_USB_VENDOR_ID:
+ return "USB vendor identifier";
+ case CSR_PSKEY_USB_PRODUCT_ID:
+ return "USB product identifier";
+ case CSR_PSKEY_USB_MANUF_STRING:
+ return "USB manufacturer string";
+ case CSR_PSKEY_USB_PRODUCT_STRING:
+ return "USB product string";
+ case CSR_PSKEY_USB_SERIAL_NUMBER_STRING:
+ return "USB serial number string";
+ case CSR_PSKEY_USB_CONFIG_STRING:
+ return "USB configuration string";
+ case CSR_PSKEY_USB_ATTRIBUTES:
+ return "USB attributes bitmap";
+ case CSR_PSKEY_USB_MAX_POWER:
+ return "USB device maximum power consumption";
+ case CSR_PSKEY_USB_BT_IF_CLASS_CODES:
+ return "USB Bluetooth interface class codes";
+ case CSR_PSKEY_USB_LANGID:
+ return "USB language strings supported";
+ case CSR_PSKEY_USB_DFU_CLASS_CODES:
+ return "USB DFU class codes block";
+ case CSR_PSKEY_USB_DFU_PRODUCT_ID:
+ return "USB DFU product ID";
+ case CSR_PSKEY_USB_PIO_DETACH:
+ return "USB detach/attach PIO line";
+ case CSR_PSKEY_USB_PIO_WAKEUP:
+ return "USB wakeup PIO line";
+ case CSR_PSKEY_USB_PIO_PULLUP:
+ return "USB D+ pullup PIO line";
+ case CSR_PSKEY_USB_PIO_VBUS:
+ return "USB VBus detection PIO Line";
+ case CSR_PSKEY_USB_PIO_WAKE_TIMEOUT:
+ return "Timeout for assertion of USB PIO wake signal";
+ case CSR_PSKEY_USB_PIO_RESUME:
+ return "PIO signal used in place of bus resume";
+ case CSR_PSKEY_USB_BT_SCO_IF_CLASS_CODES:
+ return "USB Bluetooth SCO interface class codes";
+ case CSR_PSKEY_USB_SUSPEND_PIO_LEVEL:
+ return "USB PIO levels to set when suspended";
+ case CSR_PSKEY_USB_SUSPEND_PIO_DIR:
+ return "USB PIO I/O directions to set when suspended";
+ case CSR_PSKEY_USB_SUSPEND_PIO_MASK:
+ return "USB PIO lines to be set forcibly in suspend";
+ case CSR_PSKEY_USB_ENDPOINT_0_MAX_PACKET_SIZE:
+ return "The maxmimum packet size for USB endpoint 0";
+ case CSR_PSKEY_USB_CONFIG:
+ return "USB config params for new chips (>bc2)";
+ case CSR_PSKEY_RADIOTEST_ATTEN_INIT:
+ return "Radio test initial attenuator";
+ case CSR_PSKEY_RADIOTEST_FIRST_TRIM_TIME:
+ return "IQ first calibration period in test";
+ case CSR_PSKEY_RADIOTEST_SUBSEQUENT_TRIM_TIME:
+ return "IQ subsequent calibration period in test";
+ case CSR_PSKEY_RADIOTEST_LO_LVL_TRIM_ENABLE:
+ return "LO_LVL calibration enable";
+ case CSR_PSKEY_RADIOTEST_DISABLE_MODULATION:
+ return "Disable modulation during radiotest transmissions";
+ case CSR_PSKEY_RFCOMM_FCON_THRESHOLD:
+ return "RFCOMM aggregate flow control on threshold";
+ case CSR_PSKEY_RFCOMM_FCOFF_THRESHOLD:
+ return "RFCOMM aggregate flow control off threshold";
+ case CSR_PSKEY_IPV6_STATIC_ADDR:
+ return "Static IPv6 address";
+ case CSR_PSKEY_IPV4_STATIC_ADDR:
+ return "Static IPv4 address";
+ case CSR_PSKEY_IPV6_STATIC_PREFIX_LEN:
+ return "Static IPv6 prefix length";
+ case CSR_PSKEY_IPV6_STATIC_ROUTER_ADDR:
+ return "Static IPv6 router address";
+ case CSR_PSKEY_IPV4_STATIC_SUBNET_MASK:
+ return "Static IPv4 subnet mask";
+ case CSR_PSKEY_IPV4_STATIC_ROUTER_ADDR:
+ return "Static IPv4 router address";
+ case CSR_PSKEY_MDNS_NAME:
+ return "Multicast DNS name";
+ case CSR_PSKEY_FIXED_PIN:
+ return "Fixed PIN";
+ case CSR_PSKEY_MDNS_PORT:
+ return "Multicast DNS port";
+ case CSR_PSKEY_MDNS_TTL:
+ return "Multicast DNS TTL";
+ case CSR_PSKEY_MDNS_IPV4_ADDR:
+ return "Multicast DNS IPv4 address";
+ case CSR_PSKEY_ARP_CACHE_TIMEOUT:
+ return "ARP cache timeout";
+ case CSR_PSKEY_HFP_POWER_TABLE:
+ return "HFP power table";
+ case CSR_PSKEY_DRAIN_BORE_TIMER_COUNTERS:
+ return "Energy consumption estimation timer counters";
+ case CSR_PSKEY_DRAIN_BORE_COUNTERS:
+ return "Energy consumption estimation counters";
+ case CSR_PSKEY_LOOP_FILTER_TRIM:
+ return "Trim value to optimise loop filter";
+ case CSR_PSKEY_DRAIN_BORE_CURRENT_PEAK:
+ return "Energy consumption estimation current peak";
+ case CSR_PSKEY_VM_E2_CACHE_LIMIT:
+ return "Maximum RAM for caching EEPROM VM application";
+ case CSR_PSKEY_FORCE_16MHZ_REF_PIO:
+ return "PIO line to force 16 MHz reference to be assumed";
+ case CSR_PSKEY_CDMA_LO_REF_LIMITS:
+ return "Local oscillator frequency reference limits for CDMA";
+ case CSR_PSKEY_CDMA_LO_ERROR_LIMITS:
+ return "Local oscillator frequency error limits for CDMA";
+ case CSR_PSKEY_CLOCK_STARTUP_DELAY:
+ return "Clock startup delay in milliseconds";
+ case CSR_PSKEY_DEEP_SLEEP_CORRECTION_FACTOR:
+ return "Deep sleep clock correction factor";
+ case CSR_PSKEY_TEMPERATURE_CALIBRATION:
+ return "Temperature in deg C for a given internal setting";
+ case CSR_PSKEY_TEMPERATURE_VS_DELTA_INTERNAL_PA:
+ return "Temperature for given internal PA adjustment";
+ case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_PRE_LVL:
+ return "Temperature for a given TX_PRE_LVL adjustment";
+ case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_BB:
+ return "Temperature for a given TX_BB adjustment";
+ case CSR_PSKEY_TEMPERATURE_VS_DELTA_ANA_FTRIM:
+ return "Temperature for given crystal trim adjustment";
+ case CSR_PSKEY_TEST_DELTA_OFFSET:
+ return "Frequency offset applied to synthesiser in test mode";
+ case CSR_PSKEY_RX_DYNAMIC_LVL_OFFSET:
+ return "Receiver dynamic level offset depending on channel";
+ case CSR_PSKEY_TEST_FORCE_OFFSET:
+ return "Force use of exact value in PSKEY_TEST_DELTA_OFFSET";
+ case CSR_PSKEY_RF_TRAP_BAD_DIVISION_RATIOS:
+ return "Trap bad division ratios in radio frequency tables";
+ case CSR_PSKEY_RADIOTEST_CDMA_LO_REF_LIMITS:
+ return "LO frequency reference limits for CDMA in radiotest";
+ case CSR_PSKEY_INITIAL_BOOTMODE:
+ return "Initial device bootmode";
+ case CSR_PSKEY_ONCHIP_HCI_CLIENT:
+ return "HCI traffic routed internally";
+ case CSR_PSKEY_RX_ATTEN_BACKOFF:
+ return "Receiver attenuation back-off";
+ case CSR_PSKEY_RX_ATTEN_UPDATE_RATE:
+ return "Receiver attenuation update rate";
+ case CSR_PSKEY_SYNTH_TXRX_THRESHOLDS:
+ return "Local oscillator tuning voltage limits for tx and rx";
+ case CSR_PSKEY_MIN_WAIT_STATES:
+ return "Flash wait state indicator";
+ case CSR_PSKEY_RSSI_CORRECTION:
+ return "RSSI correction factor.";
+ case CSR_PSKEY_SCHED_THROTTLE_TIMEOUT:
+ return "Scheduler performance control.";
+ case CSR_PSKEY_DEEP_SLEEP_USE_EXTERNAL_CLOCK:
+ return "Deep sleep uses external 32 kHz clock source";
+ case CSR_PSKEY_TRIM_RADIO_FILTERS:
+ return "Trim rx and tx radio filters if true.";
+ case CSR_PSKEY_TRANSMIT_OFFSET:
+ return "Transmit offset in units of 62.5 kHz";
+ case CSR_PSKEY_USB_VM_CONTROL:
+ return "VM application will supply USB descriptors";
+ case CSR_PSKEY_MR_ANA_RX_FTRIM:
+ return "Medium rate value for the ANA_RX_FTRIM register";
+ case CSR_PSKEY_I2C_CONFIG:
+ return "I2C configuration";
+ case CSR_PSKEY_IQ_LVL_RX:
+ return "IQ demand level for reception";
+ case CSR_PSKEY_MR_TX_FILTER_CONFIG:
+ return "TX filter configuration used for enhanced data rate";
+ case CSR_PSKEY_MR_TX_CONFIG2:
+ return "TX filter configuration used for enhanced data rate";
+ case CSR_PSKEY_USB_DONT_RESET_BOOTMODE_ON_HOST_RESET:
+ return "Don't reset bootmode if USB host resets";
+ case CSR_PSKEY_LC_USE_THROTTLING:
+ return "Adjust packet selection on packet error rate";
+ case CSR_PSKEY_CHARGER_TRIM:
+ return "Trim value for the current charger";
+ case CSR_PSKEY_CLOCK_REQUEST_FEATURES:
+ return "Clock request is tristated if enabled";
+ case CSR_PSKEY_TRANSMIT_OFFSET_CLASS1:
+ return "Transmit offset / 62.5 kHz for class 1 radios";
+ case CSR_PSKEY_TX_AVOID_PA_CLASS1_PIO:
+ return "PIO line asserted in class1 operation to avoid PA";
+ case CSR_PSKEY_MR_PIO_CONFIG:
+ return "PIO line asserted in class1 operation to avoid PA";
+ case CSR_PSKEY_UART_CONFIG2:
+ return "The UART Sampling point";
+ case CSR_PSKEY_CLASS1_IQ_LVL:
+ return "IQ demand level for class 1 power level";
+ case CSR_PSKEY_CLASS1_TX_CONFIG2:
+ return "TX filter configuration used for class 1 tx power";
+ case CSR_PSKEY_TEMPERATURE_VS_DELTA_INTERNAL_PA_CLASS1:
+ return "Temperature for given internal PA adjustment";
+ case CSR_PSKEY_TEMPERATURE_VS_DELTA_EXTERNAL_PA_CLASS1:
+ return "Temperature for given internal PA adjustment";
+ case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_PRE_LVL_MR:
+ return "Temperature adjustment for TX_PRE_LVL in EDR";
+ case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_BB_MR_HEADER:
+ return "Temperature for a given TX_BB in EDR header";
+ case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_BB_MR_PAYLOAD:
+ return "Temperature for a given TX_BB in EDR payload";
+ case CSR_PSKEY_RX_MR_EQ_TAPS:
+ return "Adjust receiver configuration for EDR";
+ case CSR_PSKEY_TX_PRE_LVL_CLASS1:
+ return "TX pre-amplifier level in class 1 operation";
+ case CSR_PSKEY_ANALOGUE_ATTENUATOR:
+ return "TX analogue attenuator setting";
+ case CSR_PSKEY_MR_RX_FILTER_TRIM:
+ return "Trim for receiver used in EDR.";
+ case CSR_PSKEY_MR_RX_FILTER_RESPONSE:
+ return "Filter response for receiver used in EDR.";
+ case CSR_PSKEY_PIO_WAKEUP_STATE:
+ return "PIO deep sleep wake up state ";
+ case CSR_PSKEY_MR_TX_IF_ATTEN_OFF_TEMP:
+ return "TX IF atten off temperature when using EDR.";
+ case CSR_PSKEY_LO_DIV_LATCH_BYPASS:
+ return "Bypass latch for LO dividers";
+ case CSR_PSKEY_LO_VCO_STANDBY:
+ return "Use standby mode for the LO VCO";
+ case CSR_PSKEY_SLOW_CLOCK_FILTER_SHIFT:
+ return "Slow clock sampling filter constant";
+ case CSR_PSKEY_SLOW_CLOCK_FILTER_DIVIDER:
+ return "Slow clock filter fractional threshold";
+ case CSR_PSKEY_USB_ATTRIBUTES_POWER:
+ return "USB self powered";
+ case CSR_PSKEY_USB_ATTRIBUTES_WAKEUP:
+ return "USB responds to wake-up";
+ case CSR_PSKEY_DFU_ATTRIBUTES_MANIFESTATION_TOLERANT:
+ return "DFU manifestation tolerant";
+ case CSR_PSKEY_DFU_ATTRIBUTES_CAN_UPLOAD:
+ return "DFU can upload";
+ case CSR_PSKEY_DFU_ATTRIBUTES_CAN_DOWNLOAD:
+ return "DFU can download";
+ case CSR_PSKEY_UART_CONFIG_STOP_BITS:
+ return "UART: stop bits";
+ case CSR_PSKEY_UART_CONFIG_PARITY_BIT:
+ return "UART: parity bit";
+ case CSR_PSKEY_UART_CONFIG_FLOW_CTRL_EN:
+ return "UART: hardware flow control";
+ case CSR_PSKEY_UART_CONFIG_RTS_AUTO_EN:
+ return "UART: RTS auto-enabled";
+ case CSR_PSKEY_UART_CONFIG_RTS:
+ return "UART: RTS asserted";
+ case CSR_PSKEY_UART_CONFIG_TX_ZERO_EN:
+ return "UART: TX zero enable";
+ case CSR_PSKEY_UART_CONFIG_NON_BCSP_EN:
+ return "UART: enable BCSP-specific hardware";
+ case CSR_PSKEY_UART_CONFIG_RX_RATE_DELAY:
+ return "UART: RX rate delay";
+ case CSR_PSKEY_UART_SEQ_TIMEOUT:
+ return "UART: BCSP ack timeout";
+ case CSR_PSKEY_UART_SEQ_RETRIES:
+ return "UART: retry limit in sequencing layer";
+ case CSR_PSKEY_UART_SEQ_WINSIZE:
+ return "UART: BCSP transmit window size";
+ case CSR_PSKEY_UART_USE_CRC_ON_TX:
+ return "UART: use BCSP CRCs";
+ case CSR_PSKEY_UART_HOST_INITIAL_STATE:
+ return "UART: initial host state";
+ case CSR_PSKEY_UART_HOST_ATTENTION_SPAN:
+ return "UART: host attention span";
+ case CSR_PSKEY_UART_HOST_WAKEUP_TIME:
+ return "UART: host wakeup time";
+ case CSR_PSKEY_UART_HOST_WAKEUP_WAIT:
+ return "UART: host wakeup wait";
+ case CSR_PSKEY_BCSP_LM_MODE:
+ return "BCSP link establishment mode";
+ case CSR_PSKEY_BCSP_LM_SYNC_RETRIES:
+ return "BCSP link establishment sync retries";
+ case CSR_PSKEY_BCSP_LM_TSHY:
+ return "BCSP link establishment Tshy";
+ case CSR_PSKEY_UART_DFU_CONFIG_STOP_BITS:
+ return "DFU mode UART: stop bits";
+ case CSR_PSKEY_UART_DFU_CONFIG_PARITY_BIT:
+ return "DFU mode UART: parity bit";
+ case CSR_PSKEY_UART_DFU_CONFIG_FLOW_CTRL_EN:
+ return "DFU mode UART: hardware flow control";
+ case CSR_PSKEY_UART_DFU_CONFIG_RTS_AUTO_EN:
+ return "DFU mode UART: RTS auto-enabled";
+ case CSR_PSKEY_UART_DFU_CONFIG_RTS:
+ return "DFU mode UART: RTS asserted";
+ case CSR_PSKEY_UART_DFU_CONFIG_TX_ZERO_EN:
+ return "DFU mode UART: TX zero enable";
+ case CSR_PSKEY_UART_DFU_CONFIG_NON_BCSP_EN:
+ return "DFU mode UART: enable BCSP-specific hardware";
+ case CSR_PSKEY_UART_DFU_CONFIG_RX_RATE_DELAY:
+ return "DFU mode UART: RX rate delay";
+ case CSR_PSKEY_AMUX_AIO0:
+ return "Multiplexer for AIO 0";
+ case CSR_PSKEY_AMUX_AIO1:
+ return "Multiplexer for AIO 1";
+ case CSR_PSKEY_AMUX_AIO2:
+ return "Multiplexer for AIO 2";
+ case CSR_PSKEY_AMUX_AIO3:
+ return "Multiplexer for AIO 3";
+ case CSR_PSKEY_LOCAL_NAME_SIMPLIFIED:
+ return "Local Name (simplified)";
+ case CSR_PSKEY_EXTENDED_STUB:
+ return "Extended stub";
+ default:
+ return "Unknown";
+ }
+}
+
+char *csr_pskeytoval(uint16_t pskey)
+{
+ switch (pskey) {
+ case CSR_PSKEY_BDADDR:
+ return "BDADDR";
+ case CSR_PSKEY_COUNTRYCODE:
+ return "COUNTRYCODE";
+ case CSR_PSKEY_CLASSOFDEVICE:
+ return "CLASSOFDEVICE";
+ case CSR_PSKEY_DEVICE_DRIFT:
+ return "DEVICE_DRIFT";
+ case CSR_PSKEY_DEVICE_JITTER:
+ return "DEVICE_JITTER";
+ case CSR_PSKEY_MAX_ACLS:
+ return "MAX_ACLS";
+ case CSR_PSKEY_MAX_SCOS:
+ return "MAX_SCOS";
+ case CSR_PSKEY_MAX_REMOTE_MASTERS:
+ return "MAX_REMOTE_MASTERS";
+ case CSR_PSKEY_ENABLE_MASTERY_WITH_SLAVERY:
+ return "ENABLE_MASTERY_WITH_SLAVERY";
+ case CSR_PSKEY_H_HC_FC_MAX_ACL_PKT_LEN:
+ return "H_HC_FC_MAX_ACL_PKT_LEN";
+ case CSR_PSKEY_H_HC_FC_MAX_SCO_PKT_LEN:
+ return "H_HC_FC_MAX_SCO_PKT_LEN";
+ case CSR_PSKEY_H_HC_FC_MAX_ACL_PKTS:
+ return "H_HC_FC_MAX_ACL_PKTS";
+ case CSR_PSKEY_H_HC_FC_MAX_SCO_PKTS:
+ return "H_HC_FC_MAX_SCO_PKTS";
+ case CSR_PSKEY_LC_FC_BUFFER_LOW_WATER_MARK:
+ return "LC_FC_BUFFER_LOW_WATER_MARK";
+ case CSR_PSKEY_LC_MAX_TX_POWER:
+ return "LC_MAX_TX_POWER";
+ case CSR_PSKEY_TX_GAIN_RAMP:
+ return "TX_GAIN_RAMP";
+ case CSR_PSKEY_LC_POWER_TABLE:
+ return "LC_POWER_TABLE";
+ case CSR_PSKEY_LC_PEER_POWER_PERIOD:
+ return "LC_PEER_POWER_PERIOD";
+ case CSR_PSKEY_LC_FC_POOLS_LOW_WATER_MARK:
+ return "LC_FC_POOLS_LOW_WATER_MARK";
+ case CSR_PSKEY_LC_DEFAULT_TX_POWER:
+ return "LC_DEFAULT_TX_POWER";
+ case CSR_PSKEY_LC_RSSI_GOLDEN_RANGE:
+ return "LC_RSSI_GOLDEN_RANGE";
+ case CSR_PSKEY_LC_COMBO_DISABLE_PIO_MASK:
+ return "LC_COMBO_DISABLE_PIO_MASK";
+ case CSR_PSKEY_LC_COMBO_PRIORITY_PIO_MASK:
+ return "LC_COMBO_PRIORITY_PIO_MASK";
+ case CSR_PSKEY_LC_COMBO_DOT11_CHANNEL_PIO_BASE:
+ return "LC_COMBO_DOT11_CHANNEL_PIO_BASE";
+ case CSR_PSKEY_LC_COMBO_DOT11_BLOCK_CHANNELS:
+ return "LC_COMBO_DOT11_BLOCK_CHANNELS";
+ case CSR_PSKEY_LC_MAX_TX_POWER_NO_RSSI:
+ return "LC_MAX_TX_POWER_NO_RSSI";
+ case CSR_PSKEY_LC_CONNECTION_RX_WINDOW:
+ return "LC_CONNECTION_RX_WINDOW";
+ case CSR_PSKEY_LC_COMBO_DOT11_TX_PROTECTION_MODE:
+ return "LC_COMBO_DOT11_TX_PROTECTION_MODE";
+ case CSR_PSKEY_LC_ENHANCED_POWER_TABLE:
+ return "LC_ENHANCED_POWER_TABLE";
+ case CSR_PSKEY_LC_WIDEBAND_RSSI_CONFIG:
+ return "LC_WIDEBAND_RSSI_CONFIG";
+ case CSR_PSKEY_LC_COMBO_DOT11_PRIORITY_LEAD:
+ return "LC_COMBO_DOT11_PRIORITY_LEAD";
+ case CSR_PSKEY_BT_CLOCK_INIT:
+ return "BT_CLOCK_INIT";
+ case CSR_PSKEY_TX_MR_MOD_DELAY:
+ return "TX_MR_MOD_DELAY";
+ case CSR_PSKEY_RX_MR_SYNC_TIMING:
+ return "RX_MR_SYNC_TIMING";
+ case CSR_PSKEY_RX_MR_SYNC_CONFIG:
+ return "RX_MR_SYNC_CONFIG";
+ case CSR_PSKEY_LC_LOST_SYNC_SLOTS:
+ return "LC_LOST_SYNC_SLOTS";
+ case CSR_PSKEY_RX_MR_SAMP_CONFIG:
+ return "RX_MR_SAMP_CONFIG";
+ case CSR_PSKEY_AGC_HYST_LEVELS:
+ return "AGC_HYST_LEVELS";
+ case CSR_PSKEY_RX_LEVEL_LOW_SIGNAL:
+ return "RX_LEVEL_LOW_SIGNAL";
+ case CSR_PSKEY_AGC_IQ_LVL_VALUES:
+ return "AGC_IQ_LVL_VALUES";
+ case CSR_PSKEY_MR_FTRIM_OFFSET_12DB:
+ return "MR_FTRIM_OFFSET_12DB";
+ case CSR_PSKEY_MR_FTRIM_OFFSET_6DB:
+ return "MR_FTRIM_OFFSET_6DB";
+ case CSR_PSKEY_NO_CAL_ON_BOOT:
+ return "NO_CAL_ON_BOOT";
+ case CSR_PSKEY_RSSI_HI_TARGET:
+ return "RSSI_HI_TARGET";
+ case CSR_PSKEY_PREFERRED_MIN_ATTENUATION:
+ return "PREFERRED_MIN_ATTENUATION";
+ case CSR_PSKEY_LC_COMBO_DOT11_PRIORITY_OVERRIDE:
+ return "LC_COMBO_DOT11_PRIORITY_OVERRIDE";
+ case CSR_PSKEY_LC_MULTISLOT_HOLDOFF:
+ return "LC_MULTISLOT_HOLDOFF";
+ case CSR_PSKEY_FREE_KEY_PIGEON_HOLE:
+ return "FREE_KEY_PIGEON_HOLE";
+ case CSR_PSKEY_LINK_KEY_BD_ADDR0:
+ return "LINK_KEY_BD_ADDR0";
+ case CSR_PSKEY_LINK_KEY_BD_ADDR1:
+ return "LINK_KEY_BD_ADDR1";
+ case CSR_PSKEY_LINK_KEY_BD_ADDR2:
+ return "LINK_KEY_BD_ADDR2";
+ case CSR_PSKEY_LINK_KEY_BD_ADDR3:
+ return "LINK_KEY_BD_ADDR3";
+ case CSR_PSKEY_LINK_KEY_BD_ADDR4:
+ return "LINK_KEY_BD_ADDR4";
+ case CSR_PSKEY_LINK_KEY_BD_ADDR5:
+ return "LINK_KEY_BD_ADDR5";
+ case CSR_PSKEY_LINK_KEY_BD_ADDR6:
+ return "LINK_KEY_BD_ADDR6";
+ case CSR_PSKEY_LINK_KEY_BD_ADDR7:
+ return "LINK_KEY_BD_ADDR7";
+ case CSR_PSKEY_LINK_KEY_BD_ADDR8:
+ return "LINK_KEY_BD_ADDR8";
+ case CSR_PSKEY_LINK_KEY_BD_ADDR9:
+ return "LINK_KEY_BD_ADDR9";
+ case CSR_PSKEY_LINK_KEY_BD_ADDR10:
+ return "LINK_KEY_BD_ADDR10";
+ case CSR_PSKEY_LINK_KEY_BD_ADDR11:
+ return "LINK_KEY_BD_ADDR11";
+ case CSR_PSKEY_LINK_KEY_BD_ADDR12:
+ return "LINK_KEY_BD_ADDR12";
+ case CSR_PSKEY_LINK_KEY_BD_ADDR13:
+ return "LINK_KEY_BD_ADDR13";
+ case CSR_PSKEY_LINK_KEY_BD_ADDR14:
+ return "LINK_KEY_BD_ADDR14";
+ case CSR_PSKEY_LINK_KEY_BD_ADDR15:
+ return "LINK_KEY_BD_ADDR15";
+ case CSR_PSKEY_ENC_KEY_LMIN:
+ return "ENC_KEY_LMIN";
+ case CSR_PSKEY_ENC_KEY_LMAX:
+ return "ENC_KEY_LMAX";
+ case CSR_PSKEY_LOCAL_SUPPORTED_FEATURES:
+ return "LOCAL_SUPPORTED_FEATURES";
+ case CSR_PSKEY_LM_USE_UNIT_KEY:
+ return "LM_USE_UNIT_KEY";
+ case CSR_PSKEY_HCI_NOP_DISABLE:
+ return "HCI_NOP_DISABLE";
+ case CSR_PSKEY_LM_MAX_EVENT_FILTERS:
+ return "LM_MAX_EVENT_FILTERS";
+ case CSR_PSKEY_LM_USE_ENC_MODE_BROADCAST:
+ return "LM_USE_ENC_MODE_BROADCAST";
+ case CSR_PSKEY_LM_TEST_SEND_ACCEPTED_TWICE:
+ return "LM_TEST_SEND_ACCEPTED_TWICE";
+ case CSR_PSKEY_LM_MAX_PAGE_HOLD_TIME:
+ return "LM_MAX_PAGE_HOLD_TIME";
+ case CSR_PSKEY_AFH_ADAPTATION_RESPONSE_TIME:
+ return "AFH_ADAPTATION_RESPONSE_TIME";
+ case CSR_PSKEY_AFH_OPTIONS:
+ return "AFH_OPTIONS";
+ case CSR_PSKEY_AFH_RSSI_RUN_PERIOD:
+ return "AFH_RSSI_RUN_PERIOD";
+ case CSR_PSKEY_AFH_REENABLE_CHANNEL_TIME:
+ return "AFH_REENABLE_CHANNEL_TIME";
+ case CSR_PSKEY_NO_DROP_ON_ACR_MS_FAIL:
+ return "NO_DROP_ON_ACR_MS_FAIL";
+ case CSR_PSKEY_MAX_PRIVATE_KEYS:
+ return "MAX_PRIVATE_KEYS";
+ case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR0:
+ return "PRIVATE_LINK_KEY_BD_ADDR0";
+ case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR1:
+ return "PRIVATE_LINK_KEY_BD_ADDR1";
+ case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR2:
+ return "PRIVATE_LINK_KEY_BD_ADDR2";
+ case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR3:
+ return "PRIVATE_LINK_KEY_BD_ADDR3";
+ case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR4:
+ return "PRIVATE_LINK_KEY_BD_ADDR4";
+ case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR5:
+ return "PRIVATE_LINK_KEY_BD_ADDR5";
+ case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR6:
+ return "PRIVATE_LINK_KEY_BD_ADDR6";
+ case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR7:
+ return "PRIVATE_LINK_KEY_BD_ADDR7";
+ case CSR_PSKEY_LOCAL_SUPPORTED_COMMANDS:
+ return "LOCAL_SUPPORTED_COMMANDS";
+ case CSR_PSKEY_LM_MAX_ABSENCE_INDEX:
+ return "LM_MAX_ABSENCE_INDEX";
+ case CSR_PSKEY_DEVICE_NAME:
+ return "DEVICE_NAME";
+ case CSR_PSKEY_AFH_RSSI_THRESHOLD:
+ return "AFH_RSSI_THRESHOLD";
+ case CSR_PSKEY_LM_CASUAL_SCAN_INTERVAL:
+ return "LM_CASUAL_SCAN_INTERVAL";
+ case CSR_PSKEY_AFH_MIN_MAP_CHANGE:
+ return "AFH_MIN_MAP_CHANGE";
+ case CSR_PSKEY_AFH_RSSI_LP_RUN_PERIOD:
+ return "AFH_RSSI_LP_RUN_PERIOD";
+ case CSR_PSKEY_HCI_LMP_LOCAL_VERSION:
+ return "HCI_LMP_LOCAL_VERSION";
+ case CSR_PSKEY_LMP_REMOTE_VERSION:
+ return "LMP_REMOTE_VERSION";
+ case CSR_PSKEY_HOLD_ERROR_MESSAGE_NUMBER:
+ return "HOLD_ERROR_MESSAGE_NUMBER";
+ case CSR_PSKEY_DFU_ATTRIBUTES:
+ return "DFU_ATTRIBUTES";
+ case CSR_PSKEY_DFU_DETACH_TO:
+ return "DFU_DETACH_TO";
+ case CSR_PSKEY_DFU_TRANSFER_SIZE:
+ return "DFU_TRANSFER_SIZE";
+ case CSR_PSKEY_DFU_ENABLE:
+ return "DFU_ENABLE";
+ case CSR_PSKEY_DFU_LIN_REG_ENABLE:
+ return "DFU_LIN_REG_ENABLE";
+ case CSR_PSKEY_DFUENC_VMAPP_PK_MODULUS_MSB:
+ return "DFUENC_VMAPP_PK_MODULUS_MSB";
+ case CSR_PSKEY_DFUENC_VMAPP_PK_MODULUS_LSB:
+ return "DFUENC_VMAPP_PK_MODULUS_LSB";
+ case CSR_PSKEY_DFUENC_VMAPP_PK_M_DASH:
+ return "DFUENC_VMAPP_PK_M_DASH";
+ case CSR_PSKEY_DFUENC_VMAPP_PK_R2N_MSB:
+ return "DFUENC_VMAPP_PK_R2N_MSB";
+ case CSR_PSKEY_DFUENC_VMAPP_PK_R2N_LSB:
+ return "DFUENC_VMAPP_PK_R2N_LSB";
+ case CSR_PSKEY_BCSP_LM_PS_BLOCK:
+ return "BCSP_LM_PS_BLOCK";
+ case CSR_PSKEY_HOSTIO_FC_PS_BLOCK:
+ return "HOSTIO_FC_PS_BLOCK";
+ case CSR_PSKEY_HOSTIO_PROTOCOL_INFO0:
+ return "HOSTIO_PROTOCOL_INFO0";
+ case CSR_PSKEY_HOSTIO_PROTOCOL_INFO1:
+ return "HOSTIO_PROTOCOL_INFO1";
+ case CSR_PSKEY_HOSTIO_PROTOCOL_INFO2:
+ return "HOSTIO_PROTOCOL_INFO2";
+ case CSR_PSKEY_HOSTIO_PROTOCOL_INFO3:
+ return "HOSTIO_PROTOCOL_INFO3";
+ case CSR_PSKEY_HOSTIO_PROTOCOL_INFO4:
+ return "HOSTIO_PROTOCOL_INFO4";
+ case CSR_PSKEY_HOSTIO_PROTOCOL_INFO5:
+ return "HOSTIO_PROTOCOL_INFO5";
+ case CSR_PSKEY_HOSTIO_PROTOCOL_INFO6:
+ return "HOSTIO_PROTOCOL_INFO6";
+ case CSR_PSKEY_HOSTIO_PROTOCOL_INFO7:
+ return "HOSTIO_PROTOCOL_INFO7";
+ case CSR_PSKEY_HOSTIO_PROTOCOL_INFO8:
+ return "HOSTIO_PROTOCOL_INFO8";
+ case CSR_PSKEY_HOSTIO_PROTOCOL_INFO9:
+ return "HOSTIO_PROTOCOL_INFO9";
+ case CSR_PSKEY_HOSTIO_PROTOCOL_INFO10:
+ return "HOSTIO_PROTOCOL_INFO10";
+ case CSR_PSKEY_HOSTIO_PROTOCOL_INFO11:
+ return "HOSTIO_PROTOCOL_INFO11";
+ case CSR_PSKEY_HOSTIO_PROTOCOL_INFO12:
+ return "HOSTIO_PROTOCOL_INFO12";
+ case CSR_PSKEY_HOSTIO_PROTOCOL_INFO13:
+ return "HOSTIO_PROTOCOL_INFO13";
+ case CSR_PSKEY_HOSTIO_PROTOCOL_INFO14:
+ return "HOSTIO_PROTOCOL_INFO14";
+ case CSR_PSKEY_HOSTIO_PROTOCOL_INFO15:
+ return "HOSTIO_PROTOCOL_INFO15";
+ case CSR_PSKEY_HOSTIO_UART_RESET_TIMEOUT:
+ return "HOSTIO_UART_RESET_TIMEOUT";
+ case CSR_PSKEY_HOSTIO_USE_HCI_EXTN:
+ return "HOSTIO_USE_HCI_EXTN";
+ case CSR_PSKEY_HOSTIO_USE_HCI_EXTN_CCFC:
+ return "HOSTIO_USE_HCI_EXTN_CCFC";
+ case CSR_PSKEY_HOSTIO_HCI_EXTN_PAYLOAD_SIZE:
+ return "HOSTIO_HCI_EXTN_PAYLOAD_SIZE";
+ case CSR_PSKEY_BCSP_LM_CNF_CNT_LIMIT:
+ return "BCSP_LM_CNF_CNT_LIMIT";
+ case CSR_PSKEY_HOSTIO_MAP_SCO_PCM:
+ return "HOSTIO_MAP_SCO_PCM";
+ case CSR_PSKEY_HOSTIO_AWKWARD_PCM_SYNC:
+ return "HOSTIO_AWKWARD_PCM_SYNC";
+ case CSR_PSKEY_HOSTIO_BREAK_POLL_PERIOD:
+ return "HOSTIO_BREAK_POLL_PERIOD";
+ case CSR_PSKEY_HOSTIO_MIN_UART_HCI_SCO_SIZE:
+ return "HOSTIO_MIN_UART_HCI_SCO_SIZE";
+ case CSR_PSKEY_HOSTIO_MAP_SCO_CODEC:
+ return "HOSTIO_MAP_SCO_CODEC";
+ case CSR_PSKEY_PCM_CVSD_TX_HI_FREQ_BOOST:
+ return "PCM_CVSD_TX_HI_FREQ_BOOST";
+ case CSR_PSKEY_PCM_CVSD_RX_HI_FREQ_BOOST:
+ return "PCM_CVSD_RX_HI_FREQ_BOOST";
+ case CSR_PSKEY_PCM_CONFIG32:
+ return "PCM_CONFIG32";
+ case CSR_PSKEY_USE_OLD_BCSP_LE:
+ return "USE_OLD_BCSP_LE";
+ case CSR_PSKEY_PCM_CVSD_USE_NEW_FILTER:
+ return "PCM_CVSD_USE_NEW_FILTER";
+ case CSR_PSKEY_PCM_FORMAT:
+ return "PCM_FORMAT";
+ case CSR_PSKEY_CODEC_OUT_GAIN:
+ return "CODEC_OUT_GAIN";
+ case CSR_PSKEY_CODEC_IN_GAIN:
+ return "CODEC_IN_GAIN";
+ case CSR_PSKEY_CODEC_PIO:
+ return "CODEC_PIO";
+ case CSR_PSKEY_PCM_LOW_JITTER_CONFIG:
+ return "PCM_LOW_JITTER_CONFIG";
+ case CSR_PSKEY_HOSTIO_SCO_PCM_THRESHOLDS:
+ return "HOSTIO_SCO_PCM_THRESHOLDS";
+ case CSR_PSKEY_HOSTIO_SCO_HCI_THRESHOLDS:
+ return "HOSTIO_SCO_HCI_THRESHOLDS";
+ case CSR_PSKEY_HOSTIO_MAP_SCO_PCM_SLOT:
+ return "HOSTIO_MAP_SCO_PCM_SLOT";
+ case CSR_PSKEY_UART_BAUDRATE:
+ return "UART_BAUDRATE";
+ case CSR_PSKEY_UART_CONFIG_BCSP:
+ return "UART_CONFIG_BCSP";
+ case CSR_PSKEY_UART_CONFIG_H4:
+ return "UART_CONFIG_H4";
+ case CSR_PSKEY_UART_CONFIG_H5:
+ return "UART_CONFIG_H5";
+ case CSR_PSKEY_UART_CONFIG_USR:
+ return "UART_CONFIG_USR";
+ case CSR_PSKEY_UART_TX_CRCS:
+ return "UART_TX_CRCS";
+ case CSR_PSKEY_UART_ACK_TIMEOUT:
+ return "UART_ACK_TIMEOUT";
+ case CSR_PSKEY_UART_TX_MAX_ATTEMPTS:
+ return "UART_TX_MAX_ATTEMPTS";
+ case CSR_PSKEY_UART_TX_WINDOW_SIZE:
+ return "UART_TX_WINDOW_SIZE";
+ case CSR_PSKEY_UART_HOST_WAKE:
+ return "UART_HOST_WAKE";
+ case CSR_PSKEY_HOSTIO_THROTTLE_TIMEOUT:
+ return "HOSTIO_THROTTLE_TIMEOUT";
+ case CSR_PSKEY_PCM_ALWAYS_ENABLE:
+ return "PCM_ALWAYS_ENABLE";
+ case CSR_PSKEY_UART_HOST_WAKE_SIGNAL:
+ return "UART_HOST_WAKE_SIGNAL";
+ case CSR_PSKEY_UART_CONFIG_H4DS:
+ return "UART_CONFIG_H4DS";
+ case CSR_PSKEY_H4DS_WAKE_DURATION:
+ return "H4DS_WAKE_DURATION";
+ case CSR_PSKEY_H4DS_MAXWU:
+ return "H4DS_MAXWU";
+ case CSR_PSKEY_H4DS_LE_TIMER_PERIOD:
+ return "H4DS_LE_TIMER_PERIOD";
+ case CSR_PSKEY_H4DS_TWU_TIMER_PERIOD:
+ return "H4DS_TWU_TIMER_PERIOD";
+ case CSR_PSKEY_H4DS_UART_IDLE_TIMER_PERIOD:
+ return "H4DS_UART_IDLE_TIMER_PERIOD";
+ case CSR_PSKEY_ANA_FTRIM:
+ return "ANA_FTRIM";
+ case CSR_PSKEY_WD_TIMEOUT:
+ return "WD_TIMEOUT";
+ case CSR_PSKEY_WD_PERIOD:
+ return "WD_PERIOD";
+ case CSR_PSKEY_HOST_INTERFACE:
+ return "HOST_INTERFACE";
+ case CSR_PSKEY_HQ_HOST_TIMEOUT:
+ return "HQ_HOST_TIMEOUT";
+ case CSR_PSKEY_HQ_ACTIVE:
+ return "HQ_ACTIVE";
+ case CSR_PSKEY_BCCMD_SECURITY_ACTIVE:
+ return "BCCMD_SECURITY_ACTIVE";
+ case CSR_PSKEY_ANA_FREQ:
+ return "ANA_FREQ";
+ case CSR_PSKEY_PIO_PROTECT_MASK:
+ return "PIO_PROTECT_MASK";
+ case CSR_PSKEY_PMALLOC_SIZES:
+ return "PMALLOC_SIZES";
+ case CSR_PSKEY_UART_BAUD_RATE:
+ return "UART_BAUD_RATE";
+ case CSR_PSKEY_UART_CONFIG:
+ return "UART_CONFIG";
+ case CSR_PSKEY_STUB:
+ return "STUB";
+ case CSR_PSKEY_TXRX_PIO_CONTROL:
+ return "TXRX_PIO_CONTROL";
+ case CSR_PSKEY_ANA_RX_LEVEL:
+ return "ANA_RX_LEVEL";
+ case CSR_PSKEY_ANA_RX_FTRIM:
+ return "ANA_RX_FTRIM";
+ case CSR_PSKEY_PSBC_DATA_VERSION:
+ return "PSBC_DATA_VERSION";
+ case CSR_PSKEY_PCM0_ATTENUATION:
+ return "PCM0_ATTENUATION";
+ case CSR_PSKEY_LO_LVL_MAX:
+ return "LO_LVL_MAX";
+ case CSR_PSKEY_LO_ADC_AMPL_MIN:
+ return "LO_ADC_AMPL_MIN";
+ case CSR_PSKEY_LO_ADC_AMPL_MAX:
+ return "LO_ADC_AMPL_MAX";
+ case CSR_PSKEY_IQ_TRIM_CHANNEL:
+ return "IQ_TRIM_CHANNEL";
+ case CSR_PSKEY_IQ_TRIM_GAIN:
+ return "IQ_TRIM_GAIN";
+ case CSR_PSKEY_IQ_TRIM_ENABLE:
+ return "IQ_TRIM_ENABLE";
+ case CSR_PSKEY_TX_OFFSET_HALF_MHZ:
+ return "TX_OFFSET_HALF_MHZ";
+ case CSR_PSKEY_GBL_MISC_ENABLES:
+ return "GBL_MISC_ENABLES";
+ case CSR_PSKEY_UART_SLEEP_TIMEOUT:
+ return "UART_SLEEP_TIMEOUT";
+ case CSR_PSKEY_DEEP_SLEEP_STATE:
+ return "DEEP_SLEEP_STATE";
+ case CSR_PSKEY_IQ_ENABLE_PHASE_TRIM:
+ return "IQ_ENABLE_PHASE_TRIM";
+ case CSR_PSKEY_HCI_HANDLE_FREEZE_PERIOD:
+ return "HCI_HANDLE_FREEZE_PERIOD";
+ case CSR_PSKEY_MAX_FROZEN_HCI_HANDLES:
+ return "MAX_FROZEN_HCI_HANDLES";
+ case CSR_PSKEY_PAGETABLE_DESTRUCTION_DELAY:
+ return "PAGETABLE_DESTRUCTION_DELAY";
+ case CSR_PSKEY_IQ_TRIM_PIO_SETTINGS:
+ return "IQ_TRIM_PIO_SETTINGS";
+ case CSR_PSKEY_USE_EXTERNAL_CLOCK:
+ return "USE_EXTERNAL_CLOCK";
+ case CSR_PSKEY_DEEP_SLEEP_WAKE_CTS:
+ return "DEEP_SLEEP_WAKE_CTS";
+ case CSR_PSKEY_FC_HC2H_FLUSH_DELAY:
+ return "FC_HC2H_FLUSH_DELAY";
+ case CSR_PSKEY_RX_HIGHSIDE:
+ return "RX_HIGHSIDE";
+ case CSR_PSKEY_TX_PRE_LVL:
+ return "TX_PRE_LVL";
+ case CSR_PSKEY_RX_SINGLE_ENDED:
+ return "RX_SINGLE_ENDED";
+ case CSR_PSKEY_TX_FILTER_CONFIG:
+ return "TX_FILTER_CONFIG";
+ case CSR_PSKEY_CLOCK_REQUEST_ENABLE:
+ return "CLOCK_REQUEST_ENABLE";
+ case CSR_PSKEY_RX_MIN_ATTEN:
+ return "RX_MIN_ATTEN";
+ case CSR_PSKEY_XTAL_TARGET_AMPLITUDE:
+ return "XTAL_TARGET_AMPLITUDE";
+ case CSR_PSKEY_PCM_MIN_CPU_CLOCK:
+ return "PCM_MIN_CPU_CLOCK";
+ case CSR_PSKEY_HOST_INTERFACE_PIO_USB:
+ return "HOST_INTERFACE_PIO_USB";
+ case CSR_PSKEY_CPU_IDLE_MODE:
+ return "CPU_IDLE_MODE";
+ case CSR_PSKEY_DEEP_SLEEP_CLEAR_RTS:
+ return "DEEP_SLEEP_CLEAR_RTS";
+ case CSR_PSKEY_RF_RESONANCE_TRIM:
+ return "RF_RESONANCE_TRIM";
+ case CSR_PSKEY_DEEP_SLEEP_PIO_WAKE:
+ return "DEEP_SLEEP_PIO_WAKE";
+ case CSR_PSKEY_DRAIN_BORE_TIMERS:
+ return "DRAIN_BORE_TIMERS";
+ case CSR_PSKEY_DRAIN_TX_POWER_BASE:
+ return "DRAIN_TX_POWER_BASE";
+ case CSR_PSKEY_MODULE_ID:
+ return "MODULE_ID";
+ case CSR_PSKEY_MODULE_DESIGN:
+ return "MODULE_DESIGN";
+ case CSR_PSKEY_MODULE_SECURITY_CODE:
+ return "MODULE_SECURITY_CODE";
+ case CSR_PSKEY_VM_DISABLE:
+ return "VM_DISABLE";
+ case CSR_PSKEY_MOD_MANUF0:
+ return "MOD_MANUF0";
+ case CSR_PSKEY_MOD_MANUF1:
+ return "MOD_MANUF1";
+ case CSR_PSKEY_MOD_MANUF2:
+ return "MOD_MANUF2";
+ case CSR_PSKEY_MOD_MANUF3:
+ return "MOD_MANUF3";
+ case CSR_PSKEY_MOD_MANUF4:
+ return "MOD_MANUF4";
+ case CSR_PSKEY_MOD_MANUF5:
+ return "MOD_MANUF5";
+ case CSR_PSKEY_MOD_MANUF6:
+ return "MOD_MANUF6";
+ case CSR_PSKEY_MOD_MANUF7:
+ return "MOD_MANUF7";
+ case CSR_PSKEY_MOD_MANUF8:
+ return "MOD_MANUF8";
+ case CSR_PSKEY_MOD_MANUF9:
+ return "MOD_MANUF9";
+ case CSR_PSKEY_DUT_VM_DISABLE:
+ return "DUT_VM_DISABLE";
+ case CSR_PSKEY_USR0:
+ return "USR0";
+ case CSR_PSKEY_USR1:
+ return "USR1";
+ case CSR_PSKEY_USR2:
+ return "USR2";
+ case CSR_PSKEY_USR3:
+ return "USR3";
+ case CSR_PSKEY_USR4:
+ return "USR4";
+ case CSR_PSKEY_USR5:
+ return "USR5";
+ case CSR_PSKEY_USR6:
+ return "USR6";
+ case CSR_PSKEY_USR7:
+ return "USR7";
+ case CSR_PSKEY_USR8:
+ return "USR8";
+ case CSR_PSKEY_USR9:
+ return "USR9";
+ case CSR_PSKEY_USR10:
+ return "USR10";
+ case CSR_PSKEY_USR11:
+ return "USR11";
+ case CSR_PSKEY_USR12:
+ return "USR12";
+ case CSR_PSKEY_USR13:
+ return "USR13";
+ case CSR_PSKEY_USR14:
+ return "USR14";
+ case CSR_PSKEY_USR15:
+ return "USR15";
+ case CSR_PSKEY_USR16:
+ return "USR16";
+ case CSR_PSKEY_USR17:
+ return "USR17";
+ case CSR_PSKEY_USR18:
+ return "USR18";
+ case CSR_PSKEY_USR19:
+ return "USR19";
+ case CSR_PSKEY_USR20:
+ return "USR20";
+ case CSR_PSKEY_USR21:
+ return "USR21";
+ case CSR_PSKEY_USR22:
+ return "USR22";
+ case CSR_PSKEY_USR23:
+ return "USR23";
+ case CSR_PSKEY_USR24:
+ return "USR24";
+ case CSR_PSKEY_USR25:
+ return "USR25";
+ case CSR_PSKEY_USR26:
+ return "USR26";
+ case CSR_PSKEY_USR27:
+ return "USR27";
+ case CSR_PSKEY_USR28:
+ return "USR28";
+ case CSR_PSKEY_USR29:
+ return "USR29";
+ case CSR_PSKEY_USR30:
+ return "USR30";
+ case CSR_PSKEY_USR31:
+ return "USR31";
+ case CSR_PSKEY_USR32:
+ return "USR32";
+ case CSR_PSKEY_USR33:
+ return "USR33";
+ case CSR_PSKEY_USR34:
+ return "USR34";
+ case CSR_PSKEY_USR35:
+ return "USR35";
+ case CSR_PSKEY_USR36:
+ return "USR36";
+ case CSR_PSKEY_USR37:
+ return "USR37";
+ case CSR_PSKEY_USR38:
+ return "USR38";
+ case CSR_PSKEY_USR39:
+ return "USR39";
+ case CSR_PSKEY_USR40:
+ return "USR40";
+ case CSR_PSKEY_USR41:
+ return "USR41";
+ case CSR_PSKEY_USR42:
+ return "USR42";
+ case CSR_PSKEY_USR43:
+ return "USR43";
+ case CSR_PSKEY_USR44:
+ return "USR44";
+ case CSR_PSKEY_USR45:
+ return "USR45";
+ case CSR_PSKEY_USR46:
+ return "USR46";
+ case CSR_PSKEY_USR47:
+ return "USR47";
+ case CSR_PSKEY_USR48:
+ return "USR48";
+ case CSR_PSKEY_USR49:
+ return "USR49";
+ case CSR_PSKEY_USB_VERSION:
+ return "USB_VERSION";
+ case CSR_PSKEY_USB_DEVICE_CLASS_CODES:
+ return "USB_DEVICE_CLASS_CODES";
+ case CSR_PSKEY_USB_VENDOR_ID:
+ return "USB_VENDOR_ID";
+ case CSR_PSKEY_USB_PRODUCT_ID:
+ return "USB_PRODUCT_ID";
+ case CSR_PSKEY_USB_MANUF_STRING:
+ return "USB_MANUF_STRING";
+ case CSR_PSKEY_USB_PRODUCT_STRING:
+ return "USB_PRODUCT_STRING";
+ case CSR_PSKEY_USB_SERIAL_NUMBER_STRING:
+ return "USB_SERIAL_NUMBER_STRING";
+ case CSR_PSKEY_USB_CONFIG_STRING:
+ return "USB_CONFIG_STRING";
+ case CSR_PSKEY_USB_ATTRIBUTES:
+ return "USB_ATTRIBUTES";
+ case CSR_PSKEY_USB_MAX_POWER:
+ return "USB_MAX_POWER";
+ case CSR_PSKEY_USB_BT_IF_CLASS_CODES:
+ return "USB_BT_IF_CLASS_CODES";
+ case CSR_PSKEY_USB_LANGID:
+ return "USB_LANGID";
+ case CSR_PSKEY_USB_DFU_CLASS_CODES:
+ return "USB_DFU_CLASS_CODES";
+ case CSR_PSKEY_USB_DFU_PRODUCT_ID:
+ return "USB_DFU_PRODUCT_ID";
+ case CSR_PSKEY_USB_PIO_DETACH:
+ return "USB_PIO_DETACH";
+ case CSR_PSKEY_USB_PIO_WAKEUP:
+ return "USB_PIO_WAKEUP";
+ case CSR_PSKEY_USB_PIO_PULLUP:
+ return "USB_PIO_PULLUP";
+ case CSR_PSKEY_USB_PIO_VBUS:
+ return "USB_PIO_VBUS";
+ case CSR_PSKEY_USB_PIO_WAKE_TIMEOUT:
+ return "USB_PIO_WAKE_TIMEOUT";
+ case CSR_PSKEY_USB_PIO_RESUME:
+ return "USB_PIO_RESUME";
+ case CSR_PSKEY_USB_BT_SCO_IF_CLASS_CODES:
+ return "USB_BT_SCO_IF_CLASS_CODES";
+ case CSR_PSKEY_USB_SUSPEND_PIO_LEVEL:
+ return "USB_SUSPEND_PIO_LEVEL";
+ case CSR_PSKEY_USB_SUSPEND_PIO_DIR:
+ return "USB_SUSPEND_PIO_DIR";
+ case CSR_PSKEY_USB_SUSPEND_PIO_MASK:
+ return "USB_SUSPEND_PIO_MASK";
+ case CSR_PSKEY_USB_ENDPOINT_0_MAX_PACKET_SIZE:
+ return "USB_ENDPOINT_0_MAX_PACKET_SIZE";
+ case CSR_PSKEY_USB_CONFIG:
+ return "USB_CONFIG";
+ case CSR_PSKEY_RADIOTEST_ATTEN_INIT:
+ return "RADIOTEST_ATTEN_INIT";
+ case CSR_PSKEY_RADIOTEST_FIRST_TRIM_TIME:
+ return "RADIOTEST_FIRST_TRIM_TIME";
+ case CSR_PSKEY_RADIOTEST_SUBSEQUENT_TRIM_TIME:
+ return "RADIOTEST_SUBSEQUENT_TRIM_TIME";
+ case CSR_PSKEY_RADIOTEST_LO_LVL_TRIM_ENABLE:
+ return "RADIOTEST_LO_LVL_TRIM_ENABLE";
+ case CSR_PSKEY_RADIOTEST_DISABLE_MODULATION:
+ return "RADIOTEST_DISABLE_MODULATION";
+ case CSR_PSKEY_RFCOMM_FCON_THRESHOLD:
+ return "RFCOMM_FCON_THRESHOLD";
+ case CSR_PSKEY_RFCOMM_FCOFF_THRESHOLD:
+ return "RFCOMM_FCOFF_THRESHOLD";
+ case CSR_PSKEY_IPV6_STATIC_ADDR:
+ return "IPV6_STATIC_ADDR";
+ case CSR_PSKEY_IPV4_STATIC_ADDR:
+ return "IPV4_STATIC_ADDR";
+ case CSR_PSKEY_IPV6_STATIC_PREFIX_LEN:
+ return "IPV6_STATIC_PREFIX_LEN";
+ case CSR_PSKEY_IPV6_STATIC_ROUTER_ADDR:
+ return "IPV6_STATIC_ROUTER_ADDR";
+ case CSR_PSKEY_IPV4_STATIC_SUBNET_MASK:
+ return "IPV4_STATIC_SUBNET_MASK";
+ case CSR_PSKEY_IPV4_STATIC_ROUTER_ADDR:
+ return "IPV4_STATIC_ROUTER_ADDR";
+ case CSR_PSKEY_MDNS_NAME:
+ return "MDNS_NAME";
+ case CSR_PSKEY_FIXED_PIN:
+ return "FIXED_PIN";
+ case CSR_PSKEY_MDNS_PORT:
+ return "MDNS_PORT";
+ case CSR_PSKEY_MDNS_TTL:
+ return "MDNS_TTL";
+ case CSR_PSKEY_MDNS_IPV4_ADDR:
+ return "MDNS_IPV4_ADDR";
+ case CSR_PSKEY_ARP_CACHE_TIMEOUT:
+ return "ARP_CACHE_TIMEOUT";
+ case CSR_PSKEY_HFP_POWER_TABLE:
+ return "HFP_POWER_TABLE";
+ case CSR_PSKEY_DRAIN_BORE_TIMER_COUNTERS:
+ return "DRAIN_BORE_TIMER_COUNTERS";
+ case CSR_PSKEY_DRAIN_BORE_COUNTERS:
+ return "DRAIN_BORE_COUNTERS";
+ case CSR_PSKEY_LOOP_FILTER_TRIM:
+ return "LOOP_FILTER_TRIM";
+ case CSR_PSKEY_DRAIN_BORE_CURRENT_PEAK:
+ return "DRAIN_BORE_CURRENT_PEAK";
+ case CSR_PSKEY_VM_E2_CACHE_LIMIT:
+ return "VM_E2_CACHE_LIMIT";
+ case CSR_PSKEY_FORCE_16MHZ_REF_PIO:
+ return "FORCE_16MHZ_REF_PIO";
+ case CSR_PSKEY_CDMA_LO_REF_LIMITS:
+ return "CDMA_LO_REF_LIMITS";
+ case CSR_PSKEY_CDMA_LO_ERROR_LIMITS:
+ return "CDMA_LO_ERROR_LIMITS";
+ case CSR_PSKEY_CLOCK_STARTUP_DELAY:
+ return "CLOCK_STARTUP_DELAY";
+ case CSR_PSKEY_DEEP_SLEEP_CORRECTION_FACTOR:
+ return "DEEP_SLEEP_CORRECTION_FACTOR";
+ case CSR_PSKEY_TEMPERATURE_CALIBRATION:
+ return "TEMPERATURE_CALIBRATION";
+ case CSR_PSKEY_TEMPERATURE_VS_DELTA_INTERNAL_PA:
+ return "TEMPERATURE_VS_DELTA_INTERNAL_PA";
+ case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_PRE_LVL:
+ return "TEMPERATURE_VS_DELTA_TX_PRE_LVL";
+ case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_BB:
+ return "TEMPERATURE_VS_DELTA_TX_BB";
+ case CSR_PSKEY_TEMPERATURE_VS_DELTA_ANA_FTRIM:
+ return "TEMPERATURE_VS_DELTA_ANA_FTRIM";
+ case CSR_PSKEY_TEST_DELTA_OFFSET:
+ return "TEST_DELTA_OFFSET";
+ case CSR_PSKEY_RX_DYNAMIC_LVL_OFFSET:
+ return "RX_DYNAMIC_LVL_OFFSET";
+ case CSR_PSKEY_TEST_FORCE_OFFSET:
+ return "TEST_FORCE_OFFSET";
+ case CSR_PSKEY_RF_TRAP_BAD_DIVISION_RATIOS:
+ return "RF_TRAP_BAD_DIVISION_RATIOS";
+ case CSR_PSKEY_RADIOTEST_CDMA_LO_REF_LIMITS:
+ return "RADIOTEST_CDMA_LO_REF_LIMITS";
+ case CSR_PSKEY_INITIAL_BOOTMODE:
+ return "INITIAL_BOOTMODE";
+ case CSR_PSKEY_ONCHIP_HCI_CLIENT:
+ return "ONCHIP_HCI_CLIENT";
+ case CSR_PSKEY_RX_ATTEN_BACKOFF:
+ return "RX_ATTEN_BACKOFF";
+ case CSR_PSKEY_RX_ATTEN_UPDATE_RATE:
+ return "RX_ATTEN_UPDATE_RATE";
+ case CSR_PSKEY_SYNTH_TXRX_THRESHOLDS:
+ return "SYNTH_TXRX_THRESHOLDS";
+ case CSR_PSKEY_MIN_WAIT_STATES:
+ return "MIN_WAIT_STATES";
+ case CSR_PSKEY_RSSI_CORRECTION:
+ return "RSSI_CORRECTION";
+ case CSR_PSKEY_SCHED_THROTTLE_TIMEOUT:
+ return "SCHED_THROTTLE_TIMEOUT";
+ case CSR_PSKEY_DEEP_SLEEP_USE_EXTERNAL_CLOCK:
+ return "DEEP_SLEEP_USE_EXTERNAL_CLOCK";
+ case CSR_PSKEY_TRIM_RADIO_FILTERS:
+ return "TRIM_RADIO_FILTERS";
+ case CSR_PSKEY_TRANSMIT_OFFSET:
+ return "TRANSMIT_OFFSET";
+ case CSR_PSKEY_USB_VM_CONTROL:
+ return "USB_VM_CONTROL";
+ case CSR_PSKEY_MR_ANA_RX_FTRIM:
+ return "MR_ANA_RX_FTRIM";
+ case CSR_PSKEY_I2C_CONFIG:
+ return "I2C_CONFIG";
+ case CSR_PSKEY_IQ_LVL_RX:
+ return "IQ_LVL_RX";
+ case CSR_PSKEY_MR_TX_FILTER_CONFIG:
+ return "MR_TX_FILTER_CONFIG";
+ case CSR_PSKEY_MR_TX_CONFIG2:
+ return "MR_TX_CONFIG2";
+ case CSR_PSKEY_USB_DONT_RESET_BOOTMODE_ON_HOST_RESET:
+ return "USB_DONT_RESET_BOOTMODE_ON_HOST_RESET";
+ case CSR_PSKEY_LC_USE_THROTTLING:
+ return "LC_USE_THROTTLING";
+ case CSR_PSKEY_CHARGER_TRIM:
+ return "CHARGER_TRIM";
+ case CSR_PSKEY_CLOCK_REQUEST_FEATURES:
+ return "CLOCK_REQUEST_FEATURES";
+ case CSR_PSKEY_TRANSMIT_OFFSET_CLASS1:
+ return "TRANSMIT_OFFSET_CLASS1";
+ case CSR_PSKEY_TX_AVOID_PA_CLASS1_PIO:
+ return "TX_AVOID_PA_CLASS1_PIO";
+ case CSR_PSKEY_MR_PIO_CONFIG:
+ return "MR_PIO_CONFIG";
+ case CSR_PSKEY_UART_CONFIG2:
+ return "UART_CONFIG2";
+ case CSR_PSKEY_CLASS1_IQ_LVL:
+ return "CLASS1_IQ_LVL";
+ case CSR_PSKEY_CLASS1_TX_CONFIG2:
+ return "CLASS1_TX_CONFIG2";
+ case CSR_PSKEY_TEMPERATURE_VS_DELTA_INTERNAL_PA_CLASS1:
+ return "TEMPERATURE_VS_DELTA_INTERNAL_PA_CLASS1";
+ case CSR_PSKEY_TEMPERATURE_VS_DELTA_EXTERNAL_PA_CLASS1:
+ return "TEMPERATURE_VS_DELTA_EXTERNAL_PA_CLASS1";
+ case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_PRE_LVL_MR:
+ return "TEMPERATURE_VS_DELTA_TX_PRE_LVL_MR";
+ case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_BB_MR_HEADER:
+ return "TEMPERATURE_VS_DELTA_TX_BB_MR_HEADER";
+ case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_BB_MR_PAYLOAD:
+ return "TEMPERATURE_VS_DELTA_TX_BB_MR_PAYLOAD";
+ case CSR_PSKEY_RX_MR_EQ_TAPS:
+ return "RX_MR_EQ_TAPS";
+ case CSR_PSKEY_TX_PRE_LVL_CLASS1:
+ return "TX_PRE_LVL_CLASS1";
+ case CSR_PSKEY_ANALOGUE_ATTENUATOR:
+ return "ANALOGUE_ATTENUATOR";
+ case CSR_PSKEY_MR_RX_FILTER_TRIM:
+ return "MR_RX_FILTER_TRIM";
+ case CSR_PSKEY_MR_RX_FILTER_RESPONSE:
+ return "MR_RX_FILTER_RESPONSE";
+ case CSR_PSKEY_PIO_WAKEUP_STATE:
+ return "PIO_WAKEUP_STATE";
+ case CSR_PSKEY_MR_TX_IF_ATTEN_OFF_TEMP:
+ return "MR_TX_IF_ATTEN_OFF_TEMP";
+ case CSR_PSKEY_LO_DIV_LATCH_BYPASS:
+ return "LO_DIV_LATCH_BYPASS";
+ case CSR_PSKEY_LO_VCO_STANDBY:
+ return "LO_VCO_STANDBY";
+ case CSR_PSKEY_SLOW_CLOCK_FILTER_SHIFT:
+ return "SLOW_CLOCK_FILTER_SHIFT";
+ case CSR_PSKEY_SLOW_CLOCK_FILTER_DIVIDER:
+ return "SLOW_CLOCK_FILTER_DIVIDER";
+ case CSR_PSKEY_USB_ATTRIBUTES_POWER:
+ return "USB_ATTRIBUTES_POWER";
+ case CSR_PSKEY_USB_ATTRIBUTES_WAKEUP:
+ return "USB_ATTRIBUTES_WAKEUP";
+ case CSR_PSKEY_DFU_ATTRIBUTES_MANIFESTATION_TOLERANT:
+ return "DFU_ATTRIBUTES_MANIFESTATION_TOLERANT";
+ case CSR_PSKEY_DFU_ATTRIBUTES_CAN_UPLOAD:
+ return "DFU_ATTRIBUTES_CAN_UPLOAD";
+ case CSR_PSKEY_DFU_ATTRIBUTES_CAN_DOWNLOAD:
+ return "DFU_ATTRIBUTES_CAN_DOWNLOAD";
+ case CSR_PSKEY_UART_CONFIG_STOP_BITS:
+ return "UART_CONFIG_STOP_BITS";
+ case CSR_PSKEY_UART_CONFIG_PARITY_BIT:
+ return "UART_CONFIG_PARITY_BIT";
+ case CSR_PSKEY_UART_CONFIG_FLOW_CTRL_EN:
+ return "UART_CONFIG_FLOW_CTRL_EN";
+ case CSR_PSKEY_UART_CONFIG_RTS_AUTO_EN:
+ return "UART_CONFIG_RTS_AUTO_EN";
+ case CSR_PSKEY_UART_CONFIG_RTS:
+ return "UART_CONFIG_RTS";
+ case CSR_PSKEY_UART_CONFIG_TX_ZERO_EN:
+ return "UART_CONFIG_TX_ZERO_EN";
+ case CSR_PSKEY_UART_CONFIG_NON_BCSP_EN:
+ return "UART_CONFIG_NON_BCSP_EN";
+ case CSR_PSKEY_UART_CONFIG_RX_RATE_DELAY:
+ return "UART_CONFIG_RX_RATE_DELAY";
+ case CSR_PSKEY_UART_SEQ_TIMEOUT:
+ return "UART_SEQ_TIMEOUT";
+ case CSR_PSKEY_UART_SEQ_RETRIES:
+ return "UART_SEQ_RETRIES";
+ case CSR_PSKEY_UART_SEQ_WINSIZE:
+ return "UART_SEQ_WINSIZE";
+ case CSR_PSKEY_UART_USE_CRC_ON_TX:
+ return "UART_USE_CRC_ON_TX";
+ case CSR_PSKEY_UART_HOST_INITIAL_STATE:
+ return "UART_HOST_INITIAL_STATE";
+ case CSR_PSKEY_UART_HOST_ATTENTION_SPAN:
+ return "UART_HOST_ATTENTION_SPAN";
+ case CSR_PSKEY_UART_HOST_WAKEUP_TIME:
+ return "UART_HOST_WAKEUP_TIME";
+ case CSR_PSKEY_UART_HOST_WAKEUP_WAIT:
+ return "UART_HOST_WAKEUP_WAIT";
+ case CSR_PSKEY_BCSP_LM_MODE:
+ return "BCSP_LM_MODE";
+ case CSR_PSKEY_BCSP_LM_SYNC_RETRIES:
+ return "BCSP_LM_SYNC_RETRIES";
+ case CSR_PSKEY_BCSP_LM_TSHY:
+ return "BCSP_LM_TSHY";
+ case CSR_PSKEY_UART_DFU_CONFIG_STOP_BITS:
+ return "UART_DFU_CONFIG_STOP_BITS";
+ case CSR_PSKEY_UART_DFU_CONFIG_PARITY_BIT:
+ return "UART_DFU_CONFIG_PARITY_BIT";
+ case CSR_PSKEY_UART_DFU_CONFIG_FLOW_CTRL_EN:
+ return "UART_DFU_CONFIG_FLOW_CTRL_EN";
+ case CSR_PSKEY_UART_DFU_CONFIG_RTS_AUTO_EN:
+ return "UART_DFU_CONFIG_RTS_AUTO_EN";
+ case CSR_PSKEY_UART_DFU_CONFIG_RTS:
+ return "UART_DFU_CONFIG_RTS";
+ case CSR_PSKEY_UART_DFU_CONFIG_TX_ZERO_EN:
+ return "UART_DFU_CONFIG_TX_ZERO_EN";
+ case CSR_PSKEY_UART_DFU_CONFIG_NON_BCSP_EN:
+ return "UART_DFU_CONFIG_NON_BCSP_EN";
+ case CSR_PSKEY_UART_DFU_CONFIG_RX_RATE_DELAY:
+ return "UART_DFU_CONFIG_RX_RATE_DELAY";
+ case CSR_PSKEY_AMUX_AIO0:
+ return "AMUX_AIO0";
+ case CSR_PSKEY_AMUX_AIO1:
+ return "AMUX_AIO1";
+ case CSR_PSKEY_AMUX_AIO2:
+ return "AMUX_AIO2";
+ case CSR_PSKEY_AMUX_AIO3:
+ return "AMUX_AIO3";
+ case CSR_PSKEY_LOCAL_NAME_SIMPLIFIED:
+ return "LOCAL_NAME_SIMPLIFIED";
+ case CSR_PSKEY_EXTENDED_STUB:
+ return "EXTENDED_STUB";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+int csr_write_varid_valueless(int dd, uint16_t seqnum, uint16_t varid)
+{
+ unsigned char cmd[] = { 0x02, 0x00, 0x09, 0x00,
+ seqnum & 0xff, seqnum >> 8, varid & 0xff, varid >> 8, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ unsigned char cp[254], rp[254];
+ struct hci_request rq;
+
+ memset(&cp, 0, sizeof(cp));
+ cp[0] = 0xc2;
+ memcpy(cp + 1, cmd, sizeof(cmd));
+
+ switch (varid) {
+ case CSR_VARID_COLD_RESET:
+ case CSR_VARID_WARM_RESET:
+ case CSR_VARID_COLD_HALT:
+ case CSR_VARID_WARM_HALT:
+ return hci_send_cmd(dd, OGF_VENDOR_CMD, 0x00, sizeof(cmd) + 1, cp);
+ }
+
+ 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;
+}
+
+int csr_write_varid_complex(int dd, uint16_t seqnum, uint16_t varid, uint8_t *value, uint16_t length)
+{
+ unsigned char cmd[] = { 0x02, 0x00, ((length / 2) + 5) & 0xff, ((length / 2) + 5) >> 8,
+ seqnum & 0xff, seqnum >> 8, varid & 0xff, varid >> 8, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ unsigned char cp[254], rp[254];
+ struct hci_request rq;
+
+ memset(&cp, 0, sizeof(cp));
+ cp[0] = 0xc2;
+ memcpy(cp + 1, cmd, sizeof(cmd));
+ memcpy(cp + 11, value, length);
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_VENDOR_CMD;
+ rq.ocf = 0x00;
+ rq.event = EVT_VENDOR;
+ rq.cparam = cp;
+ rq.clen = sizeof(cmd) + length + 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;
+}
+
+int csr_read_varid_complex(int dd, uint16_t seqnum, uint16_t varid, uint8_t *value, uint16_t length)
+{
+ unsigned char cmd[] = { 0x00, 0x00, ((length / 2) + 5) & 0xff, ((length / 2) + 5) >> 8,
+ seqnum & 0xff, seqnum >> 8, varid & 0xff, varid >> 8, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ unsigned char cp[254], rp[254];
+ struct hci_request rq;
+
+ memset(&cp, 0, sizeof(cp));
+ cp[0] = 0xc2;
+ memcpy(cp + 1, cmd, sizeof(cmd));
+ memcpy(cp + 11, value, length);
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_VENDOR_CMD;
+ rq.ocf = 0x00;
+ rq.event = EVT_VENDOR;
+ rq.cparam = cp;
+ rq.clen = sizeof(cmd) + length + 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;
+ }
+
+ memcpy(value, rp + 11, length);
+
+ return 0;
+}
+
+int csr_read_varid_uint16(int dd, uint16_t seqnum, uint16_t varid, uint16_t *value)
+{
+ unsigned char cmd[] = { 0x00, 0x00, 0x09, 0x00,
+ seqnum & 0xff, seqnum >> 8, varid & 0xff, varid >> 8, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ unsigned char cp[254], rp[254];
+ struct hci_request rq;
+
+ 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;
+ }
+
+ *value = rp[11] + (rp[12] << 8);
+
+ return 0;
+}
+
+int csr_read_varid_uint32(int dd, uint16_t seqnum, uint16_t varid, uint32_t *value)
+{
+ unsigned char cmd[] = { 0x00, 0x00, 0x09, 0x00,
+ seqnum & 0xff, seqnum >> 8, varid & 0xff, varid >> 8, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ unsigned char cp[254], rp[254];
+ struct hci_request rq;
+
+ 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;
+ }
+
+ *value = ((rp[11] + (rp[12] << 8)) << 16) + (rp[13] + (rp[14] << 8));
+
+ return 0;
+}
+
+int csr_read_pskey_complex(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint8_t *value, uint16_t length)
+{
+ unsigned char cmd[] = { 0x00, 0x00, ((length / 2) + 8) & 0xff, ((length / 2) + 8) >> 8,
+ seqnum & 0xff, seqnum >> 8, 0x03, 0x70, 0x00, 0x00,
+ pskey & 0xff, pskey >> 8,
+ (length / 2) & 0xff, (length / 2) >> 8,
+ stores & 0xff, stores >> 8, 0x00, 0x00 };
+
+ unsigned char cp[254], rp[254];
+ struct hci_request rq;
+
+ 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) + length - 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;
+ }
+
+ memcpy(value, rp + 17, length);
+
+ return 0;
+}
+
+int csr_write_pskey_complex(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint8_t *value, uint16_t length)
+{
+ unsigned char cmd[] = { 0x02, 0x00, ((length / 2) + 8) & 0xff, ((length / 2) + 8) >> 8,
+ seqnum & 0xff, seqnum >> 8, 0x03, 0x70, 0x00, 0x00,
+ pskey & 0xff, pskey >> 8,
+ (length / 2) & 0xff, (length / 2) >> 8,
+ stores & 0xff, stores >> 8, 0x00, 0x00 };
+
+ unsigned char cp[254], rp[254];
+ struct hci_request rq;
+
+ memset(&cp, 0, sizeof(cp));
+ cp[0] = 0xc2;
+ memcpy(cp + 1, cmd, sizeof(cmd));
+
+ memcpy(cp + 17, value, length);
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_VENDOR_CMD;
+ rq.ocf = 0x00;
+ rq.event = EVT_VENDOR;
+ rq.cparam = cp;
+ rq.clen = sizeof(cmd) + length - 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;
+}
+
+int csr_read_pskey_uint16(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint16_t *value)
+{
+ uint8_t array[2] = { 0x00, 0x00 };
+ int err;
+
+ err = csr_read_pskey_complex(dd, seqnum, pskey, stores, array, 2);
+
+ *value = array[0] + (array[1] << 8);
+
+ return err;
+}
+
+int csr_write_pskey_uint16(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint16_t value)
+{
+ uint8_t array[2] = { value & 0xff, value >> 8 };
+
+ return csr_write_pskey_complex(dd, seqnum, pskey, stores, array, 2);
+}
+
+int csr_read_pskey_uint32(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint32_t *value)
+{
+ uint8_t array[4] = { 0x00, 0x00, 0x00, 0x00 };
+ int err;
+
+ err = csr_read_pskey_complex(dd, seqnum, pskey, stores, array, 4);
+
+ *value = ((array[0] + (array[1] << 8)) << 16) +
+ (array[2] + (array[3] << 8));
+
+ return err;
+}
+
+int csr_write_pskey_uint32(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint32_t value)
+{
+ uint8_t array[4] = { (value & 0xff0000) >> 16, value >> 24,
+ value & 0xff, (value & 0xff00) >> 8 };
+
+ return csr_write_pskey_complex(dd, seqnum, pskey, stores, array, 4);
+}
+
+int psr_put(uint16_t pskey, uint8_t *value, uint16_t size)
+{
+ struct psr_data *item;
+
+ item = malloc(sizeof(*item));
+ if (!item)
+ return -ENOMEM;
+
+ item->pskey = pskey;
+
+ if (size > 0) {
+ item->value = malloc(size);
+ if (!item->value) {
+ free(item);
+ return -ENOMEM;
+ }
+
+ memcpy(item->value, value, size);
+ item->size = size;
+ } else {
+ item->value = NULL;
+ item->size = 0;
+ }
+
+ item->next = NULL;
+
+ if (!head)
+ head = item;
+ else
+ tail->next = item;
+
+ tail = item;
+
+ return 0;
+}
+
+int psr_get(uint16_t *pskey, uint8_t *value, uint16_t *size)
+{
+ struct psr_data *item = head;
+
+ if (!head)
+ return -ENOENT;
+
+ *pskey = item->pskey;
+
+ if (item->value) {
+ if (value && item->size > 0)
+ memcpy(value, item->value, item->size);
+ free(item->value);
+ *size = item->size;
+ } else
+ *size = 0;
+
+ if (head == tail)
+ tail = NULL;
+
+ head = head->next;
+ free(item);
+
+ return 0;
+}
+
+static int parse_line(char *str)
+{
+ uint8_t array[256];
+ uint16_t value, pskey, length = 0;
+ char *off, *end;
+
+ pskey = strtol(str + 1, NULL, 16);
+ off = strstr(str, "=") + 1;
+ if (!off)
+ return -EIO;
+
+ while (1) {
+ value = strtol(off, &end, 16);
+ if (value == 0 && off == end)
+ break;
+
+ array[length++] = value & 0xff;
+ array[length++] = value >> 8;
+
+ if (*end == '\0')
+ break;
+
+ off = end + 1;
+ }
+
+ return psr_put(pskey, array, length);
+}
+
+int psr_read(const char *filename)
+{
+ struct stat st;
+ char *str, *map, *off, *end;
+ int fd, err = 0;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ return fd;
+
+ if (fstat(fd, &st) < 0) {
+ err = -errno;
+ goto close;
+ }
+
+ map = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (!map || map == MAP_FAILED) {
+ err = -errno;
+ goto close;
+ }
+
+ off = map;
+
+ while (1) {
+ if (*off == '\r' || *off == '\n') {
+ off++;
+ continue;
+ }
+
+ end = strpbrk(off, "\r\n");
+ if (!end)
+ break;
+
+ str = malloc(end - off + 1);
+ if (!str)
+ break;
+
+ memset(str, 0, end - off + 1);
+ strncpy(str, off, end - off);
+ if (*str == '&')
+ parse_line(str);
+
+ free(str);
+ off = end + 1;
+ }
+
+ munmap(map, st.st_size);
+
+close:
+ close(fd);
+
+ return err;
+}
+
+int psr_print(void)
+{
+ uint8_t array[256];
+ uint16_t pskey, length;
+ char *str, val[7];
+ int i;
+
+ while (1) {
+ if (psr_get(&pskey, array, &length) < 0)
+ break;
+
+ str = csr_pskeytoval(pskey);
+ if (!strcasecmp(str, "UNKNOWN")) {
+ sprintf(val, "0x%04x", pskey);
+ str = NULL;
+ }
+
+ printf("// %s%s\n&%04x =", str ? "PSKEY_" : "",
+ str ? str : val, pskey);
+ for (i = 0; i < length / 2; i++)
+ printf(" %02x%02x", array[i * 2 + 1], array[i * 2]);
+ printf("\n");
+ }
+
+ return 0;
+}
diff --git a/tools/csr.h b/tools/csr.h
new file mode 100644
index 0000000..8b94d7b
--- /dev/null
+++ b/tools/csr.h
@@ -0,0 +1,553 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2003-2010 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
+ *
+ */
+
+#include <stdint.h>
+#include <termios.h>
+
+#define CSR_VARID_PS_CLR_ALL 0x000b /* valueless */
+#define CSR_VARID_PS_FACTORY_SET 0x000c /* valueless */
+#define CSR_VARID_PS_CLR_ALL_STORES 0x082d /* uint16 */
+#define CSR_VARID_BC01_STATUS 0x2801 /* uint16 */
+#define CSR_VARID_BUILDID 0x2819 /* uint16 */
+#define CSR_VARID_CHIPVER 0x281a /* uint16 */
+#define CSR_VARID_CHIPREV 0x281b /* uint16 */
+#define CSR_VARID_INTERFACE_VERSION 0x2825 /* uint16 */
+#define CSR_VARID_RAND 0x282a /* uint16 */
+#define CSR_VARID_MAX_CRYPT_KEY_LENGTH 0x282c /* uint16 */
+#define CSR_VARID_CHIPANAREV 0x2836 /* uint16 */
+#define CSR_VARID_BUILDID_LOADER 0x2838 /* uint16 */
+#define CSR_VARID_BT_CLOCK 0x2c00 /* uint32 */
+#define CSR_VARID_PS_NEXT 0x3005 /* complex */
+#define CSR_VARID_PS_SIZE 0x3006 /* complex */
+#define CSR_VARID_CRYPT_KEY_LENGTH 0x3008 /* complex */
+#define CSR_VARID_PICONET_INSTANCE 0x3009 /* complex */
+#define CSR_VARID_GET_CLR_EVT 0x300a /* complex */
+#define CSR_VARID_GET_NEXT_BUILDDEF 0x300b /* complex */
+#define CSR_VARID_PS_MEMORY_TYPE 0x3012 /* complex */
+#define CSR_VARID_READ_BUILD_NAME 0x301c /* complex */
+#define CSR_VARID_COLD_RESET 0x4001 /* valueless */
+#define CSR_VARID_WARM_RESET 0x4002 /* valueless */
+#define CSR_VARID_COLD_HALT 0x4003 /* valueless */
+#define CSR_VARID_WARM_HALT 0x4004 /* valueless */
+#define CSR_VARID_INIT_BT_STACK 0x4005 /* valueless */
+#define CSR_VARID_ACTIVATE_BT_STACK 0x4006 /* valueless */
+#define CSR_VARID_ENABLE_TX 0x4007 /* valueless */
+#define CSR_VARID_DISABLE_TX 0x4008 /* valueless */
+#define CSR_VARID_RECAL 0x4009 /* valueless */
+#define CSR_VARID_PS_FACTORY_RESTORE 0x400d /* valueless */
+#define CSR_VARID_PS_FACTORY_RESTORE_ALL 0x400e /* valueless */
+#define CSR_VARID_PS_DEFRAG_RESET 0x400f /* valueless */
+#define CSR_VARID_KILL_VM_APPLICATION 0x4010 /* valueless */
+#define CSR_VARID_HOPPING_ON 0x4011 /* valueless */
+#define CSR_VARID_CANCEL_PAGE 0x4012 /* valueless */
+#define CSR_VARID_PS_CLR 0x4818 /* uint16 */
+#define CSR_VARID_MAP_SCO_PCM 0x481c /* uint16 */
+#define CSR_VARID_SINGLE_CHAN 0x482e /* uint16 */
+#define CSR_VARID_RADIOTEST 0x5004 /* complex */
+#define CSR_VARID_PS_CLR_STORES 0x500c /* complex */
+#define CSR_VARID_NO_VARIABLE 0x6000 /* valueless */
+#define CSR_VARID_CONFIG_UART 0x6802 /* uint16 */
+#define CSR_VARID_PANIC_ARG 0x6805 /* uint16 */
+#define CSR_VARID_FAULT_ARG 0x6806 /* uint16 */
+#define CSR_VARID_MAX_TX_POWER 0x6827 /* int8 */
+#define CSR_VARID_DEFAULT_TX_POWER 0x682b /* int8 */
+#define CSR_VARID_PS 0x7003 /* complex */
+
+#define CSR_PSKEY_BDADDR 0x0001 /* bdaddr / uint16[] = { 0x00A5A5, 0x5b, 0x0002 } */
+#define CSR_PSKEY_COUNTRYCODE 0x0002 /* uint16 */
+#define CSR_PSKEY_CLASSOFDEVICE 0x0003 /* bdcod */
+#define CSR_PSKEY_DEVICE_DRIFT 0x0004 /* uint16 */
+#define CSR_PSKEY_DEVICE_JITTER 0x0005 /* uint16 */
+#define CSR_PSKEY_MAX_ACLS 0x000d /* uint16 */
+#define CSR_PSKEY_MAX_SCOS 0x000e /* uint16 */
+#define CSR_PSKEY_MAX_REMOTE_MASTERS 0x000f /* uint16 */
+#define CSR_PSKEY_ENABLE_MASTERY_WITH_SLAVERY 0x0010 /* bool */
+#define CSR_PSKEY_H_HC_FC_MAX_ACL_PKT_LEN 0x0011 /* uint16 */
+#define CSR_PSKEY_H_HC_FC_MAX_SCO_PKT_LEN 0x0012 /* uint8 */
+#define CSR_PSKEY_H_HC_FC_MAX_ACL_PKTS 0x0013 /* uint16 */
+#define CSR_PSKEY_H_HC_FC_MAX_SCO_PKTS 0x0014 /* uint16 */
+#define CSR_PSKEY_LC_FC_BUFFER_LOW_WATER_MARK 0x0015 /* lc_fc_lwm */
+#define CSR_PSKEY_LC_MAX_TX_POWER 0x0017 /* int16 */
+#define CSR_PSKEY_TX_GAIN_RAMP 0x001d /* uint16 */
+#define CSR_PSKEY_LC_POWER_TABLE 0x001e /* power_setting[] */
+#define CSR_PSKEY_LC_PEER_POWER_PERIOD 0x001f /* TIME */
+#define CSR_PSKEY_LC_FC_POOLS_LOW_WATER_MARK 0x0020 /* lc_fc_lwm */
+#define CSR_PSKEY_LC_DEFAULT_TX_POWER 0x0021 /* int16 */
+#define CSR_PSKEY_LC_RSSI_GOLDEN_RANGE 0x0022 /* uint8 */
+#define CSR_PSKEY_LC_COMBO_DISABLE_PIO_MASK 0x0028 /* uint16[] */
+#define CSR_PSKEY_LC_COMBO_PRIORITY_PIO_MASK 0x0029 /* uint16[] */
+#define CSR_PSKEY_LC_COMBO_DOT11_CHANNEL_PIO_BASE 0x002a /* uint16 */
+#define CSR_PSKEY_LC_COMBO_DOT11_BLOCK_CHANNELS 0x002b /* uint16 */
+#define CSR_PSKEY_LC_MAX_TX_POWER_NO_RSSI 0x002d /* int8 */
+#define CSR_PSKEY_LC_CONNECTION_RX_WINDOW 0x002e /* uint16 */
+#define CSR_PSKEY_LC_COMBO_DOT11_TX_PROTECTION_MODE 0x0030 /* uint16 */
+#define CSR_PSKEY_LC_ENHANCED_POWER_TABLE 0x0031 /* enhanced_power_setting[] */
+#define CSR_PSKEY_LC_WIDEBAND_RSSI_CONFIG 0x0032 /* wideband_rssi_config */
+#define CSR_PSKEY_LC_COMBO_DOT11_PRIORITY_LEAD 0x0033 /* uint16 */
+#define CSR_PSKEY_BT_CLOCK_INIT 0x0034 /* uint32 */
+#define CSR_PSKEY_TX_MR_MOD_DELAY 0x0038 /* uint8 */
+#define CSR_PSKEY_RX_MR_SYNC_TIMING 0x0039 /* uint16 */
+#define CSR_PSKEY_RX_MR_SYNC_CONFIG 0x003a /* uint16 */
+#define CSR_PSKEY_LC_LOST_SYNC_SLOTS 0x003b /* uint16 */
+#define CSR_PSKEY_RX_MR_SAMP_CONFIG 0x003c /* uint16 */
+#define CSR_PSKEY_AGC_HYST_LEVELS 0x003d /* agc_hyst_config */
+#define CSR_PSKEY_RX_LEVEL_LOW_SIGNAL 0x003e /* uint16 */
+#define CSR_PSKEY_AGC_IQ_LVL_VALUES 0x003f /* IQ_LVL_VAL[] */
+#define CSR_PSKEY_MR_FTRIM_OFFSET_12DB 0x0040 /* uint16 */
+#define CSR_PSKEY_MR_FTRIM_OFFSET_6DB 0x0041 /* uint16 */
+#define CSR_PSKEY_NO_CAL_ON_BOOT 0x0042 /* bool */
+#define CSR_PSKEY_RSSI_HI_TARGET 0x0043 /* uint8 */
+#define CSR_PSKEY_PREFERRED_MIN_ATTENUATION 0x0044 /* uint8 */
+#define CSR_PSKEY_LC_COMBO_DOT11_PRIORITY_OVERRIDE 0x0045 /* bool */
+#define CSR_PSKEY_LC_MULTISLOT_HOLDOFF 0x0047 /* TIME */
+#define CSR_PSKEY_FREE_KEY_PIGEON_HOLE 0x00c9 /* uint16 */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR0 0x00ca /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR1 0x00cb /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR2 0x00cc /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR3 0x00cd /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR4 0x00ce /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR5 0x00cf /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR6 0x00d0 /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR7 0x00d1 /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR8 0x00d2 /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR9 0x00d3 /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR10 0x00d4 /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR11 0x00d5 /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR12 0x00d6 /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR13 0x00d7 /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR14 0x00d8 /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR15 0x00d9 /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_ENC_KEY_LMIN 0x00da /* uint16 */
+#define CSR_PSKEY_ENC_KEY_LMAX 0x00db /* uint16 */
+#define CSR_PSKEY_LOCAL_SUPPORTED_FEATURES 0x00ef /* uint16[] = { 0xffff, 0xfe8f, 0xf99b, 0x8000 }*/
+#define CSR_PSKEY_LM_USE_UNIT_KEY 0x00f0 /* bool */
+#define CSR_PSKEY_HCI_NOP_DISABLE 0x00f2 /* bool */
+#define CSR_PSKEY_LM_MAX_EVENT_FILTERS 0x00f4 /* uint8 */
+#define CSR_PSKEY_LM_USE_ENC_MODE_BROADCAST 0x00f5 /* bool */
+#define CSR_PSKEY_LM_TEST_SEND_ACCEPTED_TWICE 0x00f6 /* bool */
+#define CSR_PSKEY_LM_MAX_PAGE_HOLD_TIME 0x00f7 /* uint16 */
+#define CSR_PSKEY_AFH_ADAPTATION_RESPONSE_TIME 0x00f8 /* uint16 */
+#define CSR_PSKEY_AFH_OPTIONS 0x00f9 /* uint16 */
+#define CSR_PSKEY_AFH_RSSI_RUN_PERIOD 0x00fa /* uint16 */
+#define CSR_PSKEY_AFH_REENABLE_CHANNEL_TIME 0x00fb /* uint16 */
+#define CSR_PSKEY_NO_DROP_ON_ACR_MS_FAIL 0x00fc /* bool */
+#define CSR_PSKEY_MAX_PRIVATE_KEYS 0x00fd /* uint8 */
+#define CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR0 0x00fe /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR1 0x00ff /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR2 0x0100 /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR3 0x0101 /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR4 0x0102 /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR5 0x0103 /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR6 0x0104 /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR7 0x0105 /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LOCAL_SUPPORTED_COMMANDS 0x0106 /* uint16[] = { 0xffff, 0x03ff, 0xfffe, 0xffff, 0xffff, 0xffff, 0x0ff3, 0xfff8, 0x003f } */
+#define CSR_PSKEY_LM_MAX_ABSENCE_INDEX 0x0107 /* uint8 */
+#define CSR_PSKEY_DEVICE_NAME 0x0108 /* uint16[] */
+#define CSR_PSKEY_AFH_RSSI_THRESHOLD 0x0109 /* uint16 */
+#define CSR_PSKEY_LM_CASUAL_SCAN_INTERVAL 0x010a /* uint16 */
+#define CSR_PSKEY_AFH_MIN_MAP_CHANGE 0x010b /* uint16[] */
+#define CSR_PSKEY_AFH_RSSI_LP_RUN_PERIOD 0x010c /* uint16 */
+#define CSR_PSKEY_HCI_LMP_LOCAL_VERSION 0x010d /* uint16 */
+#define CSR_PSKEY_LMP_REMOTE_VERSION 0x010e /* uint8 */
+#define CSR_PSKEY_HOLD_ERROR_MESSAGE_NUMBER 0x0113 /* uint16 */
+#define CSR_PSKEY_DFU_ATTRIBUTES 0x0136 /* uint8 */
+#define CSR_PSKEY_DFU_DETACH_TO 0x0137 /* uint16 */
+#define CSR_PSKEY_DFU_TRANSFER_SIZE 0x0138 /* uint16 */
+#define CSR_PSKEY_DFU_ENABLE 0x0139 /* bool */
+#define CSR_PSKEY_DFU_LIN_REG_ENABLE 0x013a /* bool */
+#define CSR_PSKEY_DFUENC_VMAPP_PK_MODULUS_MSB 0x015e /* uint16[] */
+#define CSR_PSKEY_DFUENC_VMAPP_PK_MODULUS_LSB 0x015f /* uint16[] */
+#define CSR_PSKEY_DFUENC_VMAPP_PK_M_DASH 0x0160 /* uint16 */
+#define CSR_PSKEY_DFUENC_VMAPP_PK_R2N_MSB 0x0161 /* uint16[] */
+#define CSR_PSKEY_DFUENC_VMAPP_PK_R2N_LSB 0x0162 /* uint16[] */
+#define CSR_PSKEY_BCSP_LM_PS_BLOCK 0x0192 /* BCSP_LM_PS_BLOCK */
+#define CSR_PSKEY_HOSTIO_FC_PS_BLOCK 0x0193 /* HOSTIO_FC_PS_BLOCK */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO0 0x0194 /* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO1 0x0195 /* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO2 0x0196 /* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO3 0x0197 /* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO4 0x0198 /* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO5 0x0199 /* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO6 0x019a /* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO7 0x019b /* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO8 0x019c /* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO9 0x019d /* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO10 0x019e /* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO11 0x019f /* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO12 0x01a0 /* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO13 0x01a1 /* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO14 0x01a2 /* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO15 0x01a3 /* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_UART_RESET_TIMEOUT 0x01a4 /* TIME */
+#define CSR_PSKEY_HOSTIO_USE_HCI_EXTN 0x01a5 /* bool */
+#define CSR_PSKEY_HOSTIO_USE_HCI_EXTN_CCFC 0x01a6 /* bool */
+#define CSR_PSKEY_HOSTIO_HCI_EXTN_PAYLOAD_SIZE 0x01a7 /* uint16 */
+#define CSR_PSKEY_BCSP_LM_CNF_CNT_LIMIT 0x01aa /* uint16 */
+#define CSR_PSKEY_HOSTIO_MAP_SCO_PCM 0x01ab /* bool */
+#define CSR_PSKEY_HOSTIO_AWKWARD_PCM_SYNC 0x01ac /* bool */
+#define CSR_PSKEY_HOSTIO_BREAK_POLL_PERIOD 0x01ad /* TIME */
+#define CSR_PSKEY_HOSTIO_MIN_UART_HCI_SCO_SIZE 0x01ae /* uint16 */
+#define CSR_PSKEY_HOSTIO_MAP_SCO_CODEC 0x01b0 /* bool */
+#define CSR_PSKEY_PCM_CVSD_TX_HI_FREQ_BOOST 0x01b1 /* uint16 */
+#define CSR_PSKEY_PCM_CVSD_RX_HI_FREQ_BOOST 0x01b2 /* uint16 */
+#define CSR_PSKEY_PCM_CONFIG32 0x01b3 /* uint32 */
+#define CSR_PSKEY_USE_OLD_BCSP_LE 0x01b4 /* uint16 */
+#define CSR_PSKEY_PCM_CVSD_USE_NEW_FILTER 0x01b5 /* bool */
+#define CSR_PSKEY_PCM_FORMAT 0x01b6 /* uint16 */
+#define CSR_PSKEY_CODEC_OUT_GAIN 0x01b7 /* uint16 */
+#define CSR_PSKEY_CODEC_IN_GAIN 0x01b8 /* uint16 */
+#define CSR_PSKEY_CODEC_PIO 0x01b9 /* uint16 */
+#define CSR_PSKEY_PCM_LOW_JITTER_CONFIG 0x01ba /* uint32 */
+#define CSR_PSKEY_HOSTIO_SCO_PCM_THRESHOLDS 0x01bb /* uint16[] */
+#define CSR_PSKEY_HOSTIO_SCO_HCI_THRESHOLDS 0x01bc /* uint16[] */
+#define CSR_PSKEY_HOSTIO_MAP_SCO_PCM_SLOT 0x01bd /* uint16 */
+#define CSR_PSKEY_UART_BAUDRATE 0x01be /* uint16 */
+#define CSR_PSKEY_UART_CONFIG_BCSP 0x01bf /* uint16 */
+#define CSR_PSKEY_UART_CONFIG_H4 0x01c0 /* uint16 */
+#define CSR_PSKEY_UART_CONFIG_H5 0x01c1 /* uint16 */
+#define CSR_PSKEY_UART_CONFIG_USR 0x01c2 /* uint16 */
+#define CSR_PSKEY_UART_TX_CRCS 0x01c3 /* bool */
+#define CSR_PSKEY_UART_ACK_TIMEOUT 0x01c4 /* uint16 */
+#define CSR_PSKEY_UART_TX_MAX_ATTEMPTS 0x01c5 /* uint16 */
+#define CSR_PSKEY_UART_TX_WINDOW_SIZE 0x01c6 /* uint16 */
+#define CSR_PSKEY_UART_HOST_WAKE 0x01c7 /* uint16[] */
+#define CSR_PSKEY_HOSTIO_THROTTLE_TIMEOUT 0x01c8 /* TIME */
+#define CSR_PSKEY_PCM_ALWAYS_ENABLE 0x01c9 /* bool */
+#define CSR_PSKEY_UART_HOST_WAKE_SIGNAL 0x01ca /* uint16 */
+#define CSR_PSKEY_UART_CONFIG_H4DS 0x01cb /* uint16 */
+#define CSR_PSKEY_H4DS_WAKE_DURATION 0x01cc /* uint16 */
+#define CSR_PSKEY_H4DS_MAXWU 0x01cd /* uint16 */
+#define CSR_PSKEY_H4DS_LE_TIMER_PERIOD 0x01cf /* uint16 */
+#define CSR_PSKEY_H4DS_TWU_TIMER_PERIOD 0x01d0 /* uint16 */
+#define CSR_PSKEY_H4DS_UART_IDLE_TIMER_PERIOD 0x01d1 /* uint16 */
+#define CSR_PSKEY_ANA_FTRIM 0x01f6 /* uint16 */
+#define CSR_PSKEY_WD_TIMEOUT 0x01f7 /* TIME */
+#define CSR_PSKEY_WD_PERIOD 0x01f8 /* TIME */
+#define CSR_PSKEY_HOST_INTERFACE 0x01f9 /* phys_bus */
+#define CSR_PSKEY_HQ_HOST_TIMEOUT 0x01fb /* TIME */
+#define CSR_PSKEY_HQ_ACTIVE 0x01fc /* bool */
+#define CSR_PSKEY_BCCMD_SECURITY_ACTIVE 0x01fd /* bool */
+#define CSR_PSKEY_ANA_FREQ 0x01fe /* uint16 */
+#define CSR_PSKEY_PIO_PROTECT_MASK 0x0202 /* uint16 */
+#define CSR_PSKEY_PMALLOC_SIZES 0x0203 /* uint16[] */
+#define CSR_PSKEY_UART_BAUD_RATE 0x0204 /* uint16 */
+#define CSR_PSKEY_UART_CONFIG 0x0205 /* uint16 */
+#define CSR_PSKEY_STUB 0x0207 /* uint16 */
+#define CSR_PSKEY_TXRX_PIO_CONTROL 0x0209 /* uint16 */
+#define CSR_PSKEY_ANA_RX_LEVEL 0x020b /* uint16 */
+#define CSR_PSKEY_ANA_RX_FTRIM 0x020c /* uint16 */
+#define CSR_PSKEY_PSBC_DATA_VERSION 0x020d /* uint16 */
+#define CSR_PSKEY_PCM0_ATTENUATION 0x020f /* uint16 */
+#define CSR_PSKEY_LO_LVL_MAX 0x0211 /* uint16 */
+#define CSR_PSKEY_LO_ADC_AMPL_MIN 0x0212 /* uint16 */
+#define CSR_PSKEY_LO_ADC_AMPL_MAX 0x0213 /* uint16 */
+#define CSR_PSKEY_IQ_TRIM_CHANNEL 0x0214 /* uint16 */
+#define CSR_PSKEY_IQ_TRIM_GAIN 0x0215 /* uint16 */
+#define CSR_PSKEY_IQ_TRIM_ENABLE 0x0216 /* iq_trim_enable_flag */
+#define CSR_PSKEY_TX_OFFSET_HALF_MHZ 0x0217 /* int16 */
+#define CSR_PSKEY_GBL_MISC_ENABLES 0x0221 /* uint16 */
+#define CSR_PSKEY_UART_SLEEP_TIMEOUT 0x0222 /* uint16 */
+#define CSR_PSKEY_DEEP_SLEEP_STATE 0x0229 /* deep_sleep_state */
+#define CSR_PSKEY_IQ_ENABLE_PHASE_TRIM 0x022d /* bool */
+#define CSR_PSKEY_HCI_HANDLE_FREEZE_PERIOD 0x0237 /* TIME */
+#define CSR_PSKEY_MAX_FROZEN_HCI_HANDLES 0x0238 /* uint16 */
+#define CSR_PSKEY_PAGETABLE_DESTRUCTION_DELAY 0x0239 /* TIME */
+#define CSR_PSKEY_IQ_TRIM_PIO_SETTINGS 0x023a /* uint8 */
+#define CSR_PSKEY_USE_EXTERNAL_CLOCK 0x023b /* bool */
+#define CSR_PSKEY_DEEP_SLEEP_WAKE_CTS 0x023c /* uint16 */
+#define CSR_PSKEY_FC_HC2H_FLUSH_DELAY 0x023d /* TIME */
+#define CSR_PSKEY_RX_HIGHSIDE 0x023e /* bool */
+#define CSR_PSKEY_TX_PRE_LVL 0x0240 /* uint8 */
+#define CSR_PSKEY_RX_SINGLE_ENDED 0x0242 /* bool */
+#define CSR_PSKEY_TX_FILTER_CONFIG 0x0243 /* uint32 */
+#define CSR_PSKEY_CLOCK_REQUEST_ENABLE 0x0246 /* uint16 */
+#define CSR_PSKEY_RX_MIN_ATTEN 0x0249 /* uint16 */
+#define CSR_PSKEY_XTAL_TARGET_AMPLITUDE 0x024b /* uint8 */
+#define CSR_PSKEY_PCM_MIN_CPU_CLOCK 0x024d /* uint16 */
+#define CSR_PSKEY_HOST_INTERFACE_PIO_USB 0x0250 /* uint16 */
+#define CSR_PSKEY_CPU_IDLE_MODE 0x0251 /* cpu_idle_mode */
+#define CSR_PSKEY_DEEP_SLEEP_CLEAR_RTS 0x0252 /* bool */
+#define CSR_PSKEY_RF_RESONANCE_TRIM 0x0254 /* uint16 */
+#define CSR_PSKEY_DEEP_SLEEP_PIO_WAKE 0x0255 /* uint16 */
+#define CSR_PSKEY_DRAIN_BORE_TIMERS 0x0256 /* uint32[] */
+#define CSR_PSKEY_DRAIN_TX_POWER_BASE 0x0257 /* uint16 */
+#define CSR_PSKEY_MODULE_ID 0x0259 /* uint32 */
+#define CSR_PSKEY_MODULE_DESIGN 0x025a /* uint16 */
+#define CSR_PSKEY_MODULE_SECURITY_CODE 0x025c /* uint16[] */
+#define CSR_PSKEY_VM_DISABLE 0x025d /* bool */
+#define CSR_PSKEY_MOD_MANUF0 0x025e /* uint16[] */
+#define CSR_PSKEY_MOD_MANUF1 0x025f /* uint16[] */
+#define CSR_PSKEY_MOD_MANUF2 0x0260 /* uint16[] */
+#define CSR_PSKEY_MOD_MANUF3 0x0261 /* uint16[] */
+#define CSR_PSKEY_MOD_MANUF4 0x0262 /* uint16[] */
+#define CSR_PSKEY_MOD_MANUF5 0x0263 /* uint16[] */
+#define CSR_PSKEY_MOD_MANUF6 0x0264 /* uint16[] */
+#define CSR_PSKEY_MOD_MANUF7 0x0265 /* uint16[] */
+#define CSR_PSKEY_MOD_MANUF8 0x0266 /* uint16[] */
+#define CSR_PSKEY_MOD_MANUF9 0x0267 /* uint16[] */
+#define CSR_PSKEY_DUT_VM_DISABLE 0x0268 /* bool */
+#define CSR_PSKEY_USR0 0x028a /* uint16[] */
+#define CSR_PSKEY_USR1 0x028b /* uint16[] */
+#define CSR_PSKEY_USR2 0x028c /* uint16[] */
+#define CSR_PSKEY_USR3 0x028d /* uint16[] */
+#define CSR_PSKEY_USR4 0x028e /* uint16[] */
+#define CSR_PSKEY_USR5 0x028f /* uint16[] */
+#define CSR_PSKEY_USR6 0x0290 /* uint16[] */
+#define CSR_PSKEY_USR7 0x0291 /* uint16[] */
+#define CSR_PSKEY_USR8 0x0292 /* uint16[] */
+#define CSR_PSKEY_USR9 0x0293 /* uint16[] */
+#define CSR_PSKEY_USR10 0x0294 /* uint16[] */
+#define CSR_PSKEY_USR11 0x0295 /* uint16[] */
+#define CSR_PSKEY_USR12 0x0296 /* uint16[] */
+#define CSR_PSKEY_USR13 0x0297 /* uint16[] */
+#define CSR_PSKEY_USR14 0x0298 /* uint16[] */
+#define CSR_PSKEY_USR15 0x0299 /* uint16[] */
+#define CSR_PSKEY_USR16 0x029a /* uint16[] */
+#define CSR_PSKEY_USR17 0x029b /* uint16[] */
+#define CSR_PSKEY_USR18 0x029c /* uint16[] */
+#define CSR_PSKEY_USR19 0x029d /* uint16[] */
+#define CSR_PSKEY_USR20 0x029e /* uint16[] */
+#define CSR_PSKEY_USR21 0x029f /* uint16[] */
+#define CSR_PSKEY_USR22 0x02a0 /* uint16[] */
+#define CSR_PSKEY_USR23 0x02a1 /* uint16[] */
+#define CSR_PSKEY_USR24 0x02a2 /* uint16[] */
+#define CSR_PSKEY_USR25 0x02a3 /* uint16[] */
+#define CSR_PSKEY_USR26 0x02a4 /* uint16[] */
+#define CSR_PSKEY_USR27 0x02a5 /* uint16[] */
+#define CSR_PSKEY_USR28 0x02a6 /* uint16[] */
+#define CSR_PSKEY_USR29 0x02a7 /* uint16[] */
+#define CSR_PSKEY_USR30 0x02a8 /* uint16[] */
+#define CSR_PSKEY_USR31 0x02a9 /* uint16[] */
+#define CSR_PSKEY_USR32 0x02aa /* uint16[] */
+#define CSR_PSKEY_USR33 0x02ab /* uint16[] */
+#define CSR_PSKEY_USR34 0x02ac /* uint16[] */
+#define CSR_PSKEY_USR35 0x02ad /* uint16[] */
+#define CSR_PSKEY_USR36 0x02ae /* uint16[] */
+#define CSR_PSKEY_USR37 0x02af /* uint16[] */
+#define CSR_PSKEY_USR38 0x02b0 /* uint16[] */
+#define CSR_PSKEY_USR39 0x02b1 /* uint16[] */
+#define CSR_PSKEY_USR40 0x02b2 /* uint16[] */
+#define CSR_PSKEY_USR41 0x02b3 /* uint16[] */
+#define CSR_PSKEY_USR42 0x02b4 /* uint16[] */
+#define CSR_PSKEY_USR43 0x02b5 /* uint16[] */
+#define CSR_PSKEY_USR44 0x02b6 /* uint16[] */
+#define CSR_PSKEY_USR45 0x02b7 /* uint16[] */
+#define CSR_PSKEY_USR46 0x02b8 /* uint16[] */
+#define CSR_PSKEY_USR47 0x02b9 /* uint16[] */
+#define CSR_PSKEY_USR48 0x02ba /* uint16[] */
+#define CSR_PSKEY_USR49 0x02bb /* uint16[] */
+#define CSR_PSKEY_USB_VERSION 0x02bc /* uint16 */
+#define CSR_PSKEY_USB_DEVICE_CLASS_CODES 0x02bd /* usbclass */
+#define CSR_PSKEY_USB_VENDOR_ID 0x02be /* uint16 */
+#define CSR_PSKEY_USB_PRODUCT_ID 0x02bf /* uint16 */
+#define CSR_PSKEY_USB_MANUF_STRING 0x02c1 /* unicodestring */
+#define CSR_PSKEY_USB_PRODUCT_STRING 0x02c2 /* unicodestring */
+#define CSR_PSKEY_USB_SERIAL_NUMBER_STRING 0x02c3 /* unicodestring */
+#define CSR_PSKEY_USB_CONFIG_STRING 0x02c4 /* unicodestring */
+#define CSR_PSKEY_USB_ATTRIBUTES 0x02c5 /* uint8 */
+#define CSR_PSKEY_USB_MAX_POWER 0x02c6 /* uint16 */
+#define CSR_PSKEY_USB_BT_IF_CLASS_CODES 0x02c7 /* usbclass */
+#define CSR_PSKEY_USB_LANGID 0x02c9 /* uint16 */
+#define CSR_PSKEY_USB_DFU_CLASS_CODES 0x02ca /* usbclass */
+#define CSR_PSKEY_USB_DFU_PRODUCT_ID 0x02cb /* uint16 */
+#define CSR_PSKEY_USB_PIO_DETACH 0x02ce /* uint16 */
+#define CSR_PSKEY_USB_PIO_WAKEUP 0x02cf /* uint16 */
+#define CSR_PSKEY_USB_PIO_PULLUP 0x02d0 /* uint16 */
+#define CSR_PSKEY_USB_PIO_VBUS 0x02d1 /* uint16 */
+#define CSR_PSKEY_USB_PIO_WAKE_TIMEOUT 0x02d2 /* uint16 */
+#define CSR_PSKEY_USB_PIO_RESUME 0x02d3 /* uint16 */
+#define CSR_PSKEY_USB_BT_SCO_IF_CLASS_CODES 0x02d4 /* usbclass */
+#define CSR_PSKEY_USB_SUSPEND_PIO_LEVEL 0x02d5 /* uint16 */
+#define CSR_PSKEY_USB_SUSPEND_PIO_DIR 0x02d6 /* uint16 */
+#define CSR_PSKEY_USB_SUSPEND_PIO_MASK 0x02d7 /* uint16 */
+#define CSR_PSKEY_USB_ENDPOINT_0_MAX_PACKET_SIZE 0x02d8 /* uint8 */
+#define CSR_PSKEY_USB_CONFIG 0x02d9 /* uint16 */
+#define CSR_PSKEY_RADIOTEST_ATTEN_INIT 0x0320 /* uint16 */
+#define CSR_PSKEY_RADIOTEST_FIRST_TRIM_TIME 0x0326 /* TIME */
+#define CSR_PSKEY_RADIOTEST_SUBSEQUENT_TRIM_TIME 0x0327 /* TIME */
+#define CSR_PSKEY_RADIOTEST_LO_LVL_TRIM_ENABLE 0x0328 /* bool */
+#define CSR_PSKEY_RADIOTEST_DISABLE_MODULATION 0x032c /* bool */
+#define CSR_PSKEY_RFCOMM_FCON_THRESHOLD 0x0352 /* uint16 */
+#define CSR_PSKEY_RFCOMM_FCOFF_THRESHOLD 0x0353 /* uint16 */
+#define CSR_PSKEY_IPV6_STATIC_ADDR 0x0354 /* uint16[] */
+#define CSR_PSKEY_IPV4_STATIC_ADDR 0x0355 /* uint32 */
+#define CSR_PSKEY_IPV6_STATIC_PREFIX_LEN 0x0356 /* uint8 */
+#define CSR_PSKEY_IPV6_STATIC_ROUTER_ADDR 0x0357 /* uint16[] */
+#define CSR_PSKEY_IPV4_STATIC_SUBNET_MASK 0x0358 /* uint32 */
+#define CSR_PSKEY_IPV4_STATIC_ROUTER_ADDR 0x0359 /* uint32 */
+#define CSR_PSKEY_MDNS_NAME 0x035a /* char[] */
+#define CSR_PSKEY_FIXED_PIN 0x035b /* uint8[] */
+#define CSR_PSKEY_MDNS_PORT 0x035c /* uint16 */
+#define CSR_PSKEY_MDNS_TTL 0x035d /* uint8 */
+#define CSR_PSKEY_MDNS_IPV4_ADDR 0x035e /* uint32 */
+#define CSR_PSKEY_ARP_CACHE_TIMEOUT 0x035f /* uint16 */
+#define CSR_PSKEY_HFP_POWER_TABLE 0x0360 /* uint16[] */
+#define CSR_PSKEY_DRAIN_BORE_TIMER_COUNTERS 0x03e7 /* uint32[] */
+#define CSR_PSKEY_DRAIN_BORE_COUNTERS 0x03e6 /* uint32[] */
+#define CSR_PSKEY_LOOP_FILTER_TRIM 0x03e4 /* uint16 */
+#define CSR_PSKEY_DRAIN_BORE_CURRENT_PEAK 0x03e3 /* uint32[] */
+#define CSR_PSKEY_VM_E2_CACHE_LIMIT 0x03e2 /* uint16 */
+#define CSR_PSKEY_FORCE_16MHZ_REF_PIO 0x03e1 /* uint16 */
+#define CSR_PSKEY_CDMA_LO_REF_LIMITS 0x03df /* uint16 */
+#define CSR_PSKEY_CDMA_LO_ERROR_LIMITS 0x03de /* uint16 */
+#define CSR_PSKEY_CLOCK_STARTUP_DELAY 0x03dd /* uint16 */
+#define CSR_PSKEY_DEEP_SLEEP_CORRECTION_FACTOR 0x03dc /* int16 */
+#define CSR_PSKEY_TEMPERATURE_CALIBRATION 0x03db /* temperature_calibration */
+#define CSR_PSKEY_TEMPERATURE_VS_DELTA_INTERNAL_PA 0x03da /* temperature_calibration[] */
+#define CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_PRE_LVL 0x03d9 /* temperature_calibration[] */
+#define CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_BB 0x03d8 /* temperature_calibration[] */
+#define CSR_PSKEY_TEMPERATURE_VS_DELTA_ANA_FTRIM 0x03d7 /* temperature_calibration[] */
+#define CSR_PSKEY_TEST_DELTA_OFFSET 0x03d6 /* uint16 */
+#define CSR_PSKEY_RX_DYNAMIC_LVL_OFFSET 0x03d4 /* uint16 */
+#define CSR_PSKEY_TEST_FORCE_OFFSET 0x03d3 /* bool */
+#define CSR_PSKEY_RF_TRAP_BAD_DIVISION_RATIOS 0x03cf /* uint16 */
+#define CSR_PSKEY_RADIOTEST_CDMA_LO_REF_LIMITS 0x03ce /* uint16 */
+#define CSR_PSKEY_INITIAL_BOOTMODE 0x03cd /* int16 */
+#define CSR_PSKEY_ONCHIP_HCI_CLIENT 0x03cc /* bool */
+#define CSR_PSKEY_RX_ATTEN_BACKOFF 0x03ca /* uint16 */
+#define CSR_PSKEY_RX_ATTEN_UPDATE_RATE 0x03c9 /* uint16 */
+#define CSR_PSKEY_SYNTH_TXRX_THRESHOLDS 0x03c7 /* uint16 */
+#define CSR_PSKEY_MIN_WAIT_STATES 0x03c6 /* uint16 */
+#define CSR_PSKEY_RSSI_CORRECTION 0x03c5 /* int8 */
+#define CSR_PSKEY_SCHED_THROTTLE_TIMEOUT 0x03c4 /* TIME */
+#define CSR_PSKEY_DEEP_SLEEP_USE_EXTERNAL_CLOCK 0x03c3 /* bool */
+#define CSR_PSKEY_TRIM_RADIO_FILTERS 0x03c2 /* uint16 */
+#define CSR_PSKEY_TRANSMIT_OFFSET 0x03c1 /* int16 */
+#define CSR_PSKEY_USB_VM_CONTROL 0x03c0 /* bool */
+#define CSR_PSKEY_MR_ANA_RX_FTRIM 0x03bf /* uint16 */
+#define CSR_PSKEY_I2C_CONFIG 0x03be /* uint16 */
+#define CSR_PSKEY_IQ_LVL_RX 0x03bd /* uint16 */
+#define CSR_PSKEY_MR_TX_FILTER_CONFIG 0x03bb /* uint32 */
+#define CSR_PSKEY_MR_TX_CONFIG2 0x03ba /* uint16 */
+#define CSR_PSKEY_USB_DONT_RESET_BOOTMODE_ON_HOST_RESET 0x03b9 /* bool */
+#define CSR_PSKEY_LC_USE_THROTTLING 0x03b8 /* bool */
+#define CSR_PSKEY_CHARGER_TRIM 0x03b7 /* uint16 */
+#define CSR_PSKEY_CLOCK_REQUEST_FEATURES 0x03b6 /* uint16 */
+#define CSR_PSKEY_TRANSMIT_OFFSET_CLASS1 0x03b4 /* int16 */
+#define CSR_PSKEY_TX_AVOID_PA_CLASS1_PIO 0x03b3 /* uint16 */
+#define CSR_PSKEY_MR_PIO_CONFIG 0x03b2 /* uint16 */
+#define CSR_PSKEY_UART_CONFIG2 0x03b1 /* uint8 */
+#define CSR_PSKEY_CLASS1_IQ_LVL 0x03b0 /* uint16 */
+#define CSR_PSKEY_CLASS1_TX_CONFIG2 0x03af /* uint16 */
+#define CSR_PSKEY_TEMPERATURE_VS_DELTA_INTERNAL_PA_CLASS1 0x03ae /* temperature_calibration[] */
+#define CSR_PSKEY_TEMPERATURE_VS_DELTA_EXTERNAL_PA_CLASS1 0x03ad /* temperature_calibration[] */
+#define CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_PRE_LVL_MR 0x03ac /* temperature_calibration[] */
+#define CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_BB_MR_HEADER 0x03ab /* temperature_calibration[] */
+#define CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_BB_MR_PAYLOAD 0x03aa /* temperature_calibration[] */
+#define CSR_PSKEY_RX_MR_EQ_TAPS 0x03a9 /* uint16[] */
+#define CSR_PSKEY_TX_PRE_LVL_CLASS1 0x03a8 /* uint8 */
+#define CSR_PSKEY_ANALOGUE_ATTENUATOR 0x03a7 /* bool */
+#define CSR_PSKEY_MR_RX_FILTER_TRIM 0x03a6 /* uint16 */
+#define CSR_PSKEY_MR_RX_FILTER_RESPONSE 0x03a5 /* int16[] */
+#define CSR_PSKEY_PIO_WAKEUP_STATE 0x039f /* uint16 */
+#define CSR_PSKEY_MR_TX_IF_ATTEN_OFF_TEMP 0x0394 /* int16 */
+#define CSR_PSKEY_LO_DIV_LATCH_BYPASS 0x0393 /* bool */
+#define CSR_PSKEY_LO_VCO_STANDBY 0x0392 /* bool */
+#define CSR_PSKEY_SLOW_CLOCK_FILTER_SHIFT 0x0391 /* uint16 */
+#define CSR_PSKEY_SLOW_CLOCK_FILTER_DIVIDER 0x0390 /* uint16 */
+#define CSR_PSKEY_USB_ATTRIBUTES_POWER 0x03f2 /* bool */
+#define CSR_PSKEY_USB_ATTRIBUTES_WAKEUP 0x03f3 /* bool */
+#define CSR_PSKEY_DFU_ATTRIBUTES_MANIFESTATION_TOLERANT 0x03f4 /* bool */
+#define CSR_PSKEY_DFU_ATTRIBUTES_CAN_UPLOAD 0x03f5 /* bool */
+#define CSR_PSKEY_DFU_ATTRIBUTES_CAN_DOWNLOAD 0x03f6 /* bool */
+#define CSR_PSKEY_UART_CONFIG_STOP_BITS 0x03fc /* bool */
+#define CSR_PSKEY_UART_CONFIG_PARITY_BIT 0x03fd /* uint16 */
+#define CSR_PSKEY_UART_CONFIG_FLOW_CTRL_EN 0x03fe /* bool */
+#define CSR_PSKEY_UART_CONFIG_RTS_AUTO_EN 0x03ff /* bool */
+#define CSR_PSKEY_UART_CONFIG_RTS 0x0400 /* bool */
+#define CSR_PSKEY_UART_CONFIG_TX_ZERO_EN 0x0401 /* bool */
+#define CSR_PSKEY_UART_CONFIG_NON_BCSP_EN 0x0402 /* bool */
+#define CSR_PSKEY_UART_CONFIG_RX_RATE_DELAY 0x0403 /* uint16 */
+#define CSR_PSKEY_UART_SEQ_TIMEOUT 0x0405 /* uint16 */
+#define CSR_PSKEY_UART_SEQ_RETRIES 0x0406 /* uint16 */
+#define CSR_PSKEY_UART_SEQ_WINSIZE 0x0407 /* uint16 */
+#define CSR_PSKEY_UART_USE_CRC_ON_TX 0x0408 /* bool */
+#define CSR_PSKEY_UART_HOST_INITIAL_STATE 0x0409 /* hwakeup_state */
+#define CSR_PSKEY_UART_HOST_ATTENTION_SPAN 0x040a /* uint16 */
+#define CSR_PSKEY_UART_HOST_WAKEUP_TIME 0x040b /* uint16 */
+#define CSR_PSKEY_UART_HOST_WAKEUP_WAIT 0x040c /* uint16 */
+#define CSR_PSKEY_BCSP_LM_MODE 0x0410 /* uint16 */
+#define CSR_PSKEY_BCSP_LM_SYNC_RETRIES 0x0411 /* uint16 */
+#define CSR_PSKEY_BCSP_LM_TSHY 0x0412 /* uint16 */
+#define CSR_PSKEY_UART_DFU_CONFIG_STOP_BITS 0x0417 /* bool */
+#define CSR_PSKEY_UART_DFU_CONFIG_PARITY_BIT 0x0418 /* uint16 */
+#define CSR_PSKEY_UART_DFU_CONFIG_FLOW_CTRL_EN 0x0419 /* bool */
+#define CSR_PSKEY_UART_DFU_CONFIG_RTS_AUTO_EN 0x041a /* bool */
+#define CSR_PSKEY_UART_DFU_CONFIG_RTS 0x041b /* bool */
+#define CSR_PSKEY_UART_DFU_CONFIG_TX_ZERO_EN 0x041c /* bool */
+#define CSR_PSKEY_UART_DFU_CONFIG_NON_BCSP_EN 0x041d /* bool */
+#define CSR_PSKEY_UART_DFU_CONFIG_RX_RATE_DELAY 0x041e /* uint16 */
+#define CSR_PSKEY_AMUX_AIO0 0x041f /* ana_amux_sel */
+#define CSR_PSKEY_AMUX_AIO1 0x0420 /* ana_amux_sel */
+#define CSR_PSKEY_AMUX_AIO2 0x0421 /* ana_amux_sel */
+#define CSR_PSKEY_AMUX_AIO3 0x0422 /* ana_amux_sel */
+#define CSR_PSKEY_LOCAL_NAME_SIMPLIFIED 0x0423 /* local_name_complete */
+#define CSR_PSKEY_EXTENDED_STUB 0x2001 /* uint16 */
+
+char *csr_builddeftostr(uint16_t def);
+char *csr_buildidtostr(uint16_t id);
+char *csr_chipvertostr(uint16_t ver, uint16_t rev);
+char *csr_pskeytostr(uint16_t pskey);
+char *csr_pskeytoval(uint16_t pskey);
+
+int csr_open_hci(char *device);
+int csr_read_hci(uint16_t varid, uint8_t *value, uint16_t length);
+int csr_write_hci(uint16_t varid, uint8_t *value, uint16_t length);
+void csr_close_hci(void);
+
+int csr_open_usb(char *device);
+int csr_read_usb(uint16_t varid, uint8_t *value, uint16_t length);
+int csr_write_usb(uint16_t varid, uint8_t *value, uint16_t length);
+void csr_close_usb(void);
+
+int csr_open_bcsp(char *device, speed_t bcsp_rate);
+int csr_read_bcsp(uint16_t varid, uint8_t *value, uint16_t length);
+int csr_write_bcsp(uint16_t varid, uint8_t *value, uint16_t length);
+void csr_close_bcsp(void);
+
+int csr_open_h4(char *device);
+int csr_read_h4(uint16_t varid, uint8_t *value, uint16_t length);
+int csr_write_h4(uint16_t varid, uint8_t *value, uint16_t length);
+void csr_close_h4(void);
+
+int csr_open_3wire(char *device);
+int csr_read_3wire(uint16_t varid, uint8_t *value, uint16_t length);
+int csr_write_3wire(uint16_t varid, uint8_t *value, uint16_t length);
+void csr_close_3wire(void);
+
+int csr_write_varid_valueless(int dd, uint16_t seqnum, uint16_t varid);
+int csr_write_varid_complex(int dd, uint16_t seqnum, uint16_t varid, uint8_t *value, uint16_t length);
+int csr_read_varid_complex(int dd, uint16_t seqnum, uint16_t varid, uint8_t *value, uint16_t length);
+int csr_read_varid_uint16(int dd, uint16_t seqnum, uint16_t varid, uint16_t *value);
+int csr_read_varid_uint32(int dd, uint16_t seqnum, uint16_t varid, uint32_t *value);
+int csr_read_pskey_complex(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint8_t *value, uint16_t length);
+int csr_write_pskey_complex(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint8_t *value, uint16_t length);
+int csr_read_pskey_uint16(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint16_t *value);
+int csr_write_pskey_uint16(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint16_t value);
+int csr_read_pskey_uint32(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint32_t *value);
+int csr_write_pskey_uint32(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint32_t value);
+
+int psr_put(uint16_t pskey, uint8_t *value, uint16_t size);
+int psr_get(uint16_t *pskey, uint8_t *value, uint16_t *size);
+int psr_read(const char *filename);
+int psr_print(void);
diff --git a/tools/csr_3wire.c b/tools/csr_3wire.c
new file mode 100644
index 0000000..33fcf38
--- /dev/null
+++ b/tools/csr_3wire.c
@@ -0,0 +1,62 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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 <stdint.h>
+
+#include "csr.h"
+
+static uint16_t seqnum = 0x0000;
+
+int csr_open_3wire(char *device)
+{
+ fprintf(stderr, "Transport not implemented\n");
+
+ return -1;
+}
+
+static int do_command(uint16_t command, uint16_t seqnum, uint16_t varid, uint8_t *value, uint16_t length)
+{
+ errno = EIO;
+
+ return -1;
+}
+
+int csr_read_3wire(uint16_t varid, uint8_t *value, uint16_t length)
+{
+ return do_command(0x0000, seqnum++, varid, value, length);
+}
+
+int csr_write_3wire(uint16_t varid, uint8_t *value, uint16_t length)
+{
+ return do_command(0x0002, seqnum++, varid, value, length);
+}
+
+void csr_close_3wire(void)
+{
+}
diff --git a/tools/csr_bcsp.c b/tools/csr_bcsp.c
new file mode 100644
index 0000000..df247a2
--- /dev/null
+++ b/tools/csr_bcsp.c
@@ -0,0 +1,255 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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 <string.h>
+#include <stdint.h>
+#include <termios.h>
+
+#include "csr.h"
+#include "ubcsp.h"
+
+static uint16_t seqnum = 0x0000;
+
+static int fd = -1;
+
+static struct ubcsp_packet send_packet;
+static uint8_t send_buffer[512];
+
+static struct ubcsp_packet receive_packet;
+static uint8_t receive_buffer[512];
+
+int csr_open_bcsp(char *device, speed_t bcsp_rate)
+{
+ struct termios ti;
+ uint8_t delay, activity = 0x00;
+ int timeout = 0;
+
+ if (!device)
+ device = "/dev/ttyS0";
+
+ fd = open(device, O_RDWR | O_NOCTTY);
+ if (fd < 0) {
+ fprintf(stderr, "Can't open serial port: %s (%d)\n",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ tcflush(fd, TCIOFLUSH);
+
+ if (tcgetattr(fd, &ti) < 0) {
+ fprintf(stderr, "Can't get port settings: %s (%d)\n",
+ strerror(errno), errno);
+ close(fd);
+ return -1;
+ }
+
+ cfmakeraw(&ti);
+
+ ti.c_cflag |= CLOCAL;
+ ti.c_cflag &= ~CRTSCTS;
+ ti.c_cflag |= PARENB;
+ ti.c_cflag &= ~PARODD;
+ ti.c_cflag &= ~CSIZE;
+ ti.c_cflag |= CS8;
+ ti.c_cflag &= ~CSTOPB;
+
+ ti.c_cc[VMIN] = 1;
+ ti.c_cc[VTIME] = 0;
+
+ cfsetospeed(&ti, bcsp_rate);
+
+ if (tcsetattr(fd, TCSANOW, &ti) < 0) {
+ fprintf(stderr, "Can't change port settings: %s (%d)\n",
+ strerror(errno), errno);
+ close(fd);
+ return -1;
+ }
+
+ tcflush(fd, TCIOFLUSH);
+
+ if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK) < 0) {
+ fprintf(stderr, "Can't set non blocking mode: %s (%d)\n",
+ strerror(errno), errno);
+ close(fd);
+ return -1;
+ }
+
+ memset(&send_packet, 0, sizeof(send_packet));
+ memset(&receive_packet, 0, sizeof(receive_packet));
+
+ ubcsp_initialize();
+
+ send_packet.length = 512;
+ send_packet.payload = send_buffer;
+
+ receive_packet.length = 512;
+ receive_packet.payload = receive_buffer;
+
+ ubcsp_receive_packet(&receive_packet);
+
+ while (1) {
+ delay = ubcsp_poll(&activity);
+
+ if (activity & UBCSP_PACKET_RECEIVED)
+ break;
+
+ if (delay) {
+ usleep(delay * 100);
+
+ if (timeout++ > 5000) {
+ fprintf(stderr, "Initialization timed out\n");
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+void put_uart(uint8_t ch)
+{
+ if (write(fd, &ch, 1) < 0)
+ fprintf(stderr, "UART write error\n");
+}
+
+uint8_t get_uart(uint8_t *ch)
+{
+ int res = read(fd, ch, 1);
+ return res > 0 ? res : 0;
+}
+
+static int do_command(uint16_t command, uint16_t seqnum, uint16_t varid, uint8_t *value, uint16_t length)
+{
+ unsigned char cp[254], rp[254];
+ uint8_t cmd[10];
+ uint16_t size;
+ uint8_t delay, activity = 0x00;
+ int timeout = 0, sent = 0;
+
+ size = (length < 8) ? 9 : ((length + 1) / 2) + 5;
+
+ cmd[0] = command & 0xff;
+ cmd[1] = command >> 8;
+ cmd[2] = size & 0xff;
+ cmd[3] = size >> 8;
+ cmd[4] = seqnum & 0xff;
+ cmd[5] = seqnum >> 8;
+ cmd[6] = varid & 0xff;
+ cmd[7] = varid >> 8;
+ cmd[8] = 0x00;
+ cmd[9] = 0x00;
+
+ memset(cp, 0, sizeof(cp));
+ cp[0] = 0x00;
+ cp[1] = 0xfc;
+ cp[2] = (size * 2) + 1;
+ cp[3] = 0xc2;
+ memcpy(cp + 4, cmd, sizeof(cmd));
+ memcpy(cp + 14, value, length);
+
+ receive_packet.length = 512;
+ ubcsp_receive_packet(&receive_packet);
+
+ send_packet.channel = 5;
+ send_packet.reliable = 1;
+ send_packet.length = (size * 2) + 4;
+ memcpy(send_packet.payload, cp, (size * 2) + 4);
+
+ ubcsp_send_packet(&send_packet);
+
+ while (1) {
+ delay = ubcsp_poll(&activity);
+
+ if (activity & UBCSP_PACKET_SENT) {
+ switch (varid) {
+ case CSR_VARID_COLD_RESET:
+ case CSR_VARID_WARM_RESET:
+ case CSR_VARID_COLD_HALT:
+ case CSR_VARID_WARM_HALT:
+ return 0;
+ }
+
+ sent = 1;
+ timeout = 0;
+ }
+
+ if (activity & UBCSP_PACKET_RECEIVED) {
+ if (sent && receive_packet.channel == 5 &&
+ receive_packet.payload[0] == 0xff) {
+ memcpy(rp, receive_packet.payload,
+ receive_packet.length);
+ break;
+ }
+
+ receive_packet.length = 512;
+ ubcsp_receive_packet(&receive_packet);
+ timeout = 0;
+ }
+
+ if (delay) {
+ usleep(delay * 100);
+
+ if (timeout++ > 5000) {
+ fprintf(stderr, "Operation timed out\n");
+ return -1;
+ }
+ }
+ }
+
+ if (rp[0] != 0xff || rp[2] != 0xc2) {
+ errno = EIO;
+ return -1;
+ }
+
+ if ((rp[11] + (rp[12] << 8)) != 0) {
+ errno = ENXIO;
+ return -1;
+ }
+
+ memcpy(value, rp + 13, length);
+
+ return 0;
+}
+
+int csr_read_bcsp(uint16_t varid, uint8_t *value, uint16_t length)
+{
+ return do_command(0x0000, seqnum++, varid, value, length);
+}
+
+int csr_write_bcsp(uint16_t varid, uint8_t *value, uint16_t length)
+{
+ return do_command(0x0002, seqnum++, varid, value, length);
+}
+
+void csr_close_bcsp(void)
+{
+ close(fd);
+}
diff --git a/tools/csr_h4.c b/tools/csr_h4.c
new file mode 100644
index 0000000..3371770
--- /dev/null
+++ b/tools/csr_h4.c
@@ -0,0 +1,165 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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 <string.h>
+#include <stdint.h>
+#include <termios.h>
+
+#include "csr.h"
+
+static uint16_t seqnum = 0x0000;
+
+static int fd = -1;
+
+int csr_open_h4(char *device)
+{
+ struct termios ti;
+
+ if (!device)
+ device = "/dev/ttyS0";
+
+ fd = open(device, O_RDWR | O_NOCTTY);
+ if (fd < 0) {
+ fprintf(stderr, "Can't open serial port: %s (%d)\n",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ tcflush(fd, TCIOFLUSH);
+
+ if (tcgetattr(fd, &ti) < 0) {
+ fprintf(stderr, "Can't get port settings: %s (%d)\n",
+ strerror(errno), errno);
+ close(fd);
+ return -1;
+ }
+
+ cfmakeraw(&ti);
+
+ ti.c_cflag |= CLOCAL;
+ ti.c_cflag |= CRTSCTS;
+
+ cfsetospeed(&ti, B38400);
+
+ if (tcsetattr(fd, TCSANOW, &ti) < 0) {
+ fprintf(stderr, "Can't change port settings: %s (%d)\n",
+ strerror(errno), errno);
+ close(fd);
+ return -1;
+ }
+
+ tcflush(fd, TCIOFLUSH);
+
+ return 0;
+}
+
+static int do_command(uint16_t command, uint16_t seqnum, uint16_t varid, uint8_t *value, uint16_t length)
+{
+ unsigned char cp[254], rp[254];
+ uint8_t cmd[10];
+ uint16_t size;
+ int len, offset = 3;
+
+ size = (length < 8) ? 9 : ((length + 1) / 2) + 5;
+
+ cmd[0] = command & 0xff;
+ cmd[1] = command >> 8;
+ cmd[2] = size & 0xff;
+ cmd[3] = size >> 8;
+ cmd[4] = seqnum & 0xff;
+ cmd[5] = seqnum >> 8;
+ cmd[6] = varid & 0xff;
+ cmd[7] = varid >> 8;
+ cmd[8] = 0x00;
+ cmd[9] = 0x00;
+
+ memset(cp, 0, sizeof(cp));
+ cp[0] = 0x01;
+ cp[1] = 0x00;
+ cp[2] = 0xfc;
+ cp[3] = (size * 2) + 1;
+ cp[4] = 0xc2;
+ memcpy(cp + 5, cmd, sizeof(cmd));
+ memcpy(cp + 15, value, length);
+
+ if (write(fd, cp, (size * 2) + 5) < 0)
+ return -1;
+
+ switch (varid) {
+ case CSR_VARID_COLD_RESET:
+ case CSR_VARID_WARM_RESET:
+ case CSR_VARID_COLD_HALT:
+ case CSR_VARID_WARM_HALT:
+ return 0;
+ }
+
+ do {
+ if (read(fd, rp, 1) < 1)
+ return -1;
+ } while (rp[0] != 0x04);
+
+ if (read(fd, rp + 1, 2) < 2)
+ return -1;
+
+ do {
+ len = read(fd, rp + offset, sizeof(rp) - offset);
+ offset += len;
+ } while (offset < rp[2] + 3);
+
+ if (rp[0] != 0x04 || rp[1] != 0xff || rp[3] != 0xc2) {
+ errno = EIO;
+ return -1;
+ }
+
+ if ((rp[12] + (rp[13] << 8)) != 0) {
+ errno = ENXIO;
+ return -1;
+ }
+
+ memcpy(value, rp + 14, length);
+
+ return 0;
+}
+
+int csr_read_h4(uint16_t varid, uint8_t *value, uint16_t length)
+{
+ return do_command(0x0000, seqnum++, varid, value, length);
+}
+
+int csr_write_h4(uint16_t varid, uint8_t *value, uint16_t length)
+{
+ return do_command(0x0002, seqnum++, varid, value, length);
+}
+
+void csr_close_h4(void)
+{
+ close(fd);
+}
diff --git a/tools/csr_hci.c b/tools/csr_hci.c
new file mode 100644
index 0000000..6bd37c3
--- /dev/null
+++ b/tools/csr_hci.c
@@ -0,0 +1,160 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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 <string.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "csr.h"
+
+static uint16_t seqnum = 0x0000;
+
+static int dd = -1;
+
+int csr_open_hci(char *device)
+{
+ struct hci_dev_info di;
+ struct hci_version ver;
+ int dev = 0;
+
+ if (device) {
+ dev = hci_devid(device);
+ if (dev < 0) {
+ fprintf(stderr, "Device not available\n");
+ return -1;
+ }
+ }
+
+ dd = hci_open_dev(dev);
+ if (dd < 0) {
+ fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+ dev, strerror(errno), errno);
+ return -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);
+ return -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);
+ return -1;
+ }
+
+ if (ver.manufacturer != 10) {
+ fprintf(stderr, "Unsupported manufacturer\n");
+ hci_close_dev(dd);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int do_command(uint16_t command, uint16_t seqnum, uint16_t varid, uint8_t *value, uint16_t length)
+{
+ unsigned char cp[254], rp[254];
+ struct hci_request rq;
+ uint8_t cmd[10];
+ uint16_t size;
+
+ size = (length < 8) ? 9 : ((length + 1) / 2) + 5;
+
+ cmd[0] = command & 0xff;
+ cmd[1] = command >> 8;
+ cmd[2] = size & 0xff;
+ cmd[3] = size >> 8;
+ cmd[4] = seqnum & 0xff;
+ cmd[5] = seqnum >> 8;
+ cmd[6] = varid & 0xff;
+ cmd[7] = varid >> 8;
+ cmd[8] = 0x00;
+ cmd[9] = 0x00;
+
+ memset(cp, 0, sizeof(cp));
+ cp[0] = 0xc2;
+ memcpy(cp + 1, cmd, sizeof(cmd));
+ memcpy(cp + 11, value, length);
+
+ switch (varid) {
+ case CSR_VARID_COLD_RESET:
+ case CSR_VARID_WARM_RESET:
+ case CSR_VARID_COLD_HALT:
+ case CSR_VARID_WARM_HALT:
+ return hci_send_cmd(dd, OGF_VENDOR_CMD, 0x00, (size * 2) + 1, cp);
+ }
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_VENDOR_CMD;
+ rq.ocf = 0x00;
+ rq.event = EVT_VENDOR;
+ rq.cparam = cp;
+ rq.clen = (size * 2) + 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;
+ }
+
+ memcpy(value, rp + 11, length);
+
+ return 0;
+}
+
+int csr_read_hci(uint16_t varid, uint8_t *value, uint16_t length)
+{
+ return do_command(0x0000, seqnum++, varid, value, length);
+}
+
+int csr_write_hci(uint16_t varid, uint8_t *value, uint16_t length)
+{
+ return do_command(0x0002, seqnum++, varid, value, length);
+}
+
+void csr_close_hci(void)
+{
+ hci_close_dev(dd);
+}
diff --git a/tools/csr_usb.c b/tools/csr_usb.c
new file mode 100644
index 0000000..19903b0
--- /dev/null
+++ b/tools/csr_usb.c
@@ -0,0 +1,180 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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 <string.h>
+
+#include <usb.h>
+
+#include "csr.h"
+
+#ifdef NEED_USB_GET_BUSSES
+static inline struct usb_bus *usb_get_busses(void)
+{
+ return usb_busses;
+}
+#endif
+
+#ifdef NEED_USB_INTERRUPT_READ
+static inline int usb_interrupt_read(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout)
+{
+ return usb_bulk_read(dev, ep, bytes, size, timeout);
+}
+#endif
+
+#ifndef USB_DIR_OUT
+#define USB_DIR_OUT 0x00
+#endif
+
+static uint16_t seqnum = 0x0000;
+
+static struct usb_dev_handle *udev = NULL;
+
+int csr_open_usb(char *device)
+{
+ struct usb_bus *bus;
+ struct usb_device *dev;
+
+ usb_init();
+
+ usb_find_busses();
+ usb_find_devices();
+
+ for (bus = usb_get_busses(); bus; bus = bus->next) {
+ for (dev = bus->devices; dev; dev = dev->next) {
+ if (dev->descriptor.bDeviceClass == USB_CLASS_HUB)
+ continue;
+
+ if (dev->descriptor.idVendor != 0x0a12 ||
+ dev->descriptor.idProduct != 0x0001)
+ continue;
+
+ goto found;
+ }
+ }
+
+ fprintf(stderr, "Device not available\n");
+
+ return -1;
+
+found:
+ udev = usb_open(dev);
+ if (!udev) {
+ fprintf(stderr, "Can't open device: %s (%d)\n",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ if (usb_claim_interface(udev, 0) < 0) {
+ fprintf(stderr, "Can't claim interface: %s (%d)\n",
+ strerror(errno), errno);
+ usb_close(udev);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int do_command(uint16_t command, uint16_t seqnum, uint16_t varid, uint8_t *value, uint16_t length)
+{
+ unsigned char cp[254], rp[254];
+ uint8_t cmd[10];
+ uint16_t size;
+ int len, offset = 0;
+
+ size = (length < 8) ? 9 : ((length + 1) / 2) + 5;
+
+ cmd[0] = command & 0xff;
+ cmd[1] = command >> 8;
+ cmd[2] = size & 0xff;
+ cmd[3] = size >> 8;
+ cmd[4] = seqnum & 0xff;
+ cmd[5] = seqnum >> 8;
+ cmd[6] = varid & 0xff;
+ cmd[7] = varid >> 8;
+ cmd[8] = 0x00;
+ cmd[9] = 0x00;
+
+ memset(cp, 0, sizeof(cp));
+ cp[0] = 0x00;
+ cp[1] = 0xfc;
+ cp[2] = (size * 2) + 1;
+ cp[3] = 0xc2;
+ memcpy(cp + 4, cmd, sizeof(cmd));
+ memcpy(cp + 14, value, length);
+
+ usb_interrupt_read(udev, 0x81, (void *) rp, sizeof(rp), 2);
+
+ if (usb_control_msg(udev, USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_DEVICE,
+ 0, 0, 0, (void *) cp, (size * 2) + 4, 1000) < 0)
+ return -1;
+
+ switch (varid) {
+ case CSR_VARID_COLD_RESET:
+ case CSR_VARID_WARM_RESET:
+ case CSR_VARID_COLD_HALT:
+ case CSR_VARID_WARM_HALT:
+ return 0;
+ }
+
+ do {
+ len = usb_interrupt_read(udev, 0x81,
+ (void *) (rp + offset), sizeof(rp) - offset, 10);
+ offset += len;
+ } while (len > 0);
+
+ if (rp[0] != 0xff || rp[2] != 0xc2) {
+ errno = EIO;
+ return -1;
+ }
+
+ if ((rp[11] + (rp[12] << 8)) != 0) {
+ errno = ENXIO;
+ return -1;
+ }
+
+ memcpy(value, rp + 13, length);
+
+ return 0;
+}
+
+int csr_read_usb(uint16_t varid, uint8_t *value, uint16_t length)
+{
+ return do_command(0x0000, seqnum++, varid, value, length);
+}
+
+int csr_write_usb(uint16_t varid, uint8_t *value, uint16_t length)
+{
+ return do_command(0x0002, seqnum++, varid, value, length);
+}
+
+void csr_close_usb(void)
+{
+ usb_release_interface(udev, 0);
+ usb_close(udev);
+}
diff --git a/tools/dfu.c b/tools/dfu.c
new file mode 100644
index 0000000..39ec088
--- /dev/null
+++ b/tools/dfu.c
@@ -0,0 +1,168 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2003-2010 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 <string.h>
+
+#include <usb.h>
+
+#include "dfu.h"
+
+#ifndef USB_DIR_OUT
+#define USB_DIR_OUT 0x00
+#endif
+
+#ifndef USB_DIR_IN
+#define USB_DIR_IN 0x80
+#endif
+
+#ifndef USB_DT_DFU
+#define USB_DT_DFU 0x21
+#endif
+
+#define DFU_PACKETSIZE 0x03ff /* CSR default value: 1023 */
+#define DFU_TIMEOUT 10000
+
+static uint32_t dfu_crc32_table[] = {
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+ 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+ 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+ 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+ 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+ 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+ 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+ 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+ 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+ 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+ 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+ 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+ 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+ 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+ 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+ 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+ 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+ 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+ 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+ 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+ 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+ 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+};
+
+uint32_t crc32_init(void)
+{
+ return 0xffffffff;
+}
+
+uint32_t crc32_byte(uint32_t accum, uint8_t delta)
+{
+ return dfu_crc32_table[(accum ^ delta) & 0xff] ^ (accum >> 8);
+}
+
+int dfu_detach(struct usb_dev_handle *udev, int intf)
+{
+ if (!udev)
+ return -EIO;
+
+ return usb_control_msg(udev, USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ DFU_DETACH, 0x1388, intf, NULL, 0, DFU_TIMEOUT);
+}
+
+int dfu_upload(struct usb_dev_handle *udev, int intf, int block, char *buffer, int size)
+{
+ if (!udev)
+ return -EIO;
+
+ return usb_control_msg(udev, USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE,
+ DFU_UPLOAD, block, intf, buffer, size, DFU_TIMEOUT);
+}
+
+int dfu_download(struct usb_dev_handle *udev, int intf, int block, char *buffer, int size)
+{
+ if (!udev)
+ return -EIO;
+
+ return usb_control_msg(udev, USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_INTERFACE,
+ DFU_DNLOAD, block, intf, buffer, size, DFU_TIMEOUT);
+}
+
+int dfu_get_status(struct usb_dev_handle *udev, int intf, struct dfu_status *status)
+{
+ if (!udev || !status)
+ return -EIO;
+
+ return usb_control_msg(udev, USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE,
+ DFU_GETSTATUS, 0, intf, (char *) status, DFU_STATUS_SIZE, DFU_TIMEOUT);
+}
+
+int dfu_clear_status(struct usb_dev_handle *udev, int intf)
+{
+ if (!udev)
+ return -EIO;
+
+ return usb_control_msg(udev, USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_INTERFACE,
+ DFU_CLRSTATUS, 0, intf, NULL, 0, DFU_TIMEOUT);
+}
+
+int dfu_get_state(struct usb_dev_handle *udev, int intf, uint8_t *state)
+{
+ if (!udev || !state)
+ return -EIO;
+
+ return usb_control_msg(udev, USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE,
+ DFU_GETSTATE, 0, intf, (char *) state, 1, DFU_TIMEOUT);
+}
+
+int dfu_abort(struct usb_dev_handle *udev, int intf)
+{
+ if (!udev)
+ return -EIO;
+
+ return usb_control_msg(udev, USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_INTERFACE,
+ DFU_ABORT, 0, intf, NULL, 0, DFU_TIMEOUT);
+}
diff --git a/tools/dfu.h b/tools/dfu.h
new file mode 100644
index 0000000..7f999f4
--- /dev/null
+++ b/tools/dfu.h
@@ -0,0 +1,107 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2003-2010 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
+ *
+ */
+
+#include <stdint.h>
+
+/* CRC interface */
+uint32_t crc32_init(void);
+uint32_t crc32_byte(uint32_t accum, uint8_t delta);
+
+/* DFU descriptor */
+struct usb_dfu_descriptor {
+ u_int8_t bLength;
+ u_int8_t bDescriptorType;
+ u_int8_t bmAttributes;
+ u_int16_t wDetachTimeout;
+ u_int16_t wTransferSize;
+};
+
+/* DFU commands */
+#define DFU_DETACH 0
+#define DFU_DNLOAD 1
+#define DFU_UPLOAD 2
+#define DFU_GETSTATUS 3
+#define DFU_CLRSTATUS 4
+#define DFU_GETSTATE 5
+#define DFU_ABORT 6
+
+/* DFU status */
+struct dfu_status {
+ uint8_t bStatus;
+ uint8_t bwPollTimeout[3];
+ uint8_t bState;
+ uint8_t iString;
+} __attribute__ ((packed));
+#define DFU_STATUS_SIZE 6
+
+/* DFU status */
+#define DFU_OK 0x00
+#define DFU_ERR_TARGET 0x01
+#define DFU_ERR_FILE 0x02
+#define DFU_ERR_WRITE 0x03
+#define DFU_ERR_ERASE 0x04
+#define DFU_ERR_CHECK_ERASED 0x05
+#define DFU_ERR_PROG 0x06
+#define DFU_ERR_VERIFY 0x07
+#define DFU_ERR_ADDRESS 0x08
+#define DFU_ERR_NOTDONE 0x09
+#define DFU_ERR_FIRMWARE 0x0a
+#define DFU_ERR_VENDOR 0x0b
+#define DFU_ERR_USBR 0x0c
+#define DFU_ERR_POR 0x0d
+#define DFU_ERR_UNKNOWN 0x0e
+#define DFU_ERR_STALLEDPKT 0x0f
+
+/* DFU state */
+#define DFU_STATE_APP_IDLE 0
+#define DFU_STATE_APP_DETACH 1
+#define DFU_STATE_DFU_IDLE 2
+#define DFU_STATE_DFU_DNLOAD_SYNC 3
+#define DFU_STATE_DFU_DNLOAD_BUSY 4
+#define DFU_STATE_DFU_DNLOAD_IDLE 5
+#define DFU_STATE_DFU_MANIFEST_SYNC 6
+#define DFU_STATE_DFU_MANIFEST 7
+#define DFU_STATE_MANIFEST_WAIT_RESET 8
+#define DFU_STATE_UPLOAD_IDLE 9
+#define DFU_STATE_ERROR 10
+
+/* DFU suffix */
+struct dfu_suffix {
+ uint16_t bcdDevice;
+ uint16_t idProduct;
+ uint16_t idVendor;
+ uint16_t bcdDFU;
+ uint8_t ucDfuSignature[3];
+ uint8_t bLength;
+ uint32_t dwCRC;
+} __attribute__ ((packed));
+#define DFU_SUFFIX_SIZE 16
+
+/* DFU interface */
+int dfu_detach(struct usb_dev_handle *udev, int intf);
+int dfu_upload(struct usb_dev_handle *udev, int intf, int block, char *buffer, int size);
+int dfu_download(struct usb_dev_handle *udev, int intf, int block, char *buffer, int size);
+int dfu_get_status(struct usb_dev_handle *udev, int intf, struct dfu_status *status);
+int dfu_clear_status(struct usb_dev_handle *udev, int intf);
+int dfu_get_state(struct usb_dev_handle *udev, int intf, uint8_t *state);
+int dfu_abort(struct usb_dev_handle *udev, int intf);
diff --git a/tools/dfubabel.1 b/tools/dfubabel.1
new file mode 100644
index 0000000..5e0f805
--- /dev/null
+++ b/tools/dfubabel.1
@@ -0,0 +1,38 @@
+.\"
+.\" 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.TH DFUBABEL 8 "JUNE 6, 2005" "" ""
+
+.SH NAME
+dfubabel \- Babel DFU mode switching utility
+.SH SYNOPSIS
+.BR "dfubabel
+[
+.I options
+]
+.SH DESCRIPTION
+.B dfubabel
+is used to switch Babel devices into DFU mode.
+.SH OPTIONS
+.TP
+.BI -h
+Gives a list of possible options.
+.TP
+.BI -q
+Don't display any messages.
+.SH AUTHOR
+Written by Marcel Holtmann <marcel@holtmann.org>.
+.br
diff --git a/tools/dfubabel.c b/tools/dfubabel.c
new file mode 100644
index 0000000..612accc
--- /dev/null
+++ b/tools/dfubabel.c
@@ -0,0 +1,211 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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 <stdint.h>
+#include <string.h>
+#include <getopt.h>
+
+#include <usb.h>
+
+#ifdef NEED_USB_GET_BUSSES
+static inline struct usb_bus *usb_get_busses(void)
+{
+ return usb_busses;
+}
+#endif
+
+struct device_info;
+
+struct device_id {
+ uint16_t vendor;
+ uint16_t product;
+ int (*func)(struct device_info *dev, int argc, char *argv[]);
+};
+
+struct device_info {
+ struct usb_device *dev;
+ struct device_id *id;
+};
+
+static int switch_babel(struct device_info *devinfo, int argc, char *argv[])
+{
+ char buf[3];
+ struct usb_dev_handle *udev;
+ int err;
+
+ memset(buf, 0, sizeof(buf));
+
+ buf[0] = 0x00;
+ buf[1] = 0x06;
+ buf[2] = 0x00;
+
+ udev = usb_open(devinfo->dev);
+ if (!udev)
+ return -errno;
+
+ if (usb_claim_interface(udev, 0) < 0) {
+ err = -errno;
+ usb_close(udev);
+ return err;
+ }
+
+ err = usb_bulk_write(udev, 0x02, buf, sizeof(buf), 10000);
+
+ if (err == 0) {
+ err = -1;
+ errno = EALREADY;
+ } else {
+ if (errno == ETIMEDOUT)
+ err = 0;
+ }
+
+ usb_release_interface(udev, 0);
+ usb_close(udev);
+
+ return err;
+}
+
+static struct device_id device_list[] = {
+ { 0x0a12, 0x0042, switch_babel },
+ { -1 }
+};
+
+static struct device_id *match_device(uint16_t vendor, uint16_t product)
+{
+ int i;
+
+ for (i = 0; device_list[i].func; i++) {
+ if (vendor == device_list[i].vendor &&
+ product == device_list[i].product)
+ return &device_list[i];
+ }
+
+ return NULL;
+}
+
+static int find_devices(struct device_info *devinfo, size_t size)
+{
+ struct usb_bus *bus;
+ struct usb_device *dev;
+ struct device_id *id;
+ unsigned int count = 0;
+
+ usb_find_busses();
+ usb_find_devices();
+
+ for (bus = usb_get_busses(); bus; bus = bus->next)
+ for (dev = bus->devices; dev; dev = dev->next) {
+ id = match_device(dev->descriptor.idVendor,
+ dev->descriptor.idProduct);
+ if (!id)
+ continue;
+
+ if (count < size) {
+ devinfo[count].dev = dev;
+ devinfo[count].id = id;
+ count++;
+ }
+ }
+
+ return count;
+}
+
+static void usage(void)
+{
+ printf("dfubabel - Babel DFU mode switching utility\n\n");
+
+ printf("Usage:\n"
+ "\tdfubabel [options]\n"
+ "\n");
+
+ printf("Options:\n"
+ "\t-h, --help Display help\n"
+ "\t-q, --quiet Don't display any messages\n"
+ "\n");
+}
+
+static struct option main_options[] = {
+ { "help", 0, 0, 'h' },
+ { "quiet", 0, 0, 'q' },
+ { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ struct device_info dev[16];
+ int i, opt, num, quiet = 0;
+
+ while ((opt = getopt_long(argc, argv, "+qh", main_options, NULL)) != -1) {
+ switch (opt) {
+ case 'q':
+ quiet = 1;
+ break;
+ case 'h':
+ usage();
+ exit(0);
+ default:
+ exit(0);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ usb_init();
+
+ num = find_devices(dev, sizeof(dev) / sizeof(dev[0]));
+ if (num <= 0) {
+ if (!quiet)
+ fprintf(stderr, "No Babel devices found\n");
+ exit(1);
+ }
+
+ for (i = 0; i < num; i++) {
+ struct device_id *id = dev[i].id;
+ int err;
+
+ if (!quiet)
+ printf("Switching device %04x:%04x ",
+ id->vendor, id->product);
+ fflush(stdout);
+
+ err = id->func(&dev[i], argc, argv);
+ if (err < 0) {
+ if (!quiet)
+ printf("failed (%s)\n", strerror(-err));
+ } else {
+ if (!quiet)
+ printf("was successful\n");
+ }
+ }
+
+ return 0;
+}
diff --git a/tools/dfutool.1 b/tools/dfutool.1
new file mode 100644
index 0000000..115114b
--- /dev/null
+++ b/tools/dfutool.1
@@ -0,0 +1,53 @@
+.\"
+.\" 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.TH DFUTOOL 1 "APRIL 21, 2005" "" ""
+
+.SH NAME
+dfutool \- Device Firmware Upgrade utility
+.SH SYNOPSIS
+.BR "dfutool
+[
+.I options
+] <
+.I command
+>
+.SH DESCRIPTION
+.B dfutool
+is used to verify, archive and upgrade firmware files.
+.SH OPTIONS
+.TP
+.BI -h
+Gives a list of possible commands.
+.TP
+.BI -d " <device>"
+The command specifies the device to use.
+.SH COMMANDS
+.TP
+.BI verify " <dfu-file>"
+Display information about the firmware file.
+.TP
+.BI modify " <dfu-file>"
+Change DFU specific values in the firmware file.
+.TP
+.BI upgrade " <dfu-file>"
+Upgrade the device with a new firmware.
+.TP
+.BI archive " <dfu-file>"
+Archive the current firmware of the device.
+.SH AUTHOR
+Written by Marcel Holtmann <marcel@holtmann.org>.
+.br
diff --git a/tools/dfutool.c b/tools/dfutool.c
new file mode 100644
index 0000000..16dd62e
--- /dev/null
+++ b/tools/dfutool.c
@@ -0,0 +1,791 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2003-2010 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 <stdint.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <string.h>
+#include <libgen.h>
+#include <endian.h>
+#include <byteswap.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <usb.h>
+
+#include "dfu.h"
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define cpu_to_le16(d) (d)
+#define cpu_to_le32(d) (d)
+#define le16_to_cpu(d) (d)
+#define le32_to_cpu(d) (d)
+#elif __BYTE_ORDER == __BIG_ENDIAN
+#define cpu_to_le16(d) bswap_16(d)
+#define cpu_to_le32(d) bswap_32(d)
+#define le16_to_cpu(d) bswap_16(d)
+#define le32_to_cpu(d) bswap_32(d)
+#else
+#error "Unknown byte order"
+#endif
+
+#ifdef NEED_USB_GET_BUSSES
+static inline struct usb_bus *usb_get_busses(void)
+{
+ return usb_busses;
+}
+#endif
+
+#ifndef USB_CLASS_WIRELESS
+#define USB_CLASS_WIRELESS 0xe0
+#endif
+
+#ifndef USB_CLASS_APPLICATION
+#define USB_CLASS_APPLICATION 0xfe
+#endif
+
+static int get_interface_number(struct usb_device *dev)
+{
+ int c, i, a;
+
+ for (c = 0; c < dev->descriptor.bNumConfigurations; c++) {
+ struct usb_config_descriptor *config = &dev->config[c];
+
+ for (i = 0; i < config->bNumInterfaces; i++) {
+ struct usb_interface *interface = &config->interface[i];
+
+ for (a = 0; a < interface->num_altsetting; a++) {
+ struct usb_interface_descriptor *desc = &interface->altsetting[a];
+
+ if (desc->bInterfaceClass != USB_CLASS_APPLICATION)
+ continue;
+ if (desc->bInterfaceSubClass != 0x01)
+ continue;
+ if (desc->bInterfaceProtocol != 0x00)
+ continue;
+
+ return desc->bInterfaceNumber;
+ }
+ }
+ }
+
+ return -1;
+}
+
+static void print_device(struct usb_device *dev)
+{
+ printf("Bus %s Device %s: ID %04x:%04x Interface %d%s\n",
+ dev->bus->dirname, dev->filename,
+ dev->descriptor.idVendor, dev->descriptor.idProduct,
+ get_interface_number(dev),
+ dev->descriptor.bDeviceClass == USB_CLASS_APPLICATION ? " (DFU mode)" : "");
+}
+
+static struct usb_dev_handle *open_device(char *device, struct dfu_suffix *suffix)
+{
+ struct usb_bus *bus;
+ struct usb_device *dev, *dfu_dev[10];
+ struct usb_dev_handle *udev;
+ struct dfu_status status;
+ char str[8];
+ int i, intf, sel = 0, num = 0, try = 5, bus_id = -1, dev_id = -1;
+
+ printf("Scanning USB busses ... ");
+ fflush(stdout);
+
+ usb_find_busses();
+ usb_find_devices();
+
+ for (bus = usb_get_busses(); bus; bus = bus->next) {
+ if (bus_id > 0) {
+ snprintf(str, sizeof(str) - 1, "%03i", bus_id);
+ if (strcmp(str, bus->dirname))
+ continue;
+ }
+
+ for (dev = bus->devices; dev; dev = dev->next) {
+ if (bus_id > 0 && dev_id > 0) {
+ snprintf(str, sizeof(str) - 1, "%03i", dev_id);
+ if (strcmp(str, dev->filename))
+ continue;
+ }
+
+ if (dev->descriptor.bDeviceClass == USB_CLASS_HUB)
+ continue;
+
+ if (num > 9 || get_interface_number(dev) < 0)
+ continue;
+
+ dfu_dev[num++] = dev;
+ }
+ }
+
+ if (num < 1) {
+ printf("\rCan't find any DFU devices\n");
+ return NULL;
+ }
+
+ printf("\rAvailable devices with DFU support:\n\n");
+ for (i = 0; i < num; i++) {
+ printf("\t%2d) ", i + 1);
+ print_device(dfu_dev[i]);
+ }
+ printf("\n");
+
+ do {
+ printf("\rSelect device (abort with 0): ");
+ fflush(stdout);
+ memset(str, 0, sizeof(str));
+ if (!fgets(str, sizeof(str) - 1, stdin))
+ continue;
+ sel = atoi(str);
+ } while (!isdigit(str[0]) || sel < 0 || sel > num );
+
+ if (sel < 1)
+ return NULL;
+
+ sel--;
+ intf = get_interface_number(dfu_dev[sel]);
+ printf("\n");
+
+ udev = usb_open(dfu_dev[sel]);
+ if (!udev) {
+ printf("Can't open device: %s (%d)\n", strerror(errno), errno);
+ return NULL;
+ }
+
+ if (usb_claim_interface(udev, intf) < 0) {
+ printf("Can't claim interface: %s (%d)\n", strerror(errno), errno);
+ usb_close(udev);
+ return NULL;
+ }
+
+ if (dfu_get_status(udev, intf, &status) < 0) {
+ printf("Can't get status: %s (%d)\n", strerror(errno), errno);
+ goto error;
+ }
+
+ if (status.bState == DFU_STATE_ERROR) {
+ if (dfu_clear_status(udev, intf) < 0) {
+ printf("Can't clear status: %s (%d)\n", strerror(errno), errno);
+ goto error;
+ }
+ if (dfu_abort(udev, intf) < 0) {
+ printf("Can't abort previous action: %s (%d)\n", strerror(errno), errno);
+ goto error;
+ }
+ if (dfu_get_status(udev, intf, &status) < 0) {
+ printf("Can't get status: %s (%d)\n", strerror(errno), errno);
+ goto error;
+ }
+ }
+
+ if (status.bState == DFU_STATE_DFU_IDLE) {
+ if (suffix) {
+ suffix->idVendor = cpu_to_le16(0x0000);
+ suffix->idProduct = cpu_to_le16(0x0000);
+ suffix->bcdDevice = cpu_to_le16(0x0000);
+ }
+ return udev;
+ }
+
+ if (status.bState != DFU_STATE_APP_IDLE) {
+ printf("Device is not idle, can't detach it (state %d)\n", status.bState);
+ goto error;
+ }
+
+ printf("Switching device into DFU mode ... ");
+ fflush(stdout);
+
+ if (suffix) {
+ suffix->idVendor = cpu_to_le16(dfu_dev[sel]->descriptor.idVendor);
+ suffix->idProduct = cpu_to_le16(dfu_dev[sel]->descriptor.idProduct);
+ suffix->bcdDevice = cpu_to_le16(dfu_dev[sel]->descriptor.bcdDevice);
+ }
+
+ if (dfu_detach(udev, intf) < 0) {
+ printf("\rCan't detach device: %s (%d)\n", strerror(errno), errno);
+ goto error;
+ }
+
+ if (dfu_get_status(udev, intf, &status) < 0) {
+ printf("\rCan't get status: %s (%d)\n", strerror(errno), errno);
+ goto error;
+ }
+
+ if (status.bState != DFU_STATE_APP_DETACH) {
+ printf("\rDevice is not in detach mode, try again\n");
+ goto error;
+ }
+
+ usb_release_interface(udev, intf);
+ usb_reset(udev);
+ usb_close(udev);
+
+ bus = dfu_dev[sel]->bus;
+ num = 0;
+
+ while (num != 1 && try-- > 0) {
+ sleep(1);
+ usb_find_devices();
+
+ for (dev = bus->devices; dev; dev = dev->next) {
+ if (dev->descriptor.bDeviceClass != USB_CLASS_APPLICATION)
+ continue;
+
+ if (suffix && dev->descriptor.idVendor != le16_to_cpu(suffix->idVendor))
+ continue;
+
+ if (num > 9 || get_interface_number(dev) != 0)
+ continue;
+
+ dfu_dev[num++] = dev;
+ }
+ }
+
+ if (num != 1) {
+ printf("\rCan't identify device with DFU mode\n");
+ goto error;
+ }
+
+ printf("\r");
+
+ intf = 0;
+
+ udev = usb_open(dfu_dev[0]);
+ if (!udev) {
+ printf("Can't open device: %s (%d)\n", strerror(errno), errno);
+ return NULL;
+ }
+
+ if (usb_claim_interface(udev, intf) < 0) {
+ printf("Can't claim interface: %s (%d)\n", strerror(errno), errno);
+ usb_close(udev);
+ return NULL;
+ }
+
+ if (dfu_get_status(udev, intf, &status) < 0) {
+ printf("Can't get status: %s (%d)\n", strerror(errno), errno);
+ goto error;
+ }
+
+ if (status.bState != DFU_STATE_DFU_IDLE) {
+ printf("Device is not in DFU mode, can't use it\n");
+ goto error;
+ }
+
+ return udev;
+
+error:
+ usb_release_interface(udev, intf);
+ usb_close(udev);
+ return NULL;
+}
+
+static void usage(void);
+
+static void cmd_verify(char *device, int argc, char **argv)
+{
+ struct stat st;
+ struct dfu_suffix *suffix;
+ uint32_t crc;
+ uint16_t bcd;
+ char str[16];
+ unsigned char *buf;
+ size_t size;
+ char *filename;
+ unsigned int i, len;
+ int fd;
+
+ if (argc < 2) {
+ usage();
+ exit(1);
+ }
+
+ filename = argv[1];
+
+ if (stat(filename, &st) < 0) {
+ perror("Can't access firmware");
+ exit(1);
+ }
+
+ size = st.st_size;
+
+ if (!(buf = malloc(size))) {
+ perror("Unable to allocate file buffer");
+ exit(1);
+ }
+
+ if ((fd = open(filename, O_RDONLY)) < 0) {
+ perror("Can't open firmware");
+ free(buf);
+ exit(1);
+ }
+
+ if (read(fd, buf, size) < (ssize_t) size) {
+ perror("Can't load firmware");
+ free(buf);
+ close(fd);
+ exit(1);
+ }
+
+ printf("Filename\t%s\n", basename(filename));
+ printf("Filesize\t%zd\n", size);
+
+ crc = crc32_init();
+ for (i = 0; i < size - 4; i++)
+ crc = crc32_byte(crc, buf[i]);
+ printf("Checksum\t%08x\n", crc);
+
+ printf("\n");
+ len = buf[size - 5];
+ printf("DFU suffix\t");
+ for (i = 0; i < len; i++) {
+ printf("%02x ", buf[size - len + i]);
+ }
+ printf("\n\n");
+
+ suffix = (struct dfu_suffix *) (buf + size - DFU_SUFFIX_SIZE);
+
+ printf("idVendor\t%04x\n", le16_to_cpu(suffix->idVendor));
+ printf("idProduct\t%04x\n", le16_to_cpu(suffix->idProduct));
+ printf("bcdDevice\t%x\n", le16_to_cpu(suffix->bcdDevice));
+
+ printf("\n");
+
+ bcd = le16_to_cpu(suffix->bcdDFU);
+
+ printf("bcdDFU\t\t%x.%x\n", bcd >> 8, bcd & 0xff);
+ printf("ucDfuSignature\t%c%c%c\n", suffix->ucDfuSignature[2],
+ suffix->ucDfuSignature[1], suffix->ucDfuSignature[0]);
+ printf("bLength\t\t%d\n", suffix->bLength);
+ printf("dwCRC\t\t%08x\n", le32_to_cpu(suffix->dwCRC));
+ printf("\n");
+
+ memset(str, 0, sizeof(str));
+ memcpy(str, buf, 8);
+
+ if (!strcmp(str, "CSR-dfu1") || !strcmp(str, "CSR-dfu2")) {
+ crc = crc32_init();
+ for (i = 0; i < size - DFU_SUFFIX_SIZE; i++)
+ crc = crc32_byte(crc, buf[i]);
+
+ printf("Firmware type\t%s\n", str);
+ printf("Firmware check\t%s checksum\n", crc == 0 ? "valid" : "corrupt");
+ printf("\n");
+ }
+
+ free(buf);
+
+ close(fd);
+}
+
+static void cmd_modify(char *device, int argc, char **argv)
+{
+}
+
+static void cmd_upgrade(char *device, int argc, char **argv)
+{
+ struct usb_dev_handle *udev;
+ struct dfu_status status;
+ struct dfu_suffix suffix;
+ struct stat st;
+ char *buf;
+ size_t filesize;
+ unsigned long count, timeout = 0;
+ char *filename;
+ uint32_t crc, dwCRC;
+ unsigned int i;
+ int fd, block, len, size, sent = 0, try = 10;
+
+ if (argc < 2) {
+ usage();
+ exit(1);
+ }
+
+ filename = argv[1];
+
+ if (stat(filename, &st) < 0) {
+ perror("Can't access firmware");
+ exit(1);
+ }
+
+ filesize = st.st_size;
+
+ if (!(buf = malloc(filesize))) {
+ perror("Unable to allocate file buffer");
+ exit(1);
+ }
+
+ if ((fd = open(filename, O_RDONLY)) < 0) {
+ perror("Can't open firmware");
+ free(buf);
+ exit(1);
+ }
+
+ if (read(fd, buf, filesize) < (ssize_t) filesize) {
+ perror("Can't load firmware");
+ free(buf);
+ close(fd);
+ exit(1);
+ }
+
+ memcpy(&suffix, buf + filesize - DFU_SUFFIX_SIZE, sizeof(suffix));
+ dwCRC = le32_to_cpu(suffix.dwCRC);
+
+ printf("Filename\t%s\n", basename(filename));
+ printf("Filesize\t%zd\n", filesize);
+
+ crc = crc32_init();
+ for (i = 0; i < filesize - 4; i++)
+ crc = crc32_byte(crc, buf[i]);
+
+ printf("Checksum\t%08x (%s)\n", crc,
+ crc == dwCRC ? "valid" : "corrupt");
+
+ if (crc != dwCRC) {
+ free(buf);
+ close(fd);
+ exit(1);
+ }
+
+ printf("\n");
+
+ udev = open_device(device, &suffix);
+ if (!udev)
+ exit(1);
+
+ printf("\r" " " " " " " " " " ");
+ printf("\rFirmware download ... ");
+ fflush(stdout);
+
+ count = filesize - DFU_SUFFIX_SIZE;
+ block = 0;
+
+ while (count) {
+ size = (count > 1023) ? 1023 : count;
+
+ if (dfu_get_status(udev, 0, &status) < 0) {
+ if (try-- > 0) {
+ sleep(1);
+ continue;
+ }
+ printf("\rCan't get status: %s (%d)\n", strerror(errno), errno);
+ goto done;
+ }
+
+ if (status.bStatus != DFU_OK) {
+ if (try-- > 0) {
+ dfu_clear_status(udev, 0);
+ sleep(1);
+ continue;
+ }
+ printf("\rFirmware download ... aborting (status %d state %d)\n",
+ status.bStatus, status.bState);
+ goto done;
+ }
+
+ if (status.bState != DFU_STATE_DFU_IDLE &&
+ status.bState != DFU_STATE_DFU_DNLOAD_IDLE) {
+ sleep(1);
+ continue;
+ }
+
+ timeout = (status.bwPollTimeout[2] << 16) |
+ (status.bwPollTimeout[1] << 8) |
+ status.bwPollTimeout[0];
+
+ usleep(timeout * 1000);
+
+ len = dfu_download(udev, 0, block, buf + sent, size);
+ if (len < 0) {
+ if (try-- > 0) {
+ sleep(1);
+ continue;
+ }
+ printf("\rCan't upload next block: %s (%d)\n", strerror(errno), errno);
+ goto done;
+ }
+
+ printf("\rFirmware download ... %d bytes ", block * 1023 + len);
+ fflush(stdout);
+
+ sent += len;
+ count -= len;
+ block++;
+ }
+
+ printf("\r" " " " " " " " " " ");
+ printf("\rFinishing firmware download ... ");
+ fflush(stdout);
+
+ sleep(1);
+
+ if (dfu_get_status(udev, 0, &status) < 0) {
+ printf("\rCan't get status: %s (%d)\n", strerror(errno), errno);
+ goto done;
+ }
+
+ timeout = (status.bwPollTimeout[2] << 16) |
+ (status.bwPollTimeout[1] << 8) |
+ status.bwPollTimeout[0];
+
+ usleep(timeout * 1000);
+
+ if (count == 0) {
+ len = dfu_download(udev, 0, block, NULL, 0);
+ if (len < 0) {
+ printf("\rCan't send final block: %s (%d)\n", strerror(errno), errno);
+ goto done;
+ }
+ }
+
+ printf("\r" " " " " " " " " " ");
+ printf("\rWaiting for device ... ");
+ fflush(stdout);
+
+ sleep(10);
+
+ printf("\n");
+
+done:
+ free(buf);
+ close(fd);
+
+ usb_release_interface(udev, 0);
+ usb_reset(udev);
+ usb_close(udev);
+}
+
+static void cmd_archive(char *device, int argc, char **argv)
+{
+ struct usb_dev_handle *udev;
+ struct dfu_status status;
+ struct dfu_suffix suffix;
+ char buf[2048];
+ unsigned long timeout = 0;
+ char *filename;
+ uint32_t crc;
+ int fd, i, n, len, try = 8;
+
+ if (argc < 2) {
+ usage();
+ exit(1);
+ }
+
+ filename = argv[1];
+
+ udev = open_device(device, &suffix);
+ if (!udev)
+ exit(1);
+
+ fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (fd < 0) {
+ printf("Can't open firmware file: %s (%d)\n", strerror(errno), errno);
+ goto done;
+ }
+
+ printf("\r" " " " " " " " " " ");
+ printf("\rFirmware upload ... ");
+ fflush(stdout);
+
+ crc = crc32_init();
+ n = 0;
+ while (1) {
+ if (dfu_get_status(udev, 0, &status) < 0) {
+ if (try-- > 0) {
+ sleep(1);
+ continue;
+ }
+ printf("\rCan't get status: %s (%d)\n", strerror(errno), errno);
+ goto done;
+ }
+
+ if (status.bStatus != DFU_OK) {
+ if (try-- > 0) {
+ dfu_clear_status(udev, 0);
+ sleep(1);
+ continue;
+ }
+ printf("\rFirmware upload ... aborting (status %d state %d)\n",
+ status.bStatus, status.bState);
+ goto done;
+ }
+
+ if (status.bState != DFU_STATE_DFU_IDLE &&
+ status.bState != DFU_STATE_UPLOAD_IDLE) {
+ sleep(1);
+ continue;
+ }
+
+ timeout = (status.bwPollTimeout[2] << 16) |
+ (status.bwPollTimeout[1] << 8) |
+ status.bwPollTimeout[0];
+
+ usleep(timeout * 1000);
+
+ len = dfu_upload(udev, 0, n, buf, 1023);
+ if (len < 0) {
+ if (try-- > 0) {
+ sleep(1);
+ continue;
+ }
+ printf("\rCan't upload next block: %s (%d)\n", strerror(errno), errno);
+ goto done;
+ }
+
+ printf("\rFirmware upload ... %d bytes ", n * 1023 + len);
+ fflush(stdout);
+
+ for (i = 0; i < len; i++)
+ crc = crc32_byte(crc, buf[i]);
+
+ if (len > 0) {
+ if (write(fd, buf, len) < 0) {
+ printf("\rCan't write next block: %s (%d)\n", strerror(errno), errno);
+ goto done;
+ }
+ }
+
+ n++;
+ if (len != 1023)
+ break;
+ }
+ printf("\n");
+
+ suffix.bcdDFU = cpu_to_le16(0x0100);
+ suffix.ucDfuSignature[0] = 'U';
+ suffix.ucDfuSignature[1] = 'F';
+ suffix.ucDfuSignature[2] = 'D';
+ suffix.bLength = DFU_SUFFIX_SIZE;
+
+ memcpy(buf, &suffix, DFU_SUFFIX_SIZE);
+ for (i = 0; i < DFU_SUFFIX_SIZE - 4; i++)
+ crc = crc32_byte(crc, buf[i]);
+
+ suffix.dwCRC = cpu_to_le32(crc);
+
+ if (write(fd, &suffix, DFU_SUFFIX_SIZE) < 0)
+ printf("Can't write suffix block: %s (%d)\n", strerror(errno), errno);
+
+done:
+ close(fd);
+
+ usb_release_interface(udev, 0);
+ usb_reset(udev);
+ usb_close(udev);
+}
+
+struct {
+ char *cmd;
+ char *alt;
+ void (*func)(char *device, int argc, char **argv);
+ char *opt;
+ char *doc;
+} command[] = {
+ { "verify", "check", cmd_verify, "<dfu-file>", "Check firmware file" },
+ { "modify", "change", cmd_modify, "<dfu-file>", "Change firmware attributes" },
+ { "upgrade", "download", cmd_upgrade, "<dfu-file>", "Download a new firmware" },
+ { "archive", "upload", cmd_archive, "<dfu-file>", "Upload the current firmware" },
+ { NULL, NULL, NULL, 0, 0 }
+};
+
+static void usage(void)
+{
+ int i;
+
+ printf("dfutool - Device Firmware Upgrade utility ver %s\n\n", VERSION);
+
+ printf("Usage:\n"
+ "\tdfutool [options] <command>\n"
+ "\n");
+
+ printf("Options:\n"
+ "\t-d, --device <device> USB device\n"
+ "\t-h, --help Display help\n"
+ "\n");
+
+ printf("Commands:\n");
+ for (i = 0; command[i].cmd; i++)
+ printf("\t%-8s %-10s\t%s\n", command[i].cmd,
+ command[i].opt ? command[i].opt : " ",
+ command[i].doc);
+ printf("\n");
+}
+
+static struct option main_options[] = {
+ { "help", 0, 0, 'h' },
+ { "device", 1, 0, 'd' },
+ { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ char *device = NULL;
+ int i, opt;
+
+ while ((opt = getopt_long(argc, argv, "+d:h", main_options, NULL)) != -1) {
+ switch(opt) {
+ case 'd':
+ device = strdup(optarg);
+ break;
+
+ case 'h':
+ usage();
+ exit(0);
+
+ default:
+ exit(0);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ if (argc < 1) {
+ usage();
+ exit(1);
+ }
+
+ usb_init();
+
+ for (i = 0; command[i].cmd; i++) {
+ if (strcmp(command[i].cmd, argv[0]) && strcmp(command[i].alt, argv[0]))
+ continue;
+ command[i].func(device, argc, argv);
+ exit(0);
+ }
+
+ usage();
+ exit(1);
+}
diff --git a/tools/hciattach.8 b/tools/hciattach.8
new file mode 100644
index 0000000..e0e2730
--- /dev/null
+++ b/tools/hciattach.8
@@ -0,0 +1,155 @@
+.TH HCIATTACH 8 "Jan 22 2002" BlueZ "Linux System Administration"
+.SH NAME
+hciattach \- attach serial devices via UART HCI to BlueZ stack
+.SH SYNOPSIS
+.B hciattach
+.RB [\| \-b \|]
+.RB [\| \-n \|]
+.RB [\| \-p \|]
+.RB [\| \-t
+.IR timeout \|]
+.RB [\| \-s
+.IR speed \|]
+.RB [\| \-l \|]
+.RB [\| \-r \|]
+.I tty
+.IR type \||\| id
+.I speed
+.I flow
+.I bdaddr
+.SH DESCRIPTION
+.LP
+Hciattach is used to attach a serial UART to the Bluetooth stack as HCI
+transport interface.
+.SH OPTIONS
+.TP
+.B \-b
+Send break.
+.TP
+.B \-n
+Don't detach from controlling terminal.
+.TP
+.B \-p
+Print the PID when detaching.
+.TP
+.BI \-t " timeout"
+Specify an initialization timeout. (Default is 5 seconds.)
+.TP
+.BI \-s " speed"
+Specify an initial speed instead of the hardware default.
+.TP
+.B \-l
+List all available configurations.
+.TP
+.B \-r
+Set the HCI device into raw mode (the kernel and bluetoothd will ignore it).
+.TP
+.I tty
+This specifies the serial device to attach. A leading
+.B /dev
+can be omitted. Examples:
+.B /dev/ttyS1
+.B ttyS2
+.TP
+.IR type \||\| id
+The
+.I type
+or
+.I id
+of the Bluetooth device that is to be attached, i.e. vendor or other device
+specific identifier. Currently supported types are
+.RS
+.TP
+.B type
+.B description
+.TP
+.B any
+Unspecified HCI_UART interface, no vendor specific options
+.TP
+.B ericsson
+Ericsson based modules
+.TP
+.B digi
+Digianswer based cards
+.TP
+.B xircom
+Xircom PCMCIA cards: Credit Card Adapter and Real Port Adapter
+.TP
+.B csr
+CSR Casira serial adapter or BrainBoxes serial dongle (BL642)
+.TP
+.B bboxes
+BrainBoxes PCMCIA card (BL620)
+.TP
+.B swave
+Silicon Wave kits
+.TP
+.B bcsp
+Serial adapters using CSR chips with BCSP serial protocol
+.TP
+.B ath3k
+Atheros AR300x based serial Bluetooth device
+.RE
+
+Supported IDs are (manufacturer id, product id)
+.RS
+.TP
+.B 0x0105, 0x080a
+Xircom PCMCIA cards: Credit Card Adapter and Real Port Adapter
+.TP
+.B 0x0160, 0x0002
+BrainBoxes PCMCIA card (BL620)
+.RE
+
+.TP
+.I speed
+The
+.I speed
+specifies the UART speed to use. Baudrates higher than 115.200bps require
+vendor specific initializations that are not implemented for all types of
+devices. In general the following speeds are supported:
+
+.B 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600
+
+Supported vendor devices are automatically initialised to their respective
+best settings.
+.TP
+.I flow
+If the keyword
+.I flow
+is appended to the list of options then hardware flow control is forced on
+the serial link (
+.B CRTSCTS
+). All above mentioned device types have
+.B flow
+set by default. To force no flow control use
+.B noflow
+instead.
+.TP
+.I sleep
+Enables hardware specific power management feature. If
+.I sleep
+is appended to the list of options then this feature is enabled. To disable
+this feature use
+.B nosleep
+instead.
+All above mentioned device types have
+.B nosleep
+set by default.
+
+Note: This option will only be valid for hardware which support
+hardware specific power management enable option from host.
+.TP
+.I bdaddr
+The
+.I bdaddr
+specifies the Bluetooth Address to use. Some devices (like the STLC2500)
+do not store the Bluetooth address in hardware memory. Instead it must
+be uploaded during the initialization process. If this argument
+is specified, then the address will be used to initialize the device.
+Otherwise, a default address will be used.
+
+.SH AUTHORS
+Written by Maxim Krasnyansky <maxk@qualcomm.com>
+.PP
+Manual page by Nils Faerber <nils@kernelconcepts.de>
diff --git a/tools/hciattach.c b/tools/hciattach.c
new file mode 100644
index 0000000..e4d5aa1
--- /dev/null
+++ b/tools/hciattach.c
@@ -0,0 +1,1446 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2000-2001 Qualcomm Incorporated
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 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
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <syslog.h>
+#include <termios.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "hciattach.h"
+
+#ifdef NEED_PPOLL
+#include "ppoll.h"
+#endif
+
+struct uart_t {
+ char *type;
+ int m_id;
+ int p_id;
+ int proto;
+ int init_speed;
+ int speed;
+ int flags;
+ int pm;
+ char *bdaddr;
+ int (*init) (int fd, struct uart_t *u, struct termios *ti);
+ int (*post) (int fd, struct uart_t *u, struct termios *ti);
+};
+
+#define FLOW_CTL 0x0001
+#define ENABLE_PM 1
+#define DISABLE_PM 0
+
+static volatile sig_atomic_t __io_canceled = 0;
+
+static void sig_hup(int sig)
+{
+}
+
+static void sig_term(int sig)
+{
+ __io_canceled = 1;
+}
+
+static void sig_alarm(int sig)
+{
+ fprintf(stderr, "Initialization timed out.\n");
+ exit(1);
+}
+
+static int uart_speed(int s)
+{
+ switch (s) {
+ case 9600:
+ return B9600;
+ case 19200:
+ return B19200;
+ case 38400:
+ return B38400;
+ case 57600:
+ return B57600;
+ case 115200:
+ return B115200;
+ case 230400:
+ return B230400;
+ case 460800:
+ return B460800;
+ case 500000:
+ return B500000;
+ case 576000:
+ return B576000;
+ case 921600:
+ return B921600;
+ case 1000000:
+ return B1000000;
+ case 1152000:
+ return B1152000;
+ case 1500000:
+ return B1500000;
+ case 2000000:
+ return B2000000;
+#ifdef B2500000
+ case 2500000:
+ return B2500000;
+#endif
+#ifdef B3000000
+ case 3000000:
+ return B3000000;
+#endif
+#ifdef B3500000
+ case 3500000:
+ return B3500000;
+#endif
+#ifdef B4000000
+ case 4000000:
+ return B4000000;
+#endif
+ default:
+ return B57600;
+ }
+}
+
+int set_speed(int fd, struct termios *ti, int speed)
+{
+ if (cfsetospeed(ti, uart_speed(speed)) < 0)
+ return -errno;
+
+ if (cfsetispeed(ti, uart_speed(speed)) < 0)
+ return -errno;
+
+ if (tcsetattr(fd, TCSANOW, ti) < 0)
+ return -errno;
+
+ return 0;
+}
+
+/*
+ * Read an HCI event from the given file descriptor.
+ */
+int read_hci_event(int fd, unsigned char* buf, int size)
+{
+ int remain, r;
+ int count = 0;
+
+ if (size <= 0)
+ return -1;
+
+ /* The first byte identifies the packet type. For HCI event packets, it
+ * should be 0x04, so we read until we get to the 0x04. */
+ while (1) {
+ r = read(fd, buf, 1);
+ if (r <= 0)
+ return -1;
+ if (buf[0] == 0x04)
+ break;
+ }
+ count++;
+
+ /* The next two bytes are the event code and parameter total length. */
+ while (count < 3) {
+ r = read(fd, buf + count, 3 - count);
+ if (r <= 0)
+ return -1;
+ count += r;
+ }
+
+ /* Now we read the parameters. */
+ if (buf[2] < (size - 3))
+ remain = buf[2];
+ else
+ remain = size - 3;
+
+ while ((count - 3) < remain) {
+ r = read(fd, buf + count, remain - (count - 3));
+ if (r <= 0)
+ return -1;
+ count += r;
+ }
+
+ return count;
+}
+
+/*
+ * Ericsson specific initialization
+ */
+static int ericsson(int fd, struct uart_t *u, struct termios *ti)
+{
+ struct timespec tm = {0, 50000};
+ char cmd[5];
+
+ cmd[0] = HCI_COMMAND_PKT;
+ cmd[1] = 0x09;
+ cmd[2] = 0xfc;
+ cmd[3] = 0x01;
+
+ switch (u->speed) {
+ case 57600:
+ cmd[4] = 0x03;
+ break;
+ case 115200:
+ cmd[4] = 0x02;
+ break;
+ case 230400:
+ cmd[4] = 0x01;
+ break;
+ case 460800:
+ cmd[4] = 0x00;
+ break;
+ case 921600:
+ cmd[4] = 0x20;
+ break;
+ case 2000000:
+ cmd[4] = 0x25;
+ break;
+ case 3000000:
+ cmd[4] = 0x27;
+ break;
+ case 4000000:
+ cmd[4] = 0x2B;
+ break;
+ default:
+ cmd[4] = 0x03;
+ u->speed = 57600;
+ fprintf(stderr, "Invalid speed requested, using %d bps instead\n", u->speed);
+ break;
+ }
+
+ /* Send initialization command */
+ if (write(fd, cmd, 5) != 5) {
+ perror("Failed to write init command");
+ return -1;
+ }
+
+ nanosleep(&tm, NULL);
+ return 0;
+}
+
+/*
+ * Digianswer specific initialization
+ */
+static int digi(int fd, struct uart_t *u, struct termios *ti)
+{
+ struct timespec tm = {0, 50000};
+ char cmd[5];
+
+ /* DigiAnswer set baud rate command */
+ cmd[0] = HCI_COMMAND_PKT;
+ cmd[1] = 0x07;
+ cmd[2] = 0xfc;
+ cmd[3] = 0x01;
+
+ switch (u->speed) {
+ case 57600:
+ cmd[4] = 0x08;
+ break;
+ case 115200:
+ cmd[4] = 0x09;
+ break;
+ default:
+ cmd[4] = 0x09;
+ u->speed = 115200;
+ break;
+ }
+
+ /* Send initialization command */
+ if (write(fd, cmd, 5) != 5) {
+ perror("Failed to write init command");
+ return -1;
+ }
+
+ nanosleep(&tm, NULL);
+ return 0;
+}
+
+static int texas(int fd, struct uart_t *u, struct termios *ti)
+{
+ return texas_init(fd, ti);
+}
+
+static int texas2(int fd, struct uart_t *u, struct termios *ti)
+{
+ return texas_post(fd, ti);
+}
+
+static int texasalt(int fd, struct uart_t *u, struct termios *ti)
+{
+ return texasalt_init(fd, u->speed, ti);
+}
+
+static int ath3k_ps(int fd, struct uart_t *u, struct termios *ti)
+{
+ return ath3k_init(fd, u->speed, u->init_speed, u->bdaddr, ti);
+}
+
+static int ath3k_pm(int fd, struct uart_t *u, struct termios *ti)
+{
+ return ath3k_post(fd, u->pm);
+}
+
+static int qualcomm(int fd, struct uart_t *u, struct termios *ti)
+{
+ return qualcomm_init(fd, u->speed, ti, u->bdaddr);
+}
+
+static int read_check(int fd, void *buf, int count)
+{
+ int res;
+
+ do {
+ res = read(fd, buf, count);
+ if (res != -1) {
+ buf += res;
+ count -= res;
+ }
+ } while (count && (errno == 0 || errno == EINTR));
+
+ if (count)
+ return -1;
+
+ return 0;
+}
+
+/*
+ * BCSP specific initialization
+ */
+static int serial_fd;
+static int bcsp_max_retries = 10;
+
+static void bcsp_tshy_sig_alarm(int sig)
+{
+ unsigned char bcsp_sync_pkt[10] = {0xc0,0x00,0x41,0x00,0xbe,0xda,0xdc,0xed,0xed,0xc0};
+ int len;
+ static int retries = 0;
+
+ if (retries < bcsp_max_retries) {
+ retries++;
+ len = write(serial_fd, &bcsp_sync_pkt, 10);
+ alarm(1);
+ return;
+ }
+
+ tcflush(serial_fd, TCIOFLUSH);
+ fprintf(stderr, "BCSP initialization timed out\n");
+ exit(1);
+}
+
+static void bcsp_tconf_sig_alarm(int sig)
+{
+ unsigned char bcsp_conf_pkt[10] = {0xc0,0x00,0x41,0x00,0xbe,0xad,0xef,0xac,0xed,0xc0};
+ int len;
+ static int retries = 0;
+
+ if (retries < bcsp_max_retries){
+ retries++;
+ len = write(serial_fd, &bcsp_conf_pkt, 10);
+ alarm(1);
+ return;
+ }
+
+ tcflush(serial_fd, TCIOFLUSH);
+ fprintf(stderr, "BCSP initialization timed out\n");
+ exit(1);
+}
+
+static int bcsp(int fd, struct uart_t *u, struct termios *ti)
+{
+ unsigned char byte, bcsph[4], bcspp[4],
+ bcsp_sync_resp_pkt[10] = {0xc0,0x00,0x41,0x00,0xbe,0xac,0xaf,0xef,0xee,0xc0},
+ bcsp_conf_resp_pkt[10] = {0xc0,0x00,0x41,0x00,0xbe,0xde,0xad,0xd0,0xd0,0xc0},
+ bcspsync[4] = {0xda, 0xdc, 0xed, 0xed},
+ bcspsyncresp[4] = {0xac,0xaf,0xef,0xee},
+ bcspconf[4] = {0xad,0xef,0xac,0xed},
+ bcspconfresp[4] = {0xde,0xad,0xd0,0xd0};
+ struct sigaction sa;
+ int len;
+
+ if (set_speed(fd, ti, u->speed) < 0) {
+ perror("Can't set default baud rate");
+ return -1;
+ }
+
+ ti->c_cflag |= PARENB;
+ ti->c_cflag &= ~(PARODD);
+
+ if (tcsetattr(fd, TCSANOW, ti) < 0) {
+ perror("Can't set port settings");
+ return -1;
+ }
+
+ alarm(0);
+
+ serial_fd = fd;
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_flags = SA_NOCLDSTOP;
+ sa.sa_handler = bcsp_tshy_sig_alarm;
+ sigaction(SIGALRM, &sa, NULL);
+
+ /* State = shy */
+
+ bcsp_tshy_sig_alarm(0);
+ while (1) {
+ do {
+ if (read_check(fd, &byte, 1) == -1){
+ perror("Failed to read");
+ return -1;
+ }
+ } while (byte != 0xC0);
+
+ do {
+ if ( read_check(fd, &bcsph[0], 1) == -1){
+ perror("Failed to read");
+ return -1;
+ }
+ } while (bcsph[0] == 0xC0);
+
+ if ( read_check(fd, &bcsph[1], 3) == -1){
+ perror("Failed to read");
+ return -1;
+ }
+
+ if (((bcsph[0] + bcsph[1] + bcsph[2]) & 0xFF) != (unsigned char)~bcsph[3])
+ continue;
+ if (bcsph[1] != 0x41 || bcsph[2] != 0x00)
+ continue;
+
+ if (read_check(fd, &bcspp, 4) == -1){
+ perror("Failed to read");
+ return -1;
+ }
+
+ if (!memcmp(bcspp, bcspsync, 4)) {
+ len = write(fd, &bcsp_sync_resp_pkt,10);
+ } else if (!memcmp(bcspp, bcspsyncresp, 4))
+ break;
+ }
+
+ /* State = curious */
+
+ alarm(0);
+ sa.sa_handler = bcsp_tconf_sig_alarm;
+ sigaction(SIGALRM, &sa, NULL);
+ alarm(1);
+
+ while (1) {
+ do {
+ if (read_check(fd, &byte, 1) == -1){
+ perror("Failed to read");
+ return -1;
+ }
+ } while (byte != 0xC0);
+
+ do {
+ if (read_check(fd, &bcsph[0], 1) == -1){
+ perror("Failed to read");
+ return -1;
+ }
+ } while (bcsph[0] == 0xC0);
+
+ if (read_check(fd, &bcsph[1], 3) == -1){
+ perror("Failed to read");
+ return -1;
+ }
+
+ if (((bcsph[0] + bcsph[1] + bcsph[2]) & 0xFF) != (unsigned char)~bcsph[3])
+ continue;
+
+ if (bcsph[1] != 0x41 || bcsph[2] != 0x00)
+ continue;
+
+ if (read_check(fd, &bcspp, 4) == -1){
+ perror("Failed to read");
+ return -1;
+ }
+
+ if (!memcmp(bcspp, bcspsync, 4))
+ len = write(fd, &bcsp_sync_resp_pkt, 10);
+ else if (!memcmp(bcspp, bcspconf, 4))
+ len = write(fd, &bcsp_conf_resp_pkt, 10);
+ else if (!memcmp(bcspp, bcspconfresp, 4))
+ break;
+ }
+
+ /* State = garrulous */
+
+ return 0;
+}
+
+/*
+ * CSR specific initialization
+ * Inspired strongly by code in OpenBT and experimentations with Brainboxes
+ * Pcmcia card.
+ * Jean Tourrilhes <jt@hpl.hp.com> - 14.11.01
+ */
+static int csr(int fd, struct uart_t *u, struct termios *ti)
+{
+ struct timespec tm = {0, 10000000}; /* 10ms - be generous */
+ unsigned char cmd[30]; /* Command */
+ unsigned char resp[30]; /* Response */
+ int clen = 0; /* Command len */
+ static int csr_seq = 0; /* Sequence number of command */
+ int divisor;
+
+ /* It seems that if we set the CSR UART speed straight away, it
+ * won't work, the CSR UART gets into a state where we can't talk
+ * to it anymore.
+ * On the other hand, doing a read before setting the CSR speed
+ * seems to be ok.
+ * Therefore, the strategy is to read the build ID (useful for
+ * debugging) and only then set the CSR UART speed. Doing like
+ * this is more complex but at least it works ;-)
+ * The CSR UART control may be slow to wake up or something because
+ * every time I read its speed, its bogus...
+ * Jean II */
+
+ /* Try to read the build ID of the CSR chip */
+ clen = 5 + (5 + 6) * 2;
+ /* HCI header */
+ cmd[0] = HCI_COMMAND_PKT;
+ cmd[1] = 0x00; /* CSR command */
+ cmd[2] = 0xfc; /* MANUFACTURER_SPEC */
+ cmd[3] = 1 + (5 + 6) * 2; /* len */
+ /* CSR MSG header */
+ cmd[4] = 0xC2; /* first+last+channel=BCC */
+ /* CSR BCC header */
+ cmd[5] = 0x00; /* type = GET-REQ */
+ cmd[6] = 0x00; /* - msB */
+ cmd[7] = 5 + 4; /* len */
+ cmd[8] = 0x00; /* - msB */
+ cmd[9] = csr_seq & 0xFF;/* seq num */
+ cmd[10] = (csr_seq >> 8) & 0xFF; /* - msB */
+ csr_seq++;
+ cmd[11] = 0x19; /* var_id = CSR_CMD_BUILD_ID */
+ cmd[12] = 0x28; /* - msB */
+ cmd[13] = 0x00; /* status = STATUS_OK */
+ cmd[14] = 0x00; /* - msB */
+ /* CSR BCC payload */
+ memset(cmd + 15, 0, 6 * 2);
+
+ /* Send command */
+ do {
+ if (write(fd, cmd, clen) != clen) {
+ perror("Failed to write init command (GET_BUILD_ID)");
+ return -1;
+ }
+
+ /* Read reply. */
+ if (read_hci_event(fd, resp, 100) < 0) {
+ perror("Failed to read init response (GET_BUILD_ID)");
+ return -1;
+ }
+
+ /* Event code 0xFF is for vendor-specific events, which is
+ * what we're looking for. */
+ } while (resp[1] != 0xFF);
+
+#ifdef CSR_DEBUG
+ {
+ char temp[512];
+ int i;
+ for (i=0; i < rlen; i++)
+ sprintf(temp + (i*3), "-%02X", resp[i]);
+ fprintf(stderr, "Reading CSR build ID %d [%s]\n", rlen, temp + 1);
+ // In theory, it should look like :
+ // 04-FF-13-FF-01-00-09-00-00-00-19-28-00-00-73-00-00-00-00-00-00-00
+ }
+#endif
+ /* Display that to user */
+ fprintf(stderr, "CSR build ID 0x%02X-0x%02X\n",
+ resp[15] & 0xFF, resp[14] & 0xFF);
+
+ /* Try to read the current speed of the CSR chip */
+ clen = 5 + (5 + 4)*2;
+ /* -- HCI header */
+ cmd[3] = 1 + (5 + 4)*2; /* len */
+ /* -- CSR BCC header -- */
+ cmd[9] = csr_seq & 0xFF; /* seq num */
+ cmd[10] = (csr_seq >> 8) & 0xFF; /* - msB */
+ csr_seq++;
+ cmd[11] = 0x02; /* var_id = CONFIG_UART */
+ cmd[12] = 0x68; /* - msB */
+
+#ifdef CSR_DEBUG
+ /* Send command */
+ do {
+ if (write(fd, cmd, clen) != clen) {
+ perror("Failed to write init command (GET_BUILD_ID)");
+ return -1;
+ }
+
+ /* Read reply. */
+ if (read_hci_event(fd, resp, 100) < 0) {
+ perror("Failed to read init response (GET_BUILD_ID)");
+ return -1;
+ }
+
+ /* Event code 0xFF is for vendor-specific events, which is
+ * what we're looking for. */
+ } while (resp[1] != 0xFF);
+
+ {
+ char temp[512];
+ int i;
+ for (i=0; i < rlen; i++)
+ sprintf(temp + (i*3), "-%02X", resp[i]);
+ fprintf(stderr, "Reading CSR UART speed %d [%s]\n", rlen, temp+1);
+ }
+#endif
+
+ if (u->speed > 1500000) {
+ fprintf(stderr, "Speed %d too high. Remaining at %d baud\n",
+ u->speed, u->init_speed);
+ u->speed = u->init_speed;
+ } else if (u->speed != 57600 && uart_speed(u->speed) == B57600) {
+ /* Unknown speed. Why oh why can't we just pass an int to the kernel? */
+ fprintf(stderr, "Speed %d unrecognised. Remaining at %d baud\n",
+ u->speed, u->init_speed);
+ u->speed = u->init_speed;
+ }
+ if (u->speed == u->init_speed)
+ return 0;
+
+ /* Now, create the command that will set the UART speed */
+ /* CSR BCC header */
+ cmd[5] = 0x02; /* type = SET-REQ */
+ cmd[6] = 0x00; /* - msB */
+ cmd[9] = csr_seq & 0xFF; /* seq num */
+ cmd[10] = (csr_seq >> 8) & 0xFF;/* - msB */
+ csr_seq++;
+
+ divisor = (u->speed*64+7812)/15625;
+
+ /* No parity, one stop bit -> divisor |= 0x0000; */
+ cmd[15] = (divisor) & 0xFF; /* divider */
+ cmd[16] = (divisor >> 8) & 0xFF; /* - msB */
+ /* The rest of the payload will be 0x00 */
+
+#ifdef CSR_DEBUG
+ {
+ char temp[512];
+ int i;
+ for(i = 0; i < clen; i++)
+ sprintf(temp + (i*3), "-%02X", cmd[i]);
+ fprintf(stderr, "Writing CSR UART speed %d [%s]\n", clen, temp + 1);
+ // In theory, it should look like :
+ // 01-00-FC-13-C2-02-00-09-00-03-00-02-68-00-00-BF-0E-00-00-00-00-00-00
+ // 01-00-FC-13-C2-02-00-09-00-01-00-02-68-00-00-D8-01-00-00-00-00-00-00
+ }
+#endif
+
+ /* Send the command to set the CSR UART speed */
+ if (write(fd, cmd, clen) != clen) {
+ perror("Failed to write init command (SET_UART_SPEED)");
+ return -1;
+ }
+
+ nanosleep(&tm, NULL);
+ return 0;
+}
+
+/*
+ * Silicon Wave specific initialization
+ * Thomas Moser <thomas.moser@tmoser.ch>
+ */
+static int swave(int fd, struct uart_t *u, struct termios *ti)
+{
+ struct timespec tm = { 0, 500000 };
+ char cmd[10], rsp[100];
+ int r;
+
+ // Silicon Wave set baud rate command
+ // see HCI Vendor Specific Interface from Silicon Wave
+ // first send a "param access set" command to set the
+ // appropriate data fields in RAM. Then send a "HCI Reset
+ // Subcommand", e.g. "soft reset" to make the changes effective.
+
+ cmd[0] = HCI_COMMAND_PKT; // it's a command packet
+ cmd[1] = 0x0B; // OCF 0x0B = param access set
+ cmd[2] = 0xfc; // OGF bx111111 = vendor specific
+ cmd[3] = 0x06; // 6 bytes of data following
+ cmd[4] = 0x01; // param sub command
+ cmd[5] = 0x11; // tag 17 = 0x11 = HCI Transport Params
+ cmd[6] = 0x03; // length of the parameter following
+ cmd[7] = 0x01; // HCI Transport flow control enable
+ cmd[8] = 0x01; // HCI Transport Type = UART
+
+ switch (u->speed) {
+ case 19200:
+ cmd[9] = 0x03;
+ break;
+ case 38400:
+ cmd[9] = 0x02;
+ break;
+ case 57600:
+ cmd[9] = 0x01;
+ break;
+ case 115200:
+ cmd[9] = 0x00;
+ break;
+ default:
+ u->speed = 115200;
+ cmd[9] = 0x00;
+ break;
+ }
+
+ /* Send initialization command */
+ if (write(fd, cmd, 10) != 10) {
+ perror("Failed to write init command");
+ return -1;
+ }
+
+ // We should wait for a "GET Event" to confirm the success of
+ // the baud rate setting. Wait some time before reading. Better:
+ // read with timeout, parse data
+ // until correct answer, else error handling ... todo ...
+
+ nanosleep(&tm, NULL);
+
+ r = read(fd, rsp, sizeof(rsp));
+ if (r > 0) {
+ // guess it's okay, but we should parse the reply. But since
+ // I don't react on an error anyway ... todo
+ // Response packet format:
+ // 04 Event
+ // FF Vendor specific
+ // 07 Parameter length
+ // 0B Subcommand
+ // 01 Setevent
+ // 11 Tag specifying HCI Transport Layer Parameter
+ // 03 length
+ // 01 flow on
+ // 01 Hci Transport type = Uart
+ // xx Baud rate set (see above)
+ } else {
+ // ups, got error.
+ return -1;
+ }
+
+ // we probably got the reply. Now we must send the "soft reset"
+ // which is standard HCI RESET.
+
+ cmd[0] = HCI_COMMAND_PKT; // it's a command packet
+ cmd[1] = 0x03;
+ cmd[2] = 0x0c;
+ cmd[3] = 0x00;
+
+ /* Send reset command */
+ if (write(fd, cmd, 4) != 4) {
+ perror("Can't write Silicon Wave reset cmd.");
+ return -1;
+ }
+
+ nanosleep(&tm, NULL);
+
+ // now the uart baud rate on the silicon wave module is set and effective.
+ // change our own baud rate as well. Then there is a reset event comming in
+ // on the *new* baud rate. This is *undocumented*! The packet looks like this:
+ // 04 FF 01 0B (which would make that a confirmation of 0x0B = "Param
+ // subcommand class". So: change to new baud rate, read with timeout, parse
+ // data, error handling. BTW: all param access in Silicon Wave is done this way.
+ // Maybe this code would belong in a seperate file, or at least code reuse...
+
+ return 0;
+}
+
+/*
+ * ST Microelectronics specific initialization
+ * Marcel Holtmann <marcel@holtmann.org>
+ */
+static int st(int fd, struct uart_t *u, struct termios *ti)
+{
+ struct timespec tm = {0, 50000};
+ char cmd[5];
+
+ /* ST Microelectronics set baud rate command */
+ cmd[0] = HCI_COMMAND_PKT;
+ cmd[1] = 0x46; // OCF = Hci_Cmd_ST_Set_Uart_Baud_Rate
+ cmd[2] = 0xfc; // OGF = Vendor specific
+ cmd[3] = 0x01;
+
+ switch (u->speed) {
+ case 9600:
+ cmd[4] = 0x09;
+ break;
+ case 19200:
+ cmd[4] = 0x0b;
+ break;
+ case 38400:
+ cmd[4] = 0x0d;
+ break;
+ case 57600:
+ cmd[4] = 0x0e;
+ break;
+ case 115200:
+ cmd[4] = 0x10;
+ break;
+ case 230400:
+ cmd[4] = 0x12;
+ break;
+ case 460800:
+ cmd[4] = 0x13;
+ break;
+ case 921600:
+ cmd[4] = 0x14;
+ break;
+ default:
+ cmd[4] = 0x10;
+ u->speed = 115200;
+ break;
+ }
+
+ /* Send initialization command */
+ if (write(fd, cmd, 5) != 5) {
+ perror("Failed to write init command");
+ return -1;
+ }
+
+ nanosleep(&tm, NULL);
+ return 0;
+}
+
+static int stlc2500(int fd, struct uart_t *u, struct termios *ti)
+{
+ bdaddr_t bdaddr;
+ unsigned char resp[10];
+ int n;
+ int rvalue;
+
+ /* STLC2500 has an ericsson core */
+ rvalue = ericsson(fd, u, ti);
+ if (rvalue != 0)
+ return rvalue;
+
+#ifdef STLC2500_DEBUG
+ fprintf(stderr, "Setting speed\n");
+#endif
+ if (set_speed(fd, ti, u->speed) < 0) {
+ perror("Can't set baud rate");
+ return -1;
+ }
+
+#ifdef STLC2500_DEBUG
+ fprintf(stderr, "Speed set...\n");
+#endif
+
+ /* Read reply */
+ if ((n = read_hci_event(fd, resp, 10)) < 0) {
+ fprintf(stderr, "Failed to set baud rate on chip\n");
+ return -1;
+ }
+
+#ifdef STLC2500_DEBUG
+ for (i = 0; i < n; i++) {
+ fprintf(stderr, "resp[%d] = %02x\n", i, resp[i]);
+ }
+#endif
+
+ str2ba(u->bdaddr, &bdaddr);
+ return stlc2500_init(fd, &bdaddr);
+}
+
+static int bgb2xx(int fd, struct uart_t *u, struct termios *ti)
+{
+ bdaddr_t bdaddr;
+
+ str2ba(u->bdaddr, &bdaddr);
+
+ return bgb2xx_init(fd, &bdaddr);
+}
+
+/*
+ * Broadcom specific initialization
+ * Extracted from Jungo openrg
+ */
+static int bcm2035(int fd, struct uart_t *u, struct termios *ti)
+{
+ int n;
+ unsigned char cmd[30], resp[30];
+
+ /* Reset the BT Chip */
+ memset(cmd, 0, sizeof(cmd));
+ memset(resp, 0, sizeof(resp));
+ cmd[0] = HCI_COMMAND_PKT;
+ cmd[1] = 0x03;
+ cmd[2] = 0x0c;
+ cmd[3] = 0x00;
+
+ /* Send command */
+ if (write(fd, cmd, 4) != 4) {
+ fprintf(stderr, "Failed to write reset command\n");
+ return -1;
+ }
+
+ /* Read reply */
+ if ((n = read_hci_event(fd, resp, 4)) < 0) {
+ fprintf(stderr, "Failed to reset chip\n");
+ return -1;
+ }
+
+ if (u->bdaddr != NULL) {
+ /* Set BD_ADDR */
+ memset(cmd, 0, sizeof(cmd));
+ memset(resp, 0, sizeof(resp));
+ cmd[0] = HCI_COMMAND_PKT;
+ cmd[1] = 0x01;
+ cmd[2] = 0xfc;
+ cmd[3] = 0x06;
+ str2ba(u->bdaddr, (bdaddr_t *) (cmd + 4));
+
+ /* Send command */
+ if (write(fd, cmd, 10) != 10) {
+ fprintf(stderr, "Failed to write BD_ADDR command\n");
+ return -1;
+ }
+
+ /* Read reply */
+ if ((n = read_hci_event(fd, resp, 10)) < 0) {
+ fprintf(stderr, "Failed to set BD_ADDR\n");
+ return -1;
+ }
+ }
+
+ /* Read the local version info */
+ memset(cmd, 0, sizeof(cmd));
+ memset(resp, 0, sizeof(resp));
+ cmd[0] = HCI_COMMAND_PKT;
+ cmd[1] = 0x01;
+ cmd[2] = 0x10;
+ cmd[3] = 0x00;
+
+ /* Send command */
+ if (write(fd, cmd, 4) != 4) {
+ fprintf(stderr, "Failed to write \"read local version\" "
+ "command\n");
+ return -1;
+ }
+
+ /* Read reply */
+ if ((n = read_hci_event(fd, resp, 4)) < 0) {
+ fprintf(stderr, "Failed to read local version\n");
+ return -1;
+ }
+
+ /* Read the local supported commands info */
+ memset(cmd, 0, sizeof(cmd));
+ memset(resp, 0, sizeof(resp));
+ cmd[0] = HCI_COMMAND_PKT;
+ cmd[1] = 0x02;
+ cmd[2] = 0x10;
+ cmd[3] = 0x00;
+
+ /* Send command */
+ if (write(fd, cmd, 4) != 4) {
+ fprintf(stderr, "Failed to write \"read local supported "
+ "commands\" command\n");
+ return -1;
+ }
+
+ /* Read reply */
+ if ((n = read_hci_event(fd, resp, 4)) < 0) {
+ fprintf(stderr, "Failed to read local supported commands\n");
+ return -1;
+ }
+
+ /* Set the baud rate */
+ memset(cmd, 0, sizeof(cmd));
+ memset(resp, 0, sizeof(resp));
+ cmd[0] = HCI_COMMAND_PKT;
+ cmd[1] = 0x18;
+ cmd[2] = 0xfc;
+ cmd[3] = 0x02;
+ switch (u->speed) {
+ case 57600:
+ cmd[4] = 0x00;
+ cmd[5] = 0xe6;
+ break;
+ case 230400:
+ cmd[4] = 0x22;
+ cmd[5] = 0xfa;
+ break;
+ case 460800:
+ cmd[4] = 0x22;
+ cmd[5] = 0xfd;
+ break;
+ case 921600:
+ cmd[4] = 0x55;
+ cmd[5] = 0xff;
+ break;
+ default:
+ /* Default is 115200 */
+ cmd[4] = 0x00;
+ cmd[5] = 0xf3;
+ break;
+ }
+ fprintf(stderr, "Baud rate parameters: DHBR=0x%2x,DLBR=0x%2x\n",
+ cmd[4], cmd[5]);
+
+ /* Send command */
+ if (write(fd, cmd, 6) != 6) {
+ fprintf(stderr, "Failed to write \"set baud rate\" command\n");
+ return -1;
+ }
+
+ if ((n = read_hci_event(fd, resp, 6)) < 0) {
+ fprintf(stderr, "Failed to set baud rate\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+struct uart_t uart[] = {
+ { "any", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200,
+ FLOW_CTL, DISABLE_PM, NULL, NULL },
+
+ { "ericsson", 0x0000, 0x0000, HCI_UART_H4, 57600, 115200,
+ FLOW_CTL, DISABLE_PM, NULL, ericsson },
+
+ { "digi", 0x0000, 0x0000, HCI_UART_H4, 9600, 115200,
+ FLOW_CTL, DISABLE_PM, NULL, digi },
+
+ { "bcsp", 0x0000, 0x0000, HCI_UART_BCSP, 115200, 115200,
+ 0, DISABLE_PM, NULL, bcsp },
+
+ /* Xircom PCMCIA cards: Credit Card Adapter and Real Port Adapter */
+ { "xircom", 0x0105, 0x080a, HCI_UART_H4, 115200, 115200,
+ FLOW_CTL, DISABLE_PM, NULL, NULL },
+
+ /* CSR Casira serial adapter or BrainBoxes serial dongle (BL642) */
+ { "csr", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200,
+ FLOW_CTL, DISABLE_PM, NULL, csr },
+
+ /* BrainBoxes PCMCIA card (BL620) */
+ { "bboxes", 0x0160, 0x0002, HCI_UART_H4, 115200, 460800,
+ FLOW_CTL, DISABLE_PM, NULL, csr },
+
+ /* Silicon Wave kits */
+ { "swave", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200,
+ FLOW_CTL, DISABLE_PM, NULL, swave },
+
+ /* Texas Instruments Bluelink (BRF) modules */
+ { "texas", 0x0000, 0x0000, HCI_UART_LL, 115200, 115200,
+ FLOW_CTL, DISABLE_PM, NULL, texas, texas2 },
+
+ { "texasalt", 0x0000, 0x0000, HCI_UART_LL, 115200, 115200,
+ FLOW_CTL, DISABLE_PM, NULL, texasalt, NULL },
+
+ /* ST Microelectronics minikits based on STLC2410/STLC2415 */
+ { "st", 0x0000, 0x0000, HCI_UART_H4, 57600, 115200,
+ FLOW_CTL, DISABLE_PM, NULL, st },
+
+ /* ST Microelectronics minikits based on STLC2500 */
+ { "stlc2500", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200,
+ FLOW_CTL, DISABLE_PM, "00:80:E1:00:AB:BA", stlc2500 },
+
+ /* Philips generic Ericsson IP core based */
+ { "philips", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200,
+ FLOW_CTL, DISABLE_PM, NULL, NULL },
+
+ /* Philips BGB2xx Module */
+ { "bgb2xx", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200,
+ FLOW_CTL, DISABLE_PM, "BD:B2:10:00:AB:BA", bgb2xx },
+
+ /* Sphinx Electronics PICO Card */
+ { "picocard", 0x025e, 0x1000, HCI_UART_H4, 115200, 115200,
+ FLOW_CTL, DISABLE_PM, NULL, NULL },
+
+ /* Inventel BlueBird Module */
+ { "inventel", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200,
+ FLOW_CTL, DISABLE_PM, NULL, NULL },
+
+ /* COM One Platinium Bluetooth PC Card */
+ { "comone", 0xffff, 0x0101, HCI_UART_BCSP, 115200, 115200,
+ 0, DISABLE_PM, NULL, bcsp },
+
+ /* TDK Bluetooth PC Card and IBM Bluetooth PC Card II */
+ { "tdk", 0x0105, 0x4254, HCI_UART_BCSP, 115200, 115200,
+ 0, DISABLE_PM, NULL, bcsp },
+
+ /* Socket Bluetooth CF Card (Rev G) */
+ { "socket", 0x0104, 0x0096, HCI_UART_BCSP, 230400, 230400,
+ 0, DISABLE_PM, NULL, bcsp },
+
+ /* 3Com Bluetooth Card (Version 3.0) */
+ { "3com", 0x0101, 0x0041, HCI_UART_H4, 115200, 115200,
+ FLOW_CTL, DISABLE_PM, NULL, csr },
+
+ /* AmbiCom BT2000C Bluetooth PC/CF Card */
+ { "bt2000c", 0x022d, 0x2000, HCI_UART_H4, 57600, 460800,
+ FLOW_CTL, DISABLE_PM, NULL, csr },
+
+ /* Zoom Bluetooth PCMCIA Card */
+ { "zoom", 0x0279, 0x950b, HCI_UART_BCSP, 115200, 115200,
+ 0, DISABLE_PM, NULL, bcsp },
+
+ /* Sitecom CN-504 PCMCIA Card */
+ { "sitecom", 0x0279, 0x950b, HCI_UART_BCSP, 115200, 115200,
+ 0, DISABLE_PM, NULL, bcsp },
+
+ /* Billionton PCBTC1 PCMCIA Card */
+ { "billionton", 0x0279, 0x950b, HCI_UART_BCSP, 115200, 115200,
+ 0, DISABLE_PM, NULL, bcsp },
+
+ /* Broadcom BCM2035 */
+ { "bcm2035", 0x0A5C, 0x2035, HCI_UART_H4, 115200, 460800,
+ FLOW_CTL, DISABLE_PM, NULL, bcm2035 },
+
+ { "ath3k", 0x0000, 0x0000, HCI_UART_ATH3K, 115200, 115200,
+ FLOW_CTL, DISABLE_PM, NULL, ath3k_ps, ath3k_pm },
+
+ /* QUALCOMM BTS */
+ { "qualcomm", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200,
+ FLOW_CTL, DISABLE_PM, NULL, qualcomm, NULL },
+
+ { NULL, 0 }
+};
+
+static struct uart_t * get_by_id(int m_id, int p_id)
+{
+ int i;
+ for (i = 0; uart[i].type; i++) {
+ if (uart[i].m_id == m_id && uart[i].p_id == p_id)
+ return &uart[i];
+ }
+ return NULL;
+}
+
+static struct uart_t * get_by_type(char *type)
+{
+ int i;
+ for (i = 0; uart[i].type; i++) {
+ if (!strcmp(uart[i].type, type))
+ return &uart[i];
+ }
+ return NULL;
+}
+
+/* Initialize UART driver */
+static int init_uart(char *dev, struct uart_t *u, int send_break, int raw)
+{
+ struct termios ti;
+ int fd, i;
+ unsigned long flags = 0;
+
+ if (raw)
+ flags |= 1 << HCI_UART_RAW_DEVICE;
+
+ fd = open(dev, O_RDWR | O_NOCTTY);
+ if (fd < 0) {
+ perror("Can't open serial port");
+ return -1;
+ }
+
+ tcflush(fd, TCIOFLUSH);
+
+ if (tcgetattr(fd, &ti) < 0) {
+ perror("Can't get port settings");
+ return -1;
+ }
+
+ cfmakeraw(&ti);
+
+ ti.c_cflag |= CLOCAL;
+ if (u->flags & FLOW_CTL)
+ ti.c_cflag |= CRTSCTS;
+ else
+ ti.c_cflag &= ~CRTSCTS;
+
+ if (tcsetattr(fd, TCSANOW, &ti) < 0) {
+ perror("Can't set port settings");
+ return -1;
+ }
+
+ /* Set initial baudrate */
+ if (set_speed(fd, &ti, u->init_speed) < 0) {
+ perror("Can't set initial baud rate");
+ return -1;
+ }
+
+ tcflush(fd, TCIOFLUSH);
+
+ if (send_break) {
+ tcsendbreak(fd, 0);
+ usleep(500000);
+ }
+
+ if (u->init && u->init(fd, u, &ti) < 0)
+ return -1;
+
+ tcflush(fd, TCIOFLUSH);
+
+ /* Set actual baudrate */
+ if (set_speed(fd, &ti, u->speed) < 0) {
+ perror("Can't set baud rate");
+ return -1;
+ }
+
+ /* Set TTY to N_HCI line discipline */
+ i = N_HCI;
+ if (ioctl(fd, TIOCSETD, &i) < 0) {
+ perror("Can't set line discipline");
+ return -1;
+ }
+
+ if (flags && ioctl(fd, HCIUARTSETFLAGS, flags) < 0) {
+ perror("Can't set UART flags");
+ return -1;
+ }
+
+ if (ioctl(fd, HCIUARTSETPROTO, u->proto) < 0) {
+ perror("Can't set device");
+ return -1;
+ }
+
+ if (u->post && u->post(fd, u, &ti) < 0)
+ return -1;
+
+ return fd;
+}
+
+static void usage(void)
+{
+ printf("hciattach - HCI UART driver initialization utility\n");
+ printf("Usage:\n");
+ printf("\thciattach [-n] [-p] [-b] [-r] [-t timeout] [-s initial_speed] <tty> <type | id> [speed] [flow|noflow] [bdaddr]\n");
+ printf("\thciattach -l\n");
+}
+
+int main(int argc, char *argv[])
+{
+ struct uart_t *u = NULL;
+ int detach, printpid, raw, opt, i, n, ld, err;
+ int to = 10;
+ int init_speed = 0;
+ int send_break = 0;
+ pid_t pid;
+ struct sigaction sa;
+ struct pollfd p;
+ sigset_t sigs;
+ char dev[PATH_MAX];
+
+ detach = 1;
+ printpid = 0;
+ raw = 0;
+
+ while ((opt=getopt(argc, argv, "bnpt:s:lr")) != EOF) {
+ switch(opt) {
+ case 'b':
+ send_break = 1;
+ break;
+
+ case 'n':
+ detach = 0;
+ break;
+
+ case 'p':
+ printpid = 1;
+ break;
+
+ case 't':
+ to = atoi(optarg);
+ break;
+
+ case 's':
+ init_speed = atoi(optarg);
+ break;
+
+ case 'l':
+ for (i = 0; uart[i].type; i++) {
+ printf("%-10s0x%04x,0x%04x\n", uart[i].type,
+ uart[i].m_id, uart[i].p_id);
+ }
+ exit(0);
+
+ case 'r':
+ raw = 1;
+ break;
+
+ default:
+ usage();
+ exit(1);
+ }
+ }
+
+ n = argc - optind;
+ if (n < 2) {
+ usage();
+ exit(1);
+ }
+
+ for (n = 0; optind < argc; n++, optind++) {
+ char *opt;
+
+ opt = argv[optind];
+
+ switch(n) {
+ case 0:
+ dev[0] = 0;
+ if (!strchr(opt, '/'))
+ strcpy(dev, "/dev/");
+ strcat(dev, opt);
+ break;
+
+ case 1:
+ if (strchr(argv[optind], ',')) {
+ int m_id, p_id;
+ sscanf(argv[optind], "%x,%x", &m_id, &p_id);
+ u = get_by_id(m_id, p_id);
+ } else {
+ u = get_by_type(opt);
+ }
+
+ if (!u) {
+ fprintf(stderr, "Unknown device type or id\n");
+ exit(1);
+ }
+
+ break;
+
+ case 2:
+ u->speed = atoi(argv[optind]);
+ break;
+
+ case 3:
+ if (!strcmp("flow", argv[optind]))
+ u->flags |= FLOW_CTL;
+ else
+ u->flags &= ~FLOW_CTL;
+ break;
+
+ case 4:
+ if (!strcmp("sleep", argv[optind]))
+ u->pm = ENABLE_PM;
+ else
+ u->pm = DISABLE_PM;
+ break;
+
+ case 5:
+ u->bdaddr = argv[optind];
+ break;
+ }
+ }
+
+ if (!u) {
+ fprintf(stderr, "Unknown device type or id\n");
+ exit(1);
+ }
+
+ /* If user specified a initial speed, use that instead of
+ the hardware's default */
+ if (init_speed)
+ u->init_speed = init_speed;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_flags = SA_NOCLDSTOP;
+ sa.sa_handler = sig_alarm;
+ sigaction(SIGALRM, &sa, NULL);
+
+ /* 10 seconds should be enough for initialization */
+ alarm(to);
+ bcsp_max_retries = to;
+
+ n = init_uart(dev, u, send_break, raw);
+ if (n < 0) {
+ perror("Can't initialize device");
+ exit(1);
+ }
+
+ printf("Device setup complete\n");
+
+ alarm(0);
+
+ 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);
+
+ sa.sa_handler = sig_hup;
+ sigaction(SIGHUP, &sa, NULL);
+
+ if (detach) {
+ if ((pid = fork())) {
+ if (printpid)
+ printf("%d\n", pid);
+ return 0;
+ }
+
+ for (i = 0; i < 20; i++)
+ if (i != n)
+ close(i);
+ }
+
+ p.fd = n;
+ p.events = POLLERR | POLLHUP;
+
+ sigfillset(&sigs);
+ sigdelset(&sigs, SIGCHLD);
+ sigdelset(&sigs, SIGPIPE);
+ sigdelset(&sigs, SIGTERM);
+ sigdelset(&sigs, SIGINT);
+ sigdelset(&sigs, SIGHUP);
+
+ while (!__io_canceled) {
+ p.revents = 0;
+ err = ppoll(&p, 1, NULL, &sigs);
+ if (err < 0 && errno == EINTR)
+ continue;
+ if (err)
+ break;
+ }
+
+ /* Restore TTY line discipline */
+ ld = N_TTY;
+ if (ioctl(n, TIOCSETD, &ld) < 0) {
+ perror("Can't restore line discipline");
+ exit(1);
+ }
+
+ return 0;
+}
diff --git a/tools/hciattach.h b/tools/hciattach.h
new file mode 100644
index 0000000..fed0d11
--- /dev/null
+++ b/tools/hciattach.h
@@ -0,0 +1,56 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2003-2010 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
+ *
+ */
+
+#include <termios.h>
+
+#ifndef N_HCI
+#define N_HCI 15
+#endif
+
+#define HCIUARTSETPROTO _IOW('U', 200, int)
+#define HCIUARTGETPROTO _IOR('U', 201, int)
+#define HCIUARTGETDEVICE _IOR('U', 202, int)
+#define HCIUARTSETFLAGS _IOW('U', 203, int)
+#define HCIUARTGETFLAGS _IOR('U', 204, int)
+
+#define HCI_UART_H4 0
+#define HCI_UART_BCSP 1
+#define HCI_UART_3WIRE 2
+#define HCI_UART_H4DS 3
+#define HCI_UART_LL 4
+#define HCI_UART_ATH3K 5
+
+#define HCI_UART_RAW_DEVICE 0
+
+int read_hci_event(int fd, unsigned char* buf, int size);
+int set_speed(int fd, struct termios *ti, int speed);
+
+int texas_init(int fd, struct termios *ti);
+int texas_post(int fd, struct termios *ti);
+int texasalt_init(int fd, int speed, struct termios *ti);
+int stlc2500_init(int fd, bdaddr_t *bdaddr);
+int bgb2xx_init(int dd, bdaddr_t *bdaddr);
+int ath3k_init(int fd, int speed, int init_speed, char *bdaddr,
+ struct termios *ti);
+int ath3k_post(int fd, int pm);
+int qualcomm_init(int fd, int speed, struct termios *ti, const char *bdaddr);
diff --git a/tools/hciattach_ath3k.c b/tools/hciattach_ath3k.c
new file mode 100644
index 0000000..728e660
--- /dev/null
+++ b/tools/hciattach_ath3k.c
@@ -0,0 +1,1049 @@
+/*
+ * Copyright (c) 2009-2010 Atheros Communications Inc.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "hciattach.h"
+
+#define TRUE 1
+#define FALSE 0
+
+#define FW_PATH "/lib/firmware/ar3k/"
+
+struct ps_cfg_entry {
+ uint32_t id;
+ uint32_t len;
+ uint8_t *data;
+};
+
+struct ps_entry_type {
+ unsigned char type;
+ unsigned char array;
+};
+
+#define MAX_TAGS 50
+#define PS_HDR_LEN 4
+#define HCI_VENDOR_CMD_OGF 0x3F
+#define HCI_PS_CMD_OCF 0x0B
+
+struct ps_cfg_entry ps_list[MAX_TAGS];
+
+static void load_hci_ps_hdr(uint8_t *cmd, uint8_t ps_op, int len, int index)
+{
+ hci_command_hdr *ch = (void *)cmd;
+
+ ch->opcode = htobs(cmd_opcode_pack(HCI_VENDOR_CMD_OGF,
+ HCI_PS_CMD_OCF));
+ ch->plen = len + PS_HDR_LEN;
+ cmd += HCI_COMMAND_HDR_SIZE;
+
+ cmd[0] = ps_op;
+ cmd[1] = index;
+ cmd[2] = index >> 8;
+ cmd[3] = len;
+}
+
+#define PS_EVENT_LEN 100
+
+/*
+ * Send HCI command and wait for command complete event.
+ * The event buffer has to be freed by the caller.
+ */
+static int send_hci_cmd_sync(int dev, uint8_t *cmd, int len, uint8_t **event)
+{
+ int err;
+ uint8_t *hci_event;
+ uint8_t pkt_type = HCI_COMMAND_PKT;
+
+ if (len == 0)
+ return len;
+
+ if (write(dev, &pkt_type, 1) != 1)
+ return -EILSEQ;
+ if (write(dev, (unsigned char *)cmd, len) != len)
+ return -EILSEQ;
+
+ hci_event = (uint8_t *)malloc(PS_EVENT_LEN);
+ if (!hci_event)
+ return -ENOMEM;
+
+ err = read_hci_event(dev, (unsigned char *)hci_event, PS_EVENT_LEN);
+ if (err > 0) {
+ *event = hci_event;
+ } else {
+ free(hci_event);
+ return -EILSEQ;
+ }
+
+ return len;
+}
+
+#define HCI_EV_SUCCESS 0x00
+
+static int read_ps_event(uint8_t *event, uint16_t ocf)
+{
+ hci_event_hdr *eh;
+ uint16_t opcode = htobs(cmd_opcode_pack(HCI_VENDOR_CMD_OGF, ocf));
+
+ event++;
+
+ eh = (void *)event;
+ event += HCI_EVENT_HDR_SIZE;
+
+ if (eh->evt == EVT_CMD_COMPLETE) {
+ evt_cmd_complete *cc = (void *)event;
+
+ event += EVT_CMD_COMPLETE_SIZE;
+
+ if (cc->opcode == opcode && event[0] == HCI_EV_SUCCESS)
+ return 0;
+ else
+ return -EILSEQ;
+ }
+
+ return -EILSEQ;
+}
+
+static int write_cmd(int fd, uint8_t *buffer, int len)
+{
+ uint8_t *event;
+ int err;
+
+ err = send_hci_cmd_sync(fd, buffer, len, &event);
+ if (err < 0)
+ return err;
+
+ err = read_ps_event(event, HCI_PS_CMD_OCF);
+ if (event)
+ free(event);
+
+ return err;
+}
+
+#define PS_WRITE 1
+#define PS_RESET 2
+#define WRITE_PATCH 8
+#define ENABLE_PATCH 11
+
+#define HCI_PS_CMD_HDR_LEN 7
+
+#define PS_RESET_PARAM_LEN 6
+#define HCI_MAX_CMD_SIZE 260
+#define PS_RESET_CMD_LEN (HCI_PS_CMD_HDR_LEN + PS_RESET_PARAM_LEN)
+
+#define PS_ID_MASK 0xFF
+
+/* Sends PS commands using vendor specficic HCI commands */
+static int write_ps_cmd(int fd, uint8_t opcode, uint32_t ps_param)
+{
+ uint8_t cmd[HCI_MAX_CMD_SIZE];
+ uint32_t i;
+
+ switch (opcode) {
+ case ENABLE_PATCH:
+ load_hci_ps_hdr(cmd, opcode, 0, 0x00);
+
+ if (write_cmd(fd, cmd, HCI_PS_CMD_HDR_LEN) < 0)
+ return -EILSEQ;
+ break;
+
+ case PS_RESET:
+ load_hci_ps_hdr(cmd, opcode, PS_RESET_PARAM_LEN, 0x00);
+
+ cmd[7] = 0x00;
+ cmd[PS_RESET_CMD_LEN - 2] = ps_param & PS_ID_MASK;
+ cmd[PS_RESET_CMD_LEN - 1] = (ps_param >> 8) & PS_ID_MASK;
+
+ if (write_cmd(fd, cmd, PS_RESET_CMD_LEN) < 0)
+ return -EILSEQ;
+ break;
+
+ case PS_WRITE:
+ for (i = 0; i < ps_param; i++) {
+ load_hci_ps_hdr(cmd, opcode, ps_list[i].len,
+ ps_list[i].id);
+
+ memcpy(&cmd[HCI_PS_CMD_HDR_LEN], ps_list[i].data,
+ ps_list[i].len);
+
+ if (write_cmd(fd, cmd, ps_list[i].len +
+ HCI_PS_CMD_HDR_LEN) < 0)
+ return -EILSEQ;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+#define __is_delim(ch) ((ch) == ':')
+#define MAX_PREAMBLE_LEN 4
+
+/* Parse PS entry preamble of format [X:X] for main type and subtype */
+static int get_ps_type(char *ptr, int index, char *type, char *sub_type)
+{
+ int i;
+ int delim = FALSE;
+
+ if (index > MAX_PREAMBLE_LEN)
+ return -EILSEQ;
+
+ for (i = 1; i < index; i++) {
+ if (__is_delim(ptr[i])) {
+ delim = TRUE;
+ continue;
+ }
+
+ if (isalpha(ptr[i])) {
+ if (delim == FALSE)
+ (*type) = toupper(ptr[i]);
+ else
+ (*sub_type) = toupper(ptr[i]);
+ }
+ }
+
+ return 0;
+}
+
+#define ARRAY 'A'
+#define STRING 'S'
+#define DECIMAL 'D'
+#define BINARY 'B'
+
+#define PS_HEX 0
+#define PS_DEC 1
+
+static int get_input_format(char *buf, struct ps_entry_type *format)
+{
+ char *ptr = NULL;
+ char type = '\0';
+ char sub_type = '\0';
+
+ format->type = PS_HEX;
+ format->array = TRUE;
+
+ if (strstr(buf, "[") != buf)
+ return 0;
+
+ ptr = strstr(buf, "]");
+ if (!ptr)
+ return -EILSEQ;
+
+ if (get_ps_type(buf, ptr - buf, &type, &sub_type) < 0)
+ return -EILSEQ;
+
+ /* Check is data type is of array */
+ if (type == ARRAY || sub_type == ARRAY)
+ format->array = TRUE;
+
+ if (type == STRING || sub_type == STRING)
+ format->array = FALSE;
+
+ if (type == DECIMAL || type == BINARY)
+ format->type = PS_DEC;
+ else
+ format->type = PS_HEX;
+
+ return 0;
+}
+
+#define UNDEFINED 0xFFFF
+
+static unsigned int read_data_in_section(char *buf, struct ps_entry_type type)
+{
+ char *ptr = buf;
+
+ if (!buf)
+ return UNDEFINED;
+
+ if (buf == strstr(buf, "[")) {
+ ptr = strstr(buf, "]");
+ if (!ptr)
+ return UNDEFINED;
+
+ ptr++;
+ }
+
+ if (type.type == PS_HEX && type.array != TRUE)
+ return strtol(ptr, NULL, 16);
+
+ return UNDEFINED;
+}
+
+struct tag_info {
+ unsigned section;
+ unsigned line_count;
+ unsigned char_cnt;
+ unsigned byte_count;
+};
+
+static inline int update_char_count(const char *buf)
+{
+ char *end_ptr;
+
+ if (strstr(buf, "[") == buf) {
+ end_ptr = strstr(buf, "]");
+ if (!end_ptr)
+ return 0;
+ else
+ return (end_ptr - buf) + 1;
+ }
+
+ return 0;
+}
+
+/* Read PS entries as string, convert and add to Hex array */
+static void update_tag_data(struct ps_cfg_entry *tag,
+ struct tag_info *info, const char *ptr)
+{
+ char buf[3];
+
+ buf[2] = '\0';
+
+ strncpy(buf, &ptr[info->char_cnt], 2);
+ tag->data[info->byte_count] = strtol(buf, NULL, 16);
+ info->char_cnt += 3;
+ info->byte_count++;
+
+ strncpy(buf, &ptr[info->char_cnt], 2);
+ tag->data[info->byte_count] = strtol(buf, NULL, 16);
+ info->char_cnt += 3;
+ info->byte_count++;
+}
+
+#define PS_UNDEF 0
+#define PS_ID 1
+#define PS_LEN 2
+#define PS_DATA 3
+
+#define PS_MAX_LEN 500
+#define LINE_SIZE_MAX (PS_MAX_LEN * 2)
+#define ENTRY_PER_LINE 16
+
+#define __check_comment(buf) (((buf)[0] == '/') && ((buf)[1] == '/'))
+#define __skip_space(str) while (*(str) == ' ') ((str)++)
+
+static int ath_parse_ps(FILE *stream)
+{
+ char buf[LINE_SIZE_MAX + 1];
+ char *ptr;
+ uint8_t tag_cnt = 0;
+ int16_t byte_count = 0;
+ struct ps_entry_type format;
+ struct tag_info status = { 0, 0, 0, 0 };
+
+ do {
+ int read_count;
+ struct ps_cfg_entry *tag;
+
+ ptr = fgets(buf, LINE_SIZE_MAX, stream);
+ if (!ptr)
+ break;
+
+ __skip_space(ptr);
+ if (__check_comment(ptr))
+ continue;
+
+ /* Lines with a '#' will be followed by new PS entry */
+ if (ptr == strstr(ptr, "#")) {
+ if (status.section != PS_UNDEF) {
+ return -EILSEQ;
+ } else {
+ status.section = PS_ID;
+ continue;
+ }
+ }
+
+ tag = &ps_list[tag_cnt];
+
+ switch (status.section) {
+ case PS_ID:
+ if (get_input_format(ptr, &format) < 0)
+ return -EILSEQ;
+
+ tag->id = read_data_in_section(ptr, format);
+ status.section = PS_LEN;
+ break;
+
+ case PS_LEN:
+ if (get_input_format(ptr, &format) < 0)
+ return -EILSEQ;
+
+ byte_count = read_data_in_section(ptr, format);
+ if (byte_count > PS_MAX_LEN)
+ return -EILSEQ;
+
+ tag->len = byte_count;
+ tag->data = (uint8_t *)malloc(byte_count);
+
+ status.section = PS_DATA;
+ status.line_count = 0;
+ break;
+
+ case PS_DATA:
+ if (status.line_count == 0)
+ if (get_input_format(ptr, &format) < 0)
+ return -EILSEQ;
+
+ __skip_space(ptr);
+
+ status.char_cnt = update_char_count(ptr);
+
+ read_count = (byte_count > ENTRY_PER_LINE) ?
+ ENTRY_PER_LINE : byte_count;
+
+ if (format.type == PS_HEX && format.array == TRUE) {
+ while (read_count > 0) {
+ update_tag_data(tag, &status, ptr);
+ read_count -= 2;
+ }
+
+ if (byte_count > ENTRY_PER_LINE)
+ byte_count -= ENTRY_PER_LINE;
+ else
+ byte_count = 0;
+ }
+
+ status.line_count++;
+
+ if (byte_count == 0)
+ memset(&status, 0x00, sizeof(struct tag_info));
+
+ if (status.section == PS_UNDEF)
+ tag_cnt++;
+
+ if (tag_cnt == MAX_TAGS)
+ return -EILSEQ;
+ break;
+ }
+ } while (ptr);
+
+ return tag_cnt;
+}
+
+#define MAX_PATCH_CMD 244
+struct patch_entry {
+ int16_t len;
+ uint8_t data[MAX_PATCH_CMD];
+};
+
+#define SET_PATCH_RAM_ID 0x0D
+#define SET_PATCH_RAM_CMD_SIZE 11
+#define ADDRESS_LEN 4
+static int set_patch_ram(int dev, char *patch_loc, int len)
+{
+ int err;
+ uint8_t cmd[20];
+ int i, j;
+ char loc_byte[3];
+ uint8_t *event;
+ uint8_t *loc_ptr = &cmd[7];
+
+ if (!patch_loc)
+ return -1;
+
+ loc_byte[2] = '\0';
+
+ load_hci_ps_hdr(cmd, SET_PATCH_RAM_ID, ADDRESS_LEN, 0);
+
+ for (i = 0, j = 3; i < 4; i++, j--) {
+ loc_byte[0] = patch_loc[0];
+ loc_byte[1] = patch_loc[1];
+ loc_ptr[j] = strtol(loc_byte, NULL, 16);
+ patch_loc += 2;
+ }
+
+ err = send_hci_cmd_sync(dev, cmd, SET_PATCH_RAM_CMD_SIZE, &event);
+ if (err < 0)
+ return err;
+
+ err = read_ps_event(event, HCI_PS_CMD_OCF);
+
+ if (event)
+ free(event);
+
+ return err;
+}
+
+#define PATCH_LOC_KEY "DA:"
+#define PATCH_LOC_STRING_LEN 8
+static int ps_patch_download(int fd, FILE *stream)
+{
+ char byte[3];
+ char ptr[MAX_PATCH_CMD + 1];
+ int byte_cnt;
+ int patch_count = 0;
+ char patch_loc[PATCH_LOC_STRING_LEN + 1];
+
+ byte[2] = '\0';
+
+ while (fgets(ptr, MAX_PATCH_CMD, stream)) {
+ if (strlen(ptr) <= 1)
+ continue;
+ else if (strstr(ptr, PATCH_LOC_KEY) == ptr) {
+ strncpy(patch_loc, &ptr[sizeof(PATCH_LOC_KEY) - 1],
+ PATCH_LOC_STRING_LEN);
+ if (set_patch_ram(fd, patch_loc, sizeof(patch_loc)) < 0)
+ return -1;
+ } else if (isxdigit(ptr[0]))
+ break;
+ else
+ return -1;
+ }
+
+ byte_cnt = strtol(ptr, NULL, 16);
+
+ while (byte_cnt > 0) {
+ int i;
+ uint8_t cmd[HCI_MAX_CMD_SIZE];
+ struct patch_entry patch;
+
+ if (byte_cnt > MAX_PATCH_CMD)
+ patch.len = MAX_PATCH_CMD;
+ else
+ patch.len = byte_cnt;
+
+ for (i = 0; i < patch.len; i++) {
+ if (!fgets(byte, 3, stream))
+ return -1;
+
+ patch.data[i] = strtoul(byte, NULL, 16);
+ }
+
+ load_hci_ps_hdr(cmd, WRITE_PATCH, patch.len, patch_count);
+ memcpy(&cmd[HCI_PS_CMD_HDR_LEN], patch.data, patch.len);
+
+ if (write_cmd(fd, cmd, patch.len + HCI_PS_CMD_HDR_LEN) < 0)
+ return -1;
+
+ patch_count++;
+ byte_cnt = byte_cnt - MAX_PATCH_CMD;
+ }
+
+ if (write_ps_cmd(fd, ENABLE_PATCH, 0) < 0)
+ return -1;
+
+ return patch_count;
+}
+
+#define PS_RAM_SIZE 2048
+
+static int ps_config_download(int fd, int tag_count)
+{
+ if (write_ps_cmd(fd, PS_RESET, PS_RAM_SIZE) < 0)
+ return -1;
+
+ if (tag_count > 0)
+ if (write_ps_cmd(fd, PS_WRITE, tag_count) < 0)
+ return -1;
+ return 0;
+}
+
+#define PS_ASIC_FILE "PS_ASIC.pst"
+#define PS_FPGA_FILE "PS_FPGA.pst"
+
+static void get_ps_file_name(uint32_t devtype, uint32_t rom_version,
+ char *path)
+{
+ char *filename;
+
+ if (devtype == 0xdeadc0de)
+ filename = PS_ASIC_FILE;
+ else
+ filename = PS_FPGA_FILE;
+
+ snprintf(path, MAXPATHLEN, "%s%x/%s", FW_PATH, rom_version, filename);
+}
+
+#define PATCH_FILE "RamPatch.txt"
+#define FPGA_ROM_VERSION 0x99999999
+#define ROM_DEV_TYPE 0xdeadc0de
+
+static void get_patch_file_name(uint32_t dev_type, uint32_t rom_version,
+ uint32_t build_version, char *path)
+{
+ if (rom_version == FPGA_ROM_VERSION && dev_type != ROM_DEV_TYPE &&
+ dev_type != 0 && build_version == 1)
+ path[0] = '\0';
+ else
+ snprintf(path, MAXPATHLEN, "%s%x/%s",
+ FW_PATH, rom_version, PATCH_FILE);
+}
+
+#define VERIFY_CRC 9
+#define PS_REGION 1
+#define PATCH_REGION 2
+
+static int get_ath3k_crc(int dev)
+{
+ uint8_t cmd[7];
+ uint8_t *event;
+ int err;
+
+ load_hci_ps_hdr(cmd, VERIFY_CRC, 0, PS_REGION | PATCH_REGION);
+
+ err = send_hci_cmd_sync(dev, cmd, sizeof(cmd), &event);
+ if (err < 0)
+ return err;
+ /* Send error code if CRC check patched */
+ if (read_ps_event(event, HCI_PS_CMD_OCF) >= 0)
+ err = -EILSEQ;
+
+ if (!event)
+ free(event);
+
+ return err;
+}
+
+#define DEV_REGISTER 0x4FFC
+#define GET_DEV_TYPE_OCF 0x05
+
+static int get_device_type(int dev, uint32_t *code)
+{
+ uint8_t cmd[8];
+ uint8_t *event;
+ uint32_t reg;
+ int err;
+ uint8_t *ptr = cmd;
+ hci_command_hdr *ch = (void *)cmd;
+
+ ch->opcode = htobs(cmd_opcode_pack(HCI_VENDOR_CMD_OGF,
+ GET_DEV_TYPE_OCF));
+ ch->plen = 5;
+ ptr += HCI_COMMAND_HDR_SIZE;
+
+ ptr[0] = (uint8_t)DEV_REGISTER;
+ ptr[1] = (uint8_t)DEV_REGISTER >> 8;
+ ptr[2] = (uint8_t)DEV_REGISTER >> 16;
+ ptr[3] = (uint8_t)DEV_REGISTER >> 24;
+ ptr[4] = 0x04;
+
+ err = send_hci_cmd_sync(dev, cmd, sizeof(cmd), &event);
+ if (err < 0)
+ return err;
+
+ err = read_ps_event(event, GET_DEV_TYPE_OCF);
+ if (err < 0)
+ goto cleanup;
+
+ reg = event[10];
+ reg = (reg << 8) | event[9];
+ reg = (reg << 8) | event[8];
+ reg = (reg << 8) | event[7];
+ *code = reg;
+
+cleanup:
+ if (event)
+ free(event);
+
+ return err;
+}
+
+#define GET_VERSION_OCF 0x1E
+
+static int read_ath3k_version(int pConfig, uint32_t *rom_version,
+ uint32_t *build_version)
+{
+ uint8_t cmd[3];
+ uint8_t *event;
+ int err;
+ int status;
+ hci_command_hdr *ch = (void *)cmd;
+
+ ch->opcode = htobs(cmd_opcode_pack(HCI_VENDOR_CMD_OGF,
+ GET_VERSION_OCF));
+ ch->plen = 0;
+
+ err = send_hci_cmd_sync(pConfig, cmd, sizeof(cmd), &event);
+ if (err < 0)
+ return err;
+
+ err = read_ps_event(event, GET_VERSION_OCF);
+ if (err < 0)
+ goto cleanup;
+
+ status = event[10];
+ status = (status << 8) | event[9];
+ status = (status << 8) | event[8];
+ status = (status << 8) | event[7];
+ *rom_version = status;
+
+ status = event[14];
+ status = (status << 8) | event[13];
+ status = (status << 8) | event[12];
+ status = (status << 8) | event[11];
+ *build_version = status;
+
+cleanup:
+ if (event)
+ free(event);
+
+ return err;
+}
+
+static void convert_bdaddr(char *str_bdaddr, char *bdaddr)
+{
+ char bdbyte[3];
+ char *str_byte = str_bdaddr;
+ int i, j;
+ int colon_present = 0;
+
+ if (strstr(str_bdaddr, ":"))
+ colon_present = 1;
+
+ bdbyte[2] = '\0';
+
+ /* Reverse the BDADDR to LSB first */
+ for (i = 0, j = 5; i < 6; i++, j--) {
+ bdbyte[0] = str_byte[0];
+ bdbyte[1] = str_byte[1];
+ bdaddr[j] = strtol(bdbyte, NULL, 16);
+
+ if (colon_present == 1)
+ str_byte += 3;
+ else
+ str_byte += 2;
+ }
+}
+
+static int write_bdaddr(int pConfig, char *bdaddr)
+{
+ uint8_t *event;
+ int err;
+ uint8_t cmd[13];
+ uint8_t *ptr = cmd;
+ hci_command_hdr *ch = (void *)cmd;
+
+ memset(cmd, 0, sizeof(cmd));
+
+ ch->opcode = htobs(cmd_opcode_pack(HCI_VENDOR_CMD_OGF,
+ HCI_PS_CMD_OCF));
+ ch->plen = 10;
+ ptr += HCI_COMMAND_HDR_SIZE;
+
+ ptr[0] = 0x01;
+ ptr[1] = 0x01;
+ ptr[2] = 0x00;
+ ptr[3] = 0x06;
+
+ convert_bdaddr(bdaddr, (char *)&ptr[4]);
+
+ err = send_hci_cmd_sync(pConfig, cmd, sizeof(cmd), &event);
+ if (err < 0)
+ return err;
+
+ err = read_ps_event(event, HCI_PS_CMD_OCF);
+
+ if (event)
+ free(event);
+
+ return err;
+}
+
+#define BDADDR_FILE "ar3kbdaddr.pst"
+
+static void write_bdaddr_from_file(int rom_version, int fd)
+{
+ FILE *stream;
+ char bdaddr[PATH_MAX];
+ char bdaddr_file[PATH_MAX];
+
+ snprintf(bdaddr_file, MAXPATHLEN, "%s%x/%s",
+ FW_PATH, rom_version, BDADDR_FILE);
+
+ stream = fopen(bdaddr_file, "r");
+ if (!stream)
+ return;
+
+ if (fgets(bdaddr, PATH_MAX - 1, stream))
+ write_bdaddr(fd, bdaddr);
+
+ fclose(stream);
+}
+
+static int ath_ps_download(int fd)
+{
+ int err = 0;
+ int tag_count;
+ int patch_count = 0;
+ uint32_t rom_version = 0;
+ uint32_t build_version = 0;
+ uint32_t dev_type = 0;
+ char patch_file[PATH_MAX];
+ char ps_file[PATH_MAX];
+ FILE *stream;
+
+ /*
+ * Verfiy firmware version. depending on it select the PS
+ * config file to download.
+ */
+ if (get_device_type(fd, &dev_type) < 0) {
+ err = -EILSEQ;
+ goto download_cmplete;
+ }
+
+ if (read_ath3k_version(fd, &rom_version, &build_version) < 0) {
+ err = -EILSEQ;
+ goto download_cmplete;
+ }
+
+ /* Do not download configuration if CRC passes */
+ if (get_ath3k_crc(fd) < 0) {
+ err = 0;
+ goto download_cmplete;
+ }
+
+ get_ps_file_name(dev_type, rom_version, ps_file);
+ get_patch_file_name(dev_type, rom_version, build_version, patch_file);
+
+ stream = fopen(ps_file, "r");
+ if (!stream) {
+ perror("firmware file open error\n");
+ err = -EILSEQ;
+ goto download_cmplete;
+ }
+ tag_count = ath_parse_ps(stream);
+
+ fclose(stream);
+
+ if (tag_count < 0) {
+ err = -EILSEQ;
+ goto download_cmplete;
+ }
+
+ /*
+ * It is not necessary that Patch file be available,
+ * continue with PS Operations if patch file is not available.
+ */
+ if (patch_file[0] == '\0')
+ err = 0;
+
+ stream = fopen(patch_file, "r");
+ if (!stream)
+ err = 0;
+ else {
+ patch_count = ps_patch_download(fd, stream);
+ fclose(stream);
+
+ if (patch_count < 0) {
+ err = -EILSEQ;
+ goto download_cmplete;
+ }
+ }
+
+ err = ps_config_download(fd, tag_count);
+
+download_cmplete:
+ if (!err)
+ write_bdaddr_from_file(rom_version, fd);
+
+ return err;
+}
+
+#define HCI_SLEEP_CMD_OCF 0x04
+
+/*
+ * Atheros AR300x specific initialization post callback
+ */
+int ath3k_post(int fd, int pm)
+{
+ int dev_id, dd;
+ struct timespec tm = { 0, 50000 };
+
+ sleep(1);
+
+ dev_id = ioctl(fd, HCIUARTGETDEVICE, 0);
+ if (dev_id < 0) {
+ perror("cannot get device id");
+ return dev_id;
+ }
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ return dd;
+ }
+
+ if (ioctl(dd, HCIDEVUP, dev_id) < 0 && errno != EALREADY) {
+ perror("hci down:Power management Disabled");
+ hci_close_dev(dd);
+ return -1;
+ }
+
+ /* send vendor specific command with Sleep feature Enabled */
+ if (hci_send_cmd(dd, OGF_VENDOR_CMD, HCI_SLEEP_CMD_OCF, 1, &pm) < 0)
+ perror("PM command failed, power management Disabled");
+
+ nanosleep(&tm, NULL);
+ hci_close_dev(dd);
+
+ return 0;
+}
+
+#define HCI_VENDOR_CMD_OGF 0x3F
+#define HCI_PS_CMD_OCF 0x0B
+#define HCI_CHG_BAUD_CMD_OCF 0x0C
+
+#define WRITE_BDADDR_CMD_LEN 14
+#define WRITE_BAUD_CMD_LEN 6
+#define MAX_CMD_LEN WRITE_BDADDR_CMD_LEN
+
+static int set_cntrlr_baud(int fd, int speed)
+{
+ int baud;
+ struct timespec tm = { 0, 500000 };
+ unsigned char cmd[MAX_CMD_LEN], rsp[HCI_MAX_EVENT_SIZE];
+ unsigned char *ptr = cmd + 1;
+ hci_command_hdr *ch = (void *)ptr;
+
+ cmd[0] = HCI_COMMAND_PKT;
+
+ /* set controller baud rate to user specified value */
+ ptr = cmd + 1;
+ ch->opcode = htobs(cmd_opcode_pack(HCI_VENDOR_CMD_OGF,
+ HCI_CHG_BAUD_CMD_OCF));
+ ch->plen = 2;
+ ptr += HCI_COMMAND_HDR_SIZE;
+
+ baud = speed/100;
+ ptr[0] = (char)baud;
+ ptr[1] = (char)(baud >> 8);
+
+ if (write(fd, cmd, WRITE_BAUD_CMD_LEN) != WRITE_BAUD_CMD_LEN) {
+ perror("Failed to write change baud rate command");
+ return -ETIMEDOUT;
+ }
+
+ nanosleep(&tm, NULL);
+
+ if (read_hci_event(fd, rsp, sizeof(rsp)) < 0)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+/*
+ * Atheros AR300x specific initialization and configuration file
+ * download
+ */
+int ath3k_init(int fd, int speed, int init_speed, char *bdaddr,
+ struct termios *ti)
+{
+ int r;
+ int err = 0;
+ struct timespec tm = { 0, 500000 };
+ unsigned char cmd[MAX_CMD_LEN], rsp[HCI_MAX_EVENT_SIZE];
+ unsigned char *ptr = cmd + 1;
+ hci_command_hdr *ch = (void *)ptr;
+
+ cmd[0] = HCI_COMMAND_PKT;
+
+ /* set both controller and host baud rate to maximum possible value */
+ err = set_cntrlr_baud(fd, speed);
+ if (err < 0)
+ return err;
+
+ err = set_speed(fd, ti, speed);
+ if (err < 0) {
+ perror("Can't set required baud rate");
+ return err;
+ }
+
+ /* Download PS and patch */
+ r = ath_ps_download(fd);
+ if (r < 0) {
+ perror("Failed to Download configuration");
+ err = -ETIMEDOUT;
+ goto failed;
+ }
+
+ /* Write BDADDR */
+ if (bdaddr) {
+ ch->opcode = htobs(cmd_opcode_pack(HCI_VENDOR_CMD_OGF,
+ HCI_PS_CMD_OCF));
+ ch->plen = 10;
+ ptr += HCI_COMMAND_HDR_SIZE;
+
+ ptr[0] = 0x01;
+ ptr[1] = 0x01;
+ ptr[2] = 0x00;
+ ptr[3] = 0x06;
+ str2ba(bdaddr, (bdaddr_t *)(ptr + 4));
+
+ if (write(fd, cmd, WRITE_BDADDR_CMD_LEN) !=
+ WRITE_BDADDR_CMD_LEN) {
+ perror("Failed to write BD_ADDR command\n");
+ err = -ETIMEDOUT;
+ goto failed;
+ }
+
+ if (read_hci_event(fd, rsp, sizeof(rsp)) < 0) {
+ perror("Failed to set BD_ADDR\n");
+ err = -ETIMEDOUT;
+ goto failed;
+ }
+ }
+
+ /* Send HCI Reset */
+ cmd[1] = 0x03;
+ cmd[2] = 0x0C;
+ cmd[3] = 0x00;
+
+ r = write(fd, cmd, 4);
+ if (r != 4) {
+ err = -ETIMEDOUT;
+ goto failed;
+ }
+
+ nanosleep(&tm, NULL);
+ if (read_hci_event(fd, rsp, sizeof(rsp)) < 0) {
+ err = -ETIMEDOUT;
+ goto failed;
+ }
+
+ err = set_cntrlr_baud(fd, speed);
+ if (err < 0)
+ return err;
+
+failed:
+ if (err < 0) {
+ set_cntrlr_baud(fd, init_speed);
+ set_speed(fd, ti, init_speed);
+ }
+
+ return err;
+}
diff --git a/tools/hciattach_qualcomm.c b/tools/hciattach_qualcomm.c
new file mode 100644
index 0000000..e4ff5e3
--- /dev/null
+++ b/tools/hciattach_qualcomm.c
@@ -0,0 +1,275 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2005-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ *
+ * 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 <signal.h>
+#include <syslog.h>
+#include <termios.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "hciattach.h"
+
+#define FAILIF(x, args...) do { \
+ if (x) { \
+ fprintf(stderr, ##args); \
+ return -1; \
+ } \
+} while (0)
+
+typedef struct {
+ uint8_t uart_prefix;
+ hci_event_hdr hci_hdr;
+ evt_cmd_complete cmd_complete;
+ uint8_t status;
+ uint8_t data[16];
+} __attribute__((packed)) command_complete_t;
+
+static int read_command_complete(int fd,
+ unsigned short opcode,
+ unsigned char len)
+{
+ command_complete_t resp;
+ unsigned char vsevent[512];
+ int n;
+
+ /* Read reply. */
+ n = read_hci_event(fd, vsevent, sizeof(vsevent));
+ FAILIF(n < 0, "Failed to read response");
+
+ FAILIF(vsevent[1] != 0xFF, "Failed to read response");
+
+ n = read_hci_event(fd, (unsigned char *)&resp, sizeof(resp));
+ FAILIF(n < 0, "Failed to read response");
+
+ /* event must be event-complete */
+ FAILIF(resp.hci_hdr.evt != EVT_CMD_COMPLETE,
+ "Error in response: not a cmd-complete event, "
+ "but 0x%02x!\n", resp.hci_hdr.evt);
+
+ FAILIF(resp.hci_hdr.plen < 4, /* plen >= 4 for EVT_CMD_COMPLETE */
+ "Error in response: plen is not >= 4, but 0x%02x!\n",
+ resp.hci_hdr.plen);
+
+ /* cmd-complete event: opcode */
+ FAILIF(resp.cmd_complete.opcode != 0,
+ "Error in response: opcode is 0x%04x, not 0!",
+ resp.cmd_complete.opcode);
+
+ return resp.status == 0 ? 0 : -1;
+}
+
+static int qualcomm_load_firmware(int fd, const char *firmware, const char *bdaddr_s)
+{
+
+ int fw = open(firmware, O_RDONLY);
+
+ fprintf(stdout, "Opening firmware file: %s\n", firmware);
+
+ FAILIF(fw < 0,
+ "Could not open firmware file %s: %s (%d).\n",
+ firmware, strerror(errno), errno);
+
+ fprintf(stdout, "Uploading firmware...\n");
+ do {
+ /* Read each command and wait for a response. */
+ unsigned char data[1024];
+ unsigned char cmdp[1 + sizeof(hci_command_hdr)];
+ hci_command_hdr *cmd = (hci_command_hdr *) (cmdp + 1);
+ int nr;
+
+ nr = read(fw, cmdp, sizeof(cmdp));
+ if (!nr)
+ break;
+
+ FAILIF(nr != sizeof(cmdp),
+ "Could not read H4 + HCI header!\n");
+ FAILIF(*cmdp != HCI_COMMAND_PKT,
+ "Command is not an H4 command packet!\n");
+
+ FAILIF(read(fw, data, cmd->plen) != cmd->plen,
+ "Could not read %d bytes of data \
+ for command with opcode %04x!\n",
+ cmd->plen, cmd->opcode);
+
+ if ((data[0] == 1) && (data[1] == 2) && (data[2] == 6)) {
+ bdaddr_t bdaddr;
+ if (bdaddr_s != NULL) {
+ str2ba(bdaddr_s, &bdaddr);
+ memcpy(&data[3], &bdaddr, sizeof(bdaddr_t));
+ }
+ }
+
+ {
+ int nw;
+ struct iovec iov_cmd[2];
+ iov_cmd[0].iov_base = cmdp;
+ iov_cmd[0].iov_len = sizeof(cmdp);
+ iov_cmd[1].iov_base = data;
+ iov_cmd[1].iov_len = cmd->plen;
+ nw = writev(fd, iov_cmd, 2);
+ FAILIF(nw != (int) sizeof(cmdp) + cmd->plen,
+ "Could not send entire command \
+ (sent only %d bytes)!\n",
+ nw);
+ }
+
+ /* Wait for response */
+ if (read_command_complete(fd, cmd->opcode, cmd->plen) < 0)
+ return -1;
+ } while (1);
+ fprintf(stdout, "Firmware upload successful.\n");
+
+ close(fw);
+
+ return 0;
+}
+
+int qualcomm_init(int fd, int speed, struct termios *ti, const char *bdaddr)
+{
+ struct timespec tm = {0, 50000};
+ char cmd[5];
+ unsigned char resp[100]; /* Response */
+ char fw[100];
+ int n;
+
+ memset(resp, 0, 100);
+
+ /* Get Manufacturer and LMP version */
+ cmd[0] = HCI_COMMAND_PKT;
+ cmd[1] = 0x01;
+ cmd[2] = 0x10;
+ cmd[3] = 0x00;
+
+ do {
+ n = write(fd, cmd, 4);
+ if (n < 4) {
+ perror("Failed to write init command");
+ return -1;
+ }
+
+ /* Read reply. */
+ if (read_hci_event(fd, resp, 100) < 0) {
+ perror("Failed to read init response");
+ return -1;
+ }
+
+ /* Wait for command complete event for our Opcode */
+ } while (resp[4] != cmd[1] && resp[5] != cmd[2]);
+
+ /* Verify manufacturer */
+ if ((resp[11] & 0xFF) != 0x1d)
+ fprintf(stderr,
+ "WARNING : module's manufacturer is not Qualcomm\n");
+
+ /* Print LMP version */
+ fprintf(stderr,
+ "Qualcomm module LMP version : 0x%02x\n", resp[10] & 0xFF);
+
+ /* Print LMP subversion */
+ {
+ unsigned short lmp_subv = resp[13] | (resp[14] << 8);
+
+ fprintf(stderr, "Qualcomm module LMP sub-version : 0x%04x\n",
+ lmp_subv);
+ }
+
+ /* Get SoC type */
+ cmd[0] = HCI_COMMAND_PKT;
+ cmd[1] = 0x00;
+ cmd[2] = 0xFC;
+ cmd[3] = 0x01;
+ cmd[4] = 0x06;
+
+ do {
+ n = write(fd, cmd, 5);
+ if (n < 5) {
+ perror("Failed to write vendor init command");
+ return -1;
+ }
+
+ /* Read reply. */
+ if ((n = read_hci_event(fd, resp, 100)) < 0) {
+ perror("Failed to read vendor init response");
+ return -1;
+ }
+
+ } while (resp[3] != 0 && resp[4] != 2);
+
+ snprintf(fw, sizeof(fw), "/etc/firmware/%c%c%c%c%c%c_%c%c%c%c.bin",
+ resp[18], resp[19], resp[20], resp[21],
+ resp[22], resp[23],
+ resp[32], resp[33], resp[34], resp[35]);
+
+ /* Wait for command complete event for our Opcode */
+ if (read_hci_event(fd, resp, 100) < 0) {
+ perror("Failed to read init response");
+ return -1;
+ }
+
+ qualcomm_load_firmware(fd, fw, bdaddr);
+
+ /* Reset */
+ cmd[0] = HCI_COMMAND_PKT;
+ cmd[1] = 0x03;
+ cmd[2] = 0x0C;
+ cmd[3] = 0x00;
+
+ do {
+ n = write(fd, cmd, 4);
+ if (n < 4) {
+ perror("Failed to write reset command");
+ return -1;
+ }
+
+ /* Read reply. */
+ if ((n = read_hci_event(fd, resp, 100)) < 0) {
+ perror("Failed to read reset response");
+ return -1;
+ }
+
+ } while (resp[4] != cmd[1] && resp[5] != cmd[2]);
+
+ nanosleep(&tm, NULL);
+
+ return 0;
+}
diff --git a/tools/hciattach_st.c b/tools/hciattach_st.c
new file mode 100644
index 0000000..dbb7c47
--- /dev/null
+++ b/tools/hciattach_st.c
@@ -0,0 +1,278 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2005-2010 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 <stdint.h>
+#include <string.h>
+#include <dirent.h>
+#include <sys/param.h>
+
+#include <bluetooth/bluetooth.h>
+
+#include "hciattach.h"
+
+static int debug = 0;
+
+static int do_command(int fd, uint8_t ogf, uint16_t ocf,
+ uint8_t *cparam, int clen, uint8_t *rparam, int rlen)
+{
+ //uint16_t opcode = (uint16_t) ((ocf & 0x03ff) | (ogf << 10));
+ unsigned char cp[260], rp[260];
+ int len, size, offset = 3;
+
+ cp[0] = 0x01;
+ cp[1] = ocf & 0xff;
+ cp[2] = ogf << 2 | ocf >> 8;
+ cp[3] = clen;
+
+ if (clen > 0)
+ memcpy(cp + 4, cparam, clen);
+
+ if (debug) {
+ int i;
+ printf("[<");
+ for (i = 0; i < clen + 4; i++)
+ printf(" %02x", cp[i]);
+ printf("]\n");
+ }
+
+ if (write(fd, cp, clen + 4) < 0)
+ return -1;
+
+ do {
+ if (read(fd, rp, 1) < 1)
+ return -1;
+ } while (rp[0] != 0x04);
+
+ if (read(fd, rp + 1, 2) < 2)
+ return -1;
+
+ do {
+ len = read(fd, rp + offset, sizeof(rp) - offset);
+ offset += len;
+ } while (offset < rp[2] + 3);
+
+ if (debug) {
+ int i;
+ printf("[>");
+ for (i = 0; i < offset; i++)
+ printf(" %02x", rp[i]);
+ printf("]\n");
+ }
+
+ if (rp[0] != 0x04) {
+ errno = EIO;
+ return -1;
+ }
+
+ switch (rp[1]) {
+ case 0x0e: /* command complete */
+ if (rp[6] != 0x00)
+ return -ENXIO;
+ offset = 3 + 4;
+ size = rp[2] - 4;
+ break;
+ case 0x0f: /* command status */
+ /* fall through */
+ default:
+ offset = 3;
+ size = rp[2];
+ break;
+ }
+
+ if (!rparam || rlen < size)
+ return -ENXIO;
+
+ memcpy(rparam, rp + offset, size);
+
+ return size;
+}
+
+static int load_file(int dd, uint16_t version, const char *suffix)
+{
+ DIR *dir;
+ struct dirent *d;
+ char pathname[PATH_MAX], filename[NAME_MAX], prefix[20];
+ unsigned char cmd[256];
+ unsigned char buf[256];
+ uint8_t seqnum = 0;
+ int fd, size, len, found_fw_file;
+
+ memset(filename, 0, sizeof(filename));
+
+ snprintf(prefix, sizeof(prefix), "STLC2500_R%d_%02d_",
+ version >> 8, version & 0xff);
+
+ strcpy(pathname, "/lib/firmware");
+ dir = opendir(pathname);
+ if (!dir) {
+ strcpy(pathname, ".");
+ dir = opendir(pathname);
+ if (!dir)
+ return -errno;
+ }
+
+ found_fw_file = 0;
+ while (1) {
+ d = readdir(dir);
+ if (!d)
+ break;
+
+ if (strncmp(d->d_name + strlen(d->d_name) - strlen(suffix),
+ suffix, strlen(suffix)))
+ continue;
+
+ if (strncmp(d->d_name, prefix, strlen(prefix)))
+ continue;
+
+ snprintf(filename, sizeof(filename), "%s/%s",
+ pathname, d->d_name);
+ found_fw_file = 1;
+ }
+
+ closedir(dir);
+
+ if (!found_fw_file)
+ return -ENOENT;
+
+ printf("Loading file %s\n", filename);
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ perror("Can't open firmware file");
+ return -errno;
+ }
+
+ while (1) {
+ size = read(fd, cmd + 1, 254);
+ if (size <= 0)
+ break;
+
+ cmd[0] = seqnum;
+
+ len = do_command(dd, 0xff, 0x002e, cmd, size + 1, buf, sizeof(buf));
+ if (len < 1)
+ break;
+
+ if (buf[0] != seqnum) {
+ fprintf(stderr, "Sequence number mismatch\n");
+ break;
+ }
+
+ seqnum++;
+ }
+
+ close(fd);
+
+ return 0;
+}
+
+int stlc2500_init(int dd, bdaddr_t *bdaddr)
+{
+ unsigned char cmd[16];
+ unsigned char buf[254];
+ uint16_t version;
+ int len;
+ int err;
+
+ /* Hci_Cmd_Ericsson_Read_Revision_Information */
+ len = do_command(dd, 0xff, 0x000f, NULL, 0, buf, sizeof(buf));
+ if (len < 0)
+ return -1;
+
+ printf("%s\n", buf);
+
+ /* HCI_Read_Local_Version_Information */
+ len = do_command(dd, 0x04, 0x0001, NULL, 0, buf, sizeof(buf));
+ if (len < 0)
+ return -1;
+
+ version = buf[2] << 8 | buf[1];
+
+ err = load_file(dd, version, ".ptc");
+ if (err < 0) {
+ if (err == -ENOENT)
+ fprintf(stderr, "No ROM patch file loaded.\n");
+ else
+ return -1;
+ }
+
+ err = load_file(dd, buf[2] << 8 | buf[1], ".ssf");
+ if (err < 0) {
+ if (err == -ENOENT)
+ fprintf(stderr, "No static settings file loaded.\n");
+ else
+ return -1;
+ }
+
+ cmd[0] = 0xfe;
+ cmd[1] = 0x06;
+ bacpy((bdaddr_t *) (cmd + 2), bdaddr);
+
+ /* Hci_Cmd_ST_Store_In_NVDS */
+ len = do_command(dd, 0xff, 0x0022, cmd, 8, buf, sizeof(buf));
+ if (len < 0)
+ return -1;
+
+ /* HCI_Reset : applies parameters*/
+ len = do_command(dd, 0x03, 0x0003, NULL, 0, buf, sizeof(buf));
+ if (len < 0)
+ return -1;
+
+ return 0;
+}
+
+int bgb2xx_init(int dd, bdaddr_t *bdaddr)
+{
+ unsigned char cmd[16];
+ unsigned char buf[254];
+ int len;
+
+ len = do_command(dd, 0xff, 0x000f, NULL, 0, buf, sizeof(buf));
+ if (len < 0)
+ return -1;
+
+ printf("%s\n", buf);
+
+ cmd[0] = 0xfe;
+ cmd[1] = 0x06;
+ bacpy((bdaddr_t *) (cmd + 2), bdaddr);
+
+ len = do_command(dd, 0xff, 0x0022, cmd, 8, buf, sizeof(buf));
+ if (len < 0)
+ return -1;
+
+ len = do_command(dd, 0x03, 0x0003, NULL, 0, buf, sizeof(buf));
+ if (len < 0)
+ return -1;
+
+ return 0;
+}
diff --git a/tools/hciattach_ti.c b/tools/hciattach_ti.c
new file mode 100644
index 0000000..e107a65
--- /dev/null
+++ b/tools/hciattach_ti.c
@@ -0,0 +1,529 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2007-2008 Texas Instruments, Inc.
+ * Copyright (C) 2005-2010 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 <termios.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "hciattach.h"
+
+#ifdef HCIATTACH_DEBUG
+#define DPRINTF(x...) printf(x)
+#else
+#define DPRINTF(x...)
+#endif
+
+#define HCIUARTGETDEVICE _IOR('U', 202, int)
+
+#define MAKEWORD(a, b) ((uint16_t)(((uint8_t)(a)) | ((uint16_t)((uint8_t)(b))) << 8))
+
+#define TI_MANUFACTURER_ID 13
+
+#define FIRMWARE_DIRECTORY "/lib/firmware/"
+
+#define ACTION_SEND_COMMAND 1
+#define ACTION_WAIT_EVENT 2
+#define ACTION_SERIAL 3
+#define ACTION_DELAY 4
+#define ACTION_RUN_SCRIPT 5
+#define ACTION_REMARKS 6
+
+#define BRF_DEEP_SLEEP_OPCODE_BYTE_1 0x0c
+#define BRF_DEEP_SLEEP_OPCODE_BYTE_2 0xfd
+#define BRF_DEEP_SLEEP_OPCODE \
+ (BRF_DEEP_SLEEP_OPCODE_BYTE_1 | (BRF_DEEP_SLEEP_OPCODE_BYTE_2 << 8))
+
+#define FILE_HEADER_MAGIC 0x42535442
+
+/*
+ * BRF Firmware header
+ */
+struct bts_header {
+ uint32_t magic;
+ uint32_t version;
+ uint8_t future[24];
+ uint8_t actions[0];
+}__attribute__ ((packed));
+
+/*
+ * BRF Actions structure
+ */
+struct bts_action {
+ uint16_t type;
+ uint16_t size;
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+struct bts_action_send {
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+struct bts_action_wait {
+ uint32_t msec;
+ uint32_t size;
+ uint8_t data[0];
+}__attribute__ ((packed));
+
+struct bts_action_delay {
+ uint32_t msec;
+}__attribute__ ((packed));
+
+struct bts_action_serial {
+ uint32_t baud;
+ uint32_t flow_control;
+}__attribute__ ((packed));
+
+static FILE *bts_load_script(const char* file_name, uint32_t* version)
+{
+ struct bts_header header;
+ FILE* fp;
+
+ fp = fopen(file_name, "rb");
+ if (!fp) {
+ perror("can't open firmware file");
+ goto out;
+ }
+
+ if (1 != fread(&header, sizeof(struct bts_header), 1, fp)) {
+ perror("can't read firmware file");
+ goto errclose;
+ }
+
+ if (header.magic != FILE_HEADER_MAGIC) {
+ fprintf(stderr, "%s not a legal TI firmware file\n", file_name);
+ goto errclose;
+ }
+
+ if (NULL != version)
+ *version = header.version;
+
+ goto out;
+
+errclose:
+ fclose(fp);
+ fp = NULL;
+out:
+ return fp;
+}
+
+static unsigned long bts_fetch_action(FILE* fp, unsigned char* action_buf,
+ unsigned long buf_size, uint16_t* action_type)
+{
+ struct bts_action action_hdr;
+ unsigned long nread;
+
+ if (!fp)
+ return 0;
+
+ if (1 != fread(&action_hdr, sizeof(struct bts_action), 1, fp))
+ return 0;
+
+ if (action_hdr.size > buf_size) {
+ fprintf(stderr, "bts_next_action: not enough space to read next action\n");
+ return 0;
+ }
+
+ nread = fread(action_buf, sizeof(uint8_t), action_hdr.size, fp);
+ if (nread != (action_hdr.size)) {
+ fprintf(stderr, "bts_next_action: fread failed to read next action\n");
+ return 0;
+ }
+
+ *action_type = action_hdr.type;
+
+ return nread * sizeof(uint8_t);
+}
+
+static void bts_unload_script(FILE* fp)
+{
+ if (fp)
+ fclose(fp);
+}
+
+static int is_it_texas(const uint8_t *respond)
+{
+ uint16_t manufacturer_id;
+
+ manufacturer_id = MAKEWORD(respond[11], respond[12]);
+
+ return TI_MANUFACTURER_ID == manufacturer_id ? 1 : 0;
+}
+
+static const char *get_firmware_name(const uint8_t *respond)
+{
+ static char firmware_file_name[PATH_MAX] = {0};
+ uint16_t version = 0, chip = 0, min_ver = 0, maj_ver = 0;
+
+ version = MAKEWORD(respond[13], respond[14]);
+ chip = (version & 0x7C00) >> 10;
+ min_ver = (version & 0x007F);
+ maj_ver = (version & 0x0380) >> 7;
+
+ if (version & 0x8000)
+ maj_ver |= 0x0008;
+
+ sprintf(firmware_file_name, FIRMWARE_DIRECTORY "TIInit_%d.%d.%d.bts", chip, maj_ver, min_ver);
+
+ return firmware_file_name;
+}
+
+static void brf_delay(struct bts_action_delay *delay)
+{
+ usleep(1000 * delay->msec);
+}
+
+static int brf_set_serial_params(struct bts_action_serial *serial_action,
+ int fd, struct termios *ti)
+{
+ fprintf(stderr, "texas: changing baud rate to %u, flow control to %u\n",
+ serial_action->baud, serial_action->flow_control );
+ tcflush(fd, TCIOFLUSH);
+
+ if (serial_action->flow_control)
+ ti->c_cflag |= CRTSCTS;
+ else
+ ti->c_cflag &= ~CRTSCTS;
+
+ if (tcsetattr(fd, TCSANOW, ti) < 0) {
+ perror("Can't set port settings");
+ return -1;
+ }
+
+ tcflush(fd, TCIOFLUSH);
+
+ if (set_speed(fd, ti, serial_action->baud) < 0) {
+ perror("Can't set baud rate");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int brf_send_command_socket(int fd, struct bts_action_send* send_action)
+{
+ char response[1024] = {0};
+ hci_command_hdr *cmd = (hci_command_hdr *) send_action->data;
+ uint16_t opcode = cmd->opcode;
+
+ struct hci_request rq;
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = cmd_opcode_ogf(opcode);
+ rq.ocf = cmd_opcode_ocf(opcode);
+ rq.event = EVT_CMD_COMPLETE;
+ rq.cparam = &send_action->data[3];
+ rq.clen = send_action->data[2];
+ rq.rparam = response;
+ rq.rlen = sizeof(response);
+
+ if (hci_send_req(fd, &rq, 15) < 0) {
+ perror("Cannot send hci command to socket");
+ return -1;
+ }
+
+ /* verify success */
+ if (response[0]) {
+ errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int brf_send_command_file(int fd, struct bts_action_send* send_action, long size)
+{
+ unsigned char response[1024] = {0};
+ long ret = 0;
+
+ /* send command */
+ if (size != write(fd, send_action, size)) {
+ perror("Texas: Failed to write action command");
+ return -1;
+ }
+
+ /* read response */
+ ret = read_hci_event(fd, response, sizeof(response));
+ if (ret < 0) {
+ perror("texas: failed to read command response");
+ return -1;
+ }
+
+ /* verify success */
+ if (ret < 7 || 0 != response[6]) {
+ fprintf( stderr, "TI init command failed.\n" );
+ errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int brf_send_command(int fd, struct bts_action_send* send_action, long size, int hcill_installed)
+{
+ int ret = 0;
+ char *fixed_action;
+
+ /* remove packet type when giving to socket API */
+ if (hcill_installed) {
+ fixed_action = ((char *) send_action) + 1;
+ ret = brf_send_command_socket(fd, (struct bts_action_send *) fixed_action);
+ } else {
+ ret = brf_send_command_file(fd, send_action, size);
+ }
+
+ return ret;
+}
+
+static int brf_do_action(uint16_t brf_type, uint8_t *brf_action, long brf_size,
+ int fd, struct termios *ti, int hcill_installed)
+{
+ int ret = 0;
+
+ switch (brf_type) {
+ case ACTION_SEND_COMMAND:
+ DPRINTF("W");
+ ret = brf_send_command(fd, (struct bts_action_send*) brf_action, brf_size, hcill_installed);
+ break;
+ case ACTION_WAIT_EVENT:
+ DPRINTF("R");
+ break;
+ case ACTION_SERIAL:
+ DPRINTF("S");
+ ret = brf_set_serial_params((struct bts_action_serial *) brf_action, fd, ti);
+ break;
+ case ACTION_DELAY:
+ DPRINTF("D");
+ brf_delay((struct bts_action_delay *) brf_action);
+ break;
+ case ACTION_REMARKS:
+ DPRINTF("C");
+ break;
+ default:
+ fprintf(stderr, "brf_init: unknown firmware action type (%d)\n", brf_type);
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * tests whether a given brf action is a HCI_VS_Sleep_Mode_Configurations cmd
+ */
+static int brf_action_is_deep_sleep(uint8_t *brf_action, long brf_size,
+ uint16_t brf_type)
+{
+ uint16_t opcode;
+
+ if (brf_type != ACTION_SEND_COMMAND)
+ return 0;
+
+ if (brf_size < 3)
+ return 0;
+
+ if (brf_action[0] != HCI_COMMAND_PKT)
+ return 0;
+
+ /* HCI data is little endian */
+ opcode = brf_action[1] | (brf_action[2] << 8);
+
+ if (opcode != BRF_DEEP_SLEEP_OPCODE)
+ return 0;
+
+ /* action is deep sleep configuration command ! */
+ return 1;
+}
+
+/*
+ * This function is called twice.
+ * The first time it is called, it loads the brf script, and executes its
+ * commands until it reaches a deep sleep command (or its end).
+ * The second time it is called, it assumes HCILL protocol is set up,
+ * and sends rest of brf script via the supplied socket.
+ */
+static int brf_do_script(int fd, struct termios *ti, const char *bts_file)
+{
+ int ret = 0, hcill_installed = bts_file ? 0 : 1;
+ uint32_t vers;
+ static FILE *brf_script_file = NULL;
+ static uint8_t brf_action[512];
+ static long brf_size;
+ static uint16_t brf_type;
+
+ /* is it the first time we are called ? */
+ if (0 == hcill_installed) {
+ DPRINTF("Sending script to serial device\n");
+ brf_script_file = bts_load_script(bts_file, &vers );
+ if (!brf_script_file) {
+ fprintf(stderr, "Warning: cannot find BTS file: %s\n",
+ bts_file);
+ return 0;
+ }
+
+ fprintf( stderr, "Loaded BTS script version %u\n", vers );
+
+ brf_size = bts_fetch_action(brf_script_file, brf_action,
+ sizeof(brf_action), &brf_type);
+ if (brf_size == 0) {
+ fprintf(stderr, "Warning: BTS file is empty !");
+ return 0;
+ }
+ }
+ else {
+ DPRINTF("Sending script to bluetooth socket\n");
+ }
+
+ /* execute current action and continue to parse brf script file */
+ while (brf_size != 0) {
+ ret = brf_do_action(brf_type, brf_action, brf_size,
+ fd, ti, hcill_installed);
+ if (ret == -1)
+ break;
+
+ brf_size = bts_fetch_action(brf_script_file, brf_action,
+ sizeof(brf_action), &brf_type);
+
+ /* if this is the first time we run (no HCILL yet) */
+ /* and a deep sleep command is encountered */
+ /* we exit */
+ if (!hcill_installed &&
+ brf_action_is_deep_sleep(brf_action,
+ brf_size, brf_type))
+ return 0;
+ }
+
+ bts_unload_script(brf_script_file);
+ brf_script_file = NULL;
+ DPRINTF("\n");
+
+ return ret;
+}
+
+int texas_init(int fd, struct termios *ti)
+{
+ struct timespec tm = {0, 50000};
+ char cmd[4];
+ unsigned char resp[100]; /* Response */
+ const char *bts_file;
+ int n;
+
+ memset(resp,'\0', 100);
+
+ /* It is possible to get software version with manufacturer specific
+ HCI command HCI_VS_TI_Version_Number. But the only thing you get more
+ is if this is point-to-point or point-to-multipoint module */
+
+ /* Get Manufacturer and LMP version */
+ cmd[0] = HCI_COMMAND_PKT;
+ cmd[1] = 0x01;
+ cmd[2] = 0x10;
+ cmd[3] = 0x00;
+
+ do {
+ n = write(fd, cmd, 4);
+ if (n < 0) {
+ perror("Failed to write init command (READ_LOCAL_VERSION_INFORMATION)");
+ return -1;
+ }
+ if (n < 4) {
+ fprintf(stderr, "Wanted to write 4 bytes, could only write %d. Stop\n", n);
+ return -1;
+ }
+
+ /* Read reply. */
+ if (read_hci_event(fd, resp, 100) < 0) {
+ perror("Failed to read init response (READ_LOCAL_VERSION_INFORMATION)");
+ return -1;
+ }
+
+ /* Wait for command complete event for our Opcode */
+ } while (resp[4] != cmd[1] && resp[5] != cmd[2]);
+
+ /* Verify manufacturer */
+ if (! is_it_texas(resp)) {
+ fprintf(stderr,"ERROR: module's manufacturer is not Texas Instruments\n");
+ return -1;
+ }
+
+ fprintf(stderr, "Found a Texas Instruments' chip!\n");
+
+ bts_file = get_firmware_name(resp);
+ fprintf(stderr, "Firmware file : %s\n", bts_file);
+
+ n = brf_do_script(fd, ti, bts_file);
+
+ nanosleep(&tm, NULL);
+
+ return n;
+}
+
+int texas_post(int fd, struct termios *ti)
+{
+ int dev_id, dd, ret = 0;
+
+ sleep(1);
+
+ dev_id = ioctl(fd, HCIUARTGETDEVICE, 0);
+ if (dev_id < 0) {
+ perror("cannot get device id");
+ return -1;
+ }
+
+ DPRINTF("\nAdded device hci%d\n", dev_id);
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ return -1;
+ }
+
+ if (ioctl(dd, HCIDEVUP, dev_id) < 0 && errno != EALREADY) {
+ fprintf(stderr, "Can't init device hci%d: %s (%d)", dev_id,
+ strerror(errno), errno);
+ hci_close_dev(dd);
+ return -1;
+ }
+
+ ret = brf_do_script(dd, ti, NULL);
+
+ hci_close_dev(dd);
+
+ return ret;
+}
diff --git a/tools/hciattach_tialt.c b/tools/hciattach_tialt.c
new file mode 100644
index 0000000..1ba009c
--- /dev/null
+++ b/tools/hciattach_tialt.c
@@ -0,0 +1,244 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2005-2010 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 <signal.h>
+#include <syslog.h>
+#include <termios.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "hciattach.h"
+
+#define FAILIF(x, args...) do { \
+ if (x) { \
+ fprintf(stderr, ##args); \
+ return -1; \
+ } \
+} while(0)
+
+typedef struct {
+ uint8_t uart_prefix;
+ hci_event_hdr hci_hdr;
+ evt_cmd_complete cmd_complete;
+ uint8_t status;
+ uint8_t data[16];
+} __attribute__((packed)) command_complete_t;
+
+static int read_command_complete(int fd, unsigned short opcode, unsigned char len) {
+ command_complete_t resp;
+ /* Read reply. */
+ FAILIF(read_hci_event(fd, (unsigned char *)&resp, sizeof(resp)) < 0,
+ "Failed to read response");
+
+ /* Parse speed-change reply */
+ FAILIF(resp.uart_prefix != HCI_EVENT_PKT,
+ "Error in response: not an event packet, but 0x%02x!\n",
+ resp.uart_prefix);
+
+ FAILIF(resp.hci_hdr.evt != EVT_CMD_COMPLETE, /* event must be event-complete */
+ "Error in response: not a cmd-complete event, "
+ "but 0x%02x!\n", resp.hci_hdr.evt);
+
+ FAILIF(resp.hci_hdr.plen < 4, /* plen >= 4 for EVT_CMD_COMPLETE */
+ "Error in response: plen is not >= 4, but 0x%02x!\n",
+ resp.hci_hdr.plen);
+
+ /* cmd-complete event: opcode */
+ FAILIF(resp.cmd_complete.opcode != (uint16_t)opcode,
+ "Error in response: opcode is 0x%04x, not 0x%04x!",
+ resp.cmd_complete.opcode, opcode);
+
+ return resp.status == 0 ? 0 : -1;
+}
+
+typedef struct {
+ uint8_t uart_prefix;
+ hci_command_hdr hci_hdr;
+ uint32_t speed;
+} __attribute__((packed)) texas_speed_change_cmd_t;
+
+static int texas_change_speed(int fd, uint32_t speed)
+{
+ return 0;
+}
+
+static int texas_load_firmware(int fd, const char *firmware) {
+
+ int fw = open(firmware, O_RDONLY);
+
+ fprintf(stdout, "Opening firmware file: %s\n", firmware);
+
+ FAILIF(fw < 0,
+ "Could not open firmware file %s: %s (%d).\n",
+ firmware, strerror(errno), errno);
+
+ fprintf(stdout, "Uploading firmware...\n");
+ do {
+ /* Read each command and wait for a response. */
+ unsigned char data[1024];
+ unsigned char cmdp[1 + sizeof(hci_command_hdr)];
+ hci_command_hdr *cmd = (hci_command_hdr *)(cmdp + 1);
+ int nr;
+ nr = read(fw, cmdp, sizeof(cmdp));
+ if (!nr)
+ break;
+ FAILIF(nr != sizeof(cmdp), "Could not read H4 + HCI header!\n");
+ FAILIF(*cmdp != HCI_COMMAND_PKT, "Command is not an H4 command packet!\n");
+
+ FAILIF(read(fw, data, cmd->plen) != cmd->plen,
+ "Could not read %d bytes of data for command with opcode %04x!\n",
+ cmd->plen,
+ cmd->opcode);
+
+ {
+ int nw;
+#if 0
+ fprintf(stdout, "\topcode 0x%04x (%d bytes of data).\n",
+ cmd->opcode,
+ cmd->plen);
+#endif
+ struct iovec iov_cmd[2];
+ iov_cmd[0].iov_base = cmdp;
+ iov_cmd[0].iov_len = sizeof(cmdp);
+ iov_cmd[1].iov_base = data;
+ iov_cmd[1].iov_len = cmd->plen;
+ nw = writev(fd, iov_cmd, 2);
+ FAILIF(nw != (int) sizeof(cmd) + cmd->plen,
+ "Could not send entire command (sent only %d bytes)!\n",
+ nw);
+ }
+
+ /* Wait for response */
+ if (read_command_complete(fd,
+ cmd->opcode,
+ cmd->plen) < 0) {
+ return -1;
+ }
+
+ } while(1);
+ fprintf(stdout, "Firmware upload successful.\n");
+
+ close(fw);
+ return 0;
+}
+
+int texasalt_init(int fd, int speed, struct termios *ti)
+{
+ struct timespec tm = {0, 50000};
+ char cmd[4];
+ unsigned char resp[100]; /* Response */
+ int n;
+
+ memset(resp,'\0', 100);
+
+ /* It is possible to get software version with manufacturer specific
+ HCI command HCI_VS_TI_Version_Number. But the only thing you get more
+ is if this is point-to-point or point-to-multipoint module */
+
+ /* Get Manufacturer and LMP version */
+ cmd[0] = HCI_COMMAND_PKT;
+ cmd[1] = 0x01;
+ cmd[2] = 0x10;
+ cmd[3] = 0x00;
+
+ do {
+ n = write(fd, cmd, 4);
+ if (n < 0) {
+ perror("Failed to write init command (READ_LOCAL_VERSION_INFORMATION)");
+ return -1;
+ }
+ if (n < 4) {
+ fprintf(stderr, "Wanted to write 4 bytes, could only write %d. Stop\n", n);
+ return -1;
+ }
+
+ /* Read reply. */
+ if (read_hci_event(fd, resp, 100) < 0) {
+ perror("Failed to read init response (READ_LOCAL_VERSION_INFORMATION)");
+ return -1;
+ }
+
+ /* Wait for command complete event for our Opcode */
+ } while (resp[4] != cmd[1] && resp[5] != cmd[2]);
+
+ /* Verify manufacturer */
+ if ((resp[11] & 0xFF) != 0x0d)
+ fprintf(stderr,"WARNING : module's manufacturer is not Texas Instrument\n");
+
+ /* Print LMP version */
+ fprintf(stderr, "Texas module LMP version : 0x%02x\n", resp[10] & 0xFF);
+
+ /* Print LMP subversion */
+ {
+ unsigned short lmp_subv = resp[13] | (resp[14] << 8);
+ unsigned short brf_chip = (lmp_subv & 0x7c00) >> 10;
+ static const char *c_brf_chip[8] = {
+ "unknown",
+ "unknown",
+ "brf6100",
+ "brf6150",
+ "brf6300",
+ "brf6350",
+ "unknown",
+ "wl1271"
+ };
+ char fw[100];
+
+ fprintf(stderr, "Texas module LMP sub-version : 0x%04x\n", lmp_subv);
+
+ fprintf(stderr,
+ "\tinternal version freeze: %d\n"
+ "\tsoftware version: %d\n"
+ "\tchip: %s (%d)\n",
+ lmp_subv & 0x7f,
+ ((lmp_subv & 0x8000) >> (15-3)) | ((lmp_subv & 0x380) >> 7),
+ ((brf_chip > 7) ? "unknown" : c_brf_chip[brf_chip]),
+ brf_chip);
+
+ sprintf(fw, "/etc/firmware/%s.bin", c_brf_chip[brf_chip]);
+ texas_load_firmware(fd, fw);
+
+ texas_change_speed(fd, speed);
+ }
+ nanosleep(&tm, NULL);
+ return 0;
+}
diff --git a/tools/hciconfig.8 b/tools/hciconfig.8
new file mode 100644
index 0000000..35956c4
--- /dev/null
+++ b/tools/hciconfig.8
@@ -0,0 +1,277 @@
+.TH HCICONFIG 8 "Nov 11 2002" BlueZ "Linux System Administration"
+.SH NAME
+hciconfig \- configure Bluetooth devices
+.SH SYNOPSIS
+.B hciconfig
+.B \-h
+.br
+.B hciconfig
+.RB [\| \-a \|]
+.br
+.B hciconfig
+.RB [\| \-a \|]
+.B hciX
+.RI [\| command
+.RI [\| "command parameters" \|]\|]
+
+.SH DESCRIPTION
+.LP
+.B hciconfig
+is used to configure Bluetooth devices.
+.I hciX
+is the name of a Bluetooth device installed in the system. If
+.I hciX
+is not given,
+.B hciconfig
+prints name and basic information about all the Bluetooth devices installed in
+the system. If
+.I hciX
+is given but no command is given, it prints basic information on device
+.I hciX
+only. Basic information is
+interface type, BD address, ACL MTU, SCO MTU, flags (up, init, running, raw,
+page scan enabled, inquiry scan enabled, inquiry, authentication enabled,
+encryption enabled).
+.SH OPTIONS
+.TP
+.B \-h, \-\-help
+Gives a list of possible commands.
+.TP
+.B \-a, \-\-all
+Other than the basic info, print features, packet type, link policy, link mode,
+name, class, version.
+.SH COMMANDS
+.TP
+.B up
+Open and initialize HCI device.
+.TP
+.B down
+Close HCI device.
+.TP
+.B reset
+Reset HCI device.
+.TP
+.B rstat
+Reset statistic counters.
+.TP
+.B auth
+Enable authentication (sets device to security mode 3).
+.TP
+.B noauth
+Disable authentication.
+.TP
+.B encrypt
+Enable encryption (sets device to security mode 3).
+.TP
+.B noencrypt
+Disable encryption.
+.TP
+.B secmgr
+Enable security manager (current kernel support is limited).
+.TP
+.B nosecmgr
+Disable security manager.
+.TP
+.B piscan
+Enable page and inquiry scan.
+.TP
+.B noscan
+Disable page and inquiry scan.
+.TP
+.B iscan
+Enable inquiry scan, disable page scan.
+.TP
+.B pscan
+Enable page scan, disable inquiry scan.
+.TP
+\fBptype\fP [\fItype\fP]
+With no
+.I type
+, displays the current packet types. Otherwise, all the packet types specified
+by
+.I type
+are set.
+.I type
+is a comma-separated list of packet types, where the possible packet types are
+.BR DM1 ,
+.BR DM3 ,
+.BR DM5 ,
+.BR DH1 ,
+.BR DH3 ,
+.BR DH5 ,
+.BR HV1 ,
+.BR HV2 ,
+.BR HV3 .
+.TP
+.BI name " [name]"
+With no
+.IR name ,
+prints local name. Otherwise, sets local name to
+.IR name .
+.TP
+.BI class " [class]"
+With no
+.IR class ,
+prints class of device. Otherwise, sets class of device to
+.IR class .
+.I
+class
+is a 24-bit hex number describing the class of device, as specified in section
+1.2 of the Bluetooth Assigned Numers document.
+.TP
+.BI voice " [voice]"
+With no
+.IR voice ,
+prints voice setting. Otherwise, sets voice setting to
+.IR voice .
+.I voice
+is a 16-bit hex number describing the voice setting.
+.TP
+.BI iac " [iac]"
+With no
+.IR iac ,
+prints the current IAC setting. Otherwise, sets the IAC to
+.IR iac .
+.TP
+.BI inqtpl " [level]"
+With no
+.IR level ,
+prints out the current inquiry transmit power level. Otherwise, sets
+inquiry transmit power level to
+.IR level .
+.TP
+.BI inqmode " [mode]"
+With no
+.IR mode ,
+prints out the current inquiry mode. Otherwise, sets inquiry mode to
+.IR mode .
+.TP
+.BI inqdata " [data]"
+With no
+.IR name ,
+prints out the current inquiry data. Otherwise, sets inquiry data to
+.IR data .
+.TP
+.BI inqtype " [type]"
+With no
+.IR type ,
+prints out the current inquiry scan type. Otherwise, sets inquiry scan type to
+.IR type .
+.TP
+\fBinqparams\fP [\fIwin\fP:\fIint\fP]
+With no
+.IR win : int ,
+prints inquiry scan window and interval. Otherwise, sets inquiry scan window
+to
+.I win
+slots and inquiry scan interval to
+.I int
+slots.
+.TP
+\fBpageparms\fP [\fIwin\fP:\fIint\fP]
+With no
+.IR win : int ,
+prints page scan window and interval. Otherwise, sets page scan window to
+.I win
+slots and page scan interval to
+.I int
+slots.
+.TP
+.BI pageto " [to]"
+With no
+.IR to ,
+prints page timeout. Otherwise, sets page timeout
+to .I
+to
+slots.
+.TP
+.BI afhmode " [mode]"
+With no
+.IR mode ,
+prints out the current AFH mode. Otherwise, sets AFH mode to
+.IR mode .
+.TP
+.BI sspmode " [mode]"
+With no
+.IR mode ,
+prints out the current Simple Pairing mode. Otherwise, sets Simple Pairing mode to
+.IR mode .
+.TP
+\fBaclmtu\fP \fImtu\fP:\fIpkt\fP
+Sets ACL MTU to
+to
+.I mtu
+bytes and ACL buffer size to
+.I pkt
+packets.
+.TP
+\fBscomtu\fP \fImtu\fP:\fIpkt\fP
+Sets SCO MTU to
+.I mtu
+bytes and SCO buffer size to
+.I pkt
+packets.
+.TP
+.BI putkey " <bdaddr>"
+This command stores the link key for
+.I bdaddr
+on the device.
+.TP
+.BI delkey " <bdaddr>"
+This command deletes the stored link key for
+.I bdaddr
+from the device.
+.TP
+.BI oobdata
+Display local OOB data.
+.TP
+.BI commands
+Display supported commands.
+.TP
+.BI features
+Display device features.
+.TP
+.BI version
+Display version information.
+.TP
+.BI revision
+Display revision information.
+.TP
+.BI lm " [mode]"
+With no
+.I mode
+, prints link mode.
+.B MASTER
+or
+.B SLAVE
+mean, respectively, to ask to become master or to remain slave when a
+connection request comes in. The additional keyword
+.B ACCEPT
+means that baseband connections will be accepted even if there are no
+listening
+.I AF_BLUETOOTH
+sockets.
+.I mode
+is
+.B NONE
+or a comma-separated list of keywords, where possible keywords are
+.B MASTER
+and
+.B "ACCEPT" .
+.B NONE
+sets link policy to the default behaviour of remaining slave and not accepting
+baseband connections when there are no listening
+.I AF_BLUETOOTH
+sockets. If
+.B MASTER
+is present, the device will ask to become master if a connection request comes
+in. If
+.B ACCEPT
+is present, the device will accept baseband connections even when there are no
+listening
+.I AF_BLUETOOTH
+sockets.
+.SH AUTHORS
+Written by Maxim Krasnyansky <maxk@qualcomm.com> and Marcel Holtmann <marcel@holtmann.org>
+.PP
+man page by Fabrizio Gennari <fabrizio.gennari@philips.com>
diff --git a/tools/hciconfig.c b/tools/hciconfig.c
new file mode 100644
index 0000000..3db70a4
--- /dev/null
+++ b/tools/hciconfig.c
@@ -0,0 +1,2036 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2000-2001 Qualcomm Incorporated
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 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 <string.h>
+#include <getopt.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "textfile.h"
+#include "csr.h"
+
+static struct hci_dev_info di;
+static int all;
+
+static void print_dev_hdr(struct hci_dev_info *di);
+static void print_dev_info(int ctl, struct hci_dev_info *di);
+
+static void print_dev_list(int ctl, int flags)
+{
+ struct hci_dev_list_req *dl;
+ struct hci_dev_req *dr;
+ int i;
+
+ if (!(dl = malloc(HCI_MAX_DEV * sizeof(struct hci_dev_req) +
+ sizeof(uint16_t)))) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+ dl->dev_num = HCI_MAX_DEV;
+ dr = dl->dev_req;
+
+ if (ioctl(ctl, HCIGETDEVLIST, (void *) dl) < 0) {
+ perror("Can't get device list");
+ exit(1);
+ }
+
+ for (i = 0; i< dl->dev_num; i++) {
+ di.dev_id = (dr+i)->dev_id;
+ if (ioctl(ctl, HCIGETDEVINFO, (void *) &di) < 0)
+ continue;
+ if (hci_test_bit(HCI_RAW, &di.flags) &&
+ !bacmp(&di.bdaddr, BDADDR_ANY)) {
+ int dd = hci_open_dev(di.dev_id);
+ hci_read_bd_addr(dd, &di.bdaddr, 1000);
+ hci_close_dev(dd);
+ }
+ print_dev_info(ctl, &di);
+ }
+}
+
+static void print_pkt_type(struct hci_dev_info *di)
+{
+ char *str;
+ str = hci_ptypetostr(di->pkt_type);
+ printf("\tPacket type: %s\n", str);
+ bt_free(str);
+}
+
+static void print_link_policy(struct hci_dev_info *di)
+{
+ printf("\tLink policy: %s\n", hci_lptostr(di->link_policy));
+}
+
+static void print_link_mode(struct hci_dev_info *di)
+{
+ char *str;
+ str = hci_lmtostr(di->link_mode);
+ printf("\tLink mode: %s\n", str);
+ bt_free(str);
+}
+
+static void print_dev_features(struct hci_dev_info *di, int format)
+{
+ printf("\tFeatures: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
+ "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n",
+ di->features[0], di->features[1], di->features[2],
+ di->features[3], di->features[4], di->features[5],
+ di->features[6], di->features[7]);
+
+ if (format) {
+ char *tmp = lmp_featurestostr(di->features, "\t\t", 63);
+ printf("%s\n", tmp);
+ bt_free(tmp);
+ }
+}
+
+static void print_le_states(uint64_t states)
+{
+ int i;
+ const char *le_states[] = {
+ "Non-connectable Advertising State" ,
+ "Scannable Advertising State",
+ "Connectable Advertising State",
+ "Directed Advertising State",
+ "Passive Scanning State",
+ "Active Scanning State",
+ "Initiating State/Connection State in Master Role",
+ "Connection State in the Slave Role",
+ "Non-connectable Advertising State and Passive Scanning State combination",
+ "Scannable Advertising State and Passive Scanning State combination",
+ "Connectable Advertising State and Passive Scanning State combination",
+ "Directed Advertising State and Passive Scanning State combination",
+ "Non-connectable Advertising State and Active Scanning State combination",
+ "Scannable Advertising State and Active Scanning State combination",
+ "Connectable Advertising State and Active Scanning State combination",
+ "Directed Advertising State and Active Scanning State combination",
+ "Non-connectable Advertising State and Initiating State combination",
+ "Scannable Advertising State and Initiating State combination",
+ "Non-connectable Advertising State and Master Role combination",
+ "Scannable Advertising State and Master Role combination",
+ "Non-connectable Advertising State and Slave Role combination",
+ "Scannable Advertising State and Slave Role combination",
+ "Passive Scanning State and Initiating State combination",
+ "Active Scanning State and Initiating State combination",
+ "Passive Scanning State and Master Role combination",
+ "Active Scanning State and Master Role combination",
+ "Passive Scanning State and Slave Role combination",
+ "Active Scanning State and Slave Role combination",
+ "Initiating State and Master Role combination/Master Role and Master Role combination",
+ NULL
+ };
+
+ printf("Supported link layer states:\n");
+ for (i = 0; le_states[i]; i++) {
+ const char *status;
+
+ status = states & (1 << i) ? "YES" : "NO ";
+ printf("\t%s %s\n", status, le_states[i]);
+ }
+}
+
+static void cmd_rstat(int ctl, int hdev, char *opt)
+{
+ /* Reset HCI device stat counters */
+ if (ioctl(ctl, HCIDEVRESTAT, hdev) < 0) {
+ fprintf(stderr, "Can't reset stats counters hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+}
+
+static void cmd_scan(int ctl, int hdev, char *opt)
+{
+ struct hci_dev_req dr;
+
+ dr.dev_id = hdev;
+ dr.dev_opt = SCAN_DISABLED;
+ if (!strcmp(opt, "iscan"))
+ dr.dev_opt = SCAN_INQUIRY;
+ else if (!strcmp(opt, "pscan"))
+ dr.dev_opt = SCAN_PAGE;
+ else if (!strcmp(opt, "piscan"))
+ dr.dev_opt = SCAN_PAGE | SCAN_INQUIRY;
+
+ if (ioctl(ctl, HCISETSCAN, (unsigned long) &dr) < 0) {
+ fprintf(stderr, "Can't set scan mode on hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+}
+
+static void cmd_le_addr(int ctl, int hdev, char *opt)
+{
+ struct hci_request rq;
+ le_set_random_address_cp cp;
+ uint8_t status;
+ int dd, err, ret;
+
+ if (!opt)
+ return;
+
+ if (hdev < 0)
+ hdev = hci_get_route(NULL);
+
+ dd = hci_open_dev(hdev);
+ if (dd < 0) {
+ err = errno;
+ fprintf(stderr, "Could not open device: %s(%d)\n",
+ strerror(err), err);
+ exit(1);
+ }
+
+ memset(&cp, 0, sizeof(cp));
+
+ str2ba(opt, &cp.bdaddr);
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_LE_CTL;
+ rq.ocf = OCF_LE_SET_RANDOM_ADDRESS;
+ rq.cparam = &cp;
+ rq.clen = LE_SET_RANDOM_ADDRESS_CP_SIZE;
+ rq.rparam = &status;
+ rq.rlen = 1;
+
+ ret = hci_send_req(dd, &rq, 1000);
+ if (status || ret < 0) {
+ err = errno;
+ fprintf(stderr, "Can't set random address for hci%d: "
+ "%s (%d)\n", hdev, strerror(err), err);
+ }
+
+ hci_close_dev(dd);
+}
+
+static void cmd_le_adv(int ctl, int hdev, char *opt)
+{
+ struct hci_request rq;
+ le_set_advertise_enable_cp advertise_cp;
+ uint8_t status;
+ int dd, ret;
+
+ if (hdev < 0)
+ hdev = hci_get_route(NULL);
+
+ dd = hci_open_dev(hdev);
+ if (dd < 0) {
+ perror("Could not open device");
+ exit(1);
+ }
+
+ memset(&advertise_cp, 0, sizeof(advertise_cp));
+ if (strcmp(opt, "noleadv") == 0)
+ advertise_cp.enable = 0x00;
+ else
+ advertise_cp.enable = 0x01;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_LE_CTL;
+ rq.ocf = OCF_LE_SET_ADVERTISE_ENABLE;
+ rq.cparam = &advertise_cp;
+ rq.clen = LE_SET_ADVERTISE_ENABLE_CP_SIZE;
+ rq.rparam = &status;
+ rq.rlen = 1;
+
+ ret = hci_send_req(dd, &rq, 1000);
+
+ hci_close_dev(dd);
+
+ if (ret < 0) {
+ fprintf(stderr, "Can't set advertise mode on hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ if (status) {
+ fprintf(stderr, "LE set advertise enable on hci%d returned status %d\n",
+ hdev, status);
+ exit(1);
+ }
+}
+
+static void cmd_le_states(int ctl, int hdev, char *opt)
+{
+ le_read_supported_states_rp rp;
+ struct hci_request rq;
+ int err, dd;
+
+ if (hdev < 0)
+ hdev = hci_get_route(NULL);
+
+ dd = hci_open_dev(hdev);
+ if (dd < 0) {
+ fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ memset(&rp, 0, sizeof(rp));
+ memset(&rq, 0, sizeof(rq));
+
+ rq.ogf = OGF_LE_CTL;
+ rq.ocf = OCF_LE_READ_SUPPORTED_STATES;
+ rq.rparam = &rp;
+ rq.rlen = LE_READ_SUPPORTED_STATES_RP_SIZE;
+
+ err = hci_send_req(dd, &rq, 1000);
+
+ hci_close_dev(dd);
+
+ if (err < 0) {
+ fprintf(stderr, "Can't read LE supported states on hci%d:"
+ " %s(%d)\n", hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ if (rp.status) {
+ fprintf(stderr, "Read LE supported states on hci%d"
+ " returned status %d\n", hdev, rp.status);
+ exit(1);
+ }
+
+ print_le_states(rp.states);
+}
+
+static void cmd_iac(int ctl, int hdev, char *opt)
+{
+ int s = hci_open_dev(hdev);
+
+ if (s < 0) {
+ fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+ if (opt) {
+ int l = strtoul(opt, 0, 16);
+ uint8_t lap[3];
+ if (!strcasecmp(opt, "giac")) {
+ l = 0x9e8b33;
+ } else if (!strcasecmp(opt, "liac")) {
+ l = 0x9e8b00;
+ } else if (l < 0x9e8b00 || l > 0x9e8b3f) {
+ printf("Invalid access code 0x%x\n", l);
+ exit(1);
+ }
+ lap[0] = (l & 0xff);
+ lap[1] = (l >> 8) & 0xff;
+ lap[2] = (l >> 16) & 0xff;
+ if (hci_write_current_iac_lap(s, 1, lap, 1000) < 0) {
+ printf("Failed to set IAC on hci%d: %s\n", hdev, strerror(errno));
+ exit(1);
+ }
+ } else {
+ uint8_t lap[3 * MAX_IAC_LAP];
+ int i, j;
+ uint8_t n;
+ if (hci_read_current_iac_lap(s, &n, lap, 1000) < 0) {
+ printf("Failed to read IAC from hci%d: %s\n", hdev, strerror(errno));
+ exit(1);
+ }
+ print_dev_hdr(&di);
+ printf("\tIAC: ");
+ for (i = 0; i < n; i++) {
+ printf("0x");
+ for (j = 3; j--; )
+ printf("%02x", lap[j + 3 * i]);
+ if (i < n - 1)
+ printf(", ");
+ }
+ printf("\n");
+ }
+ close(s);
+}
+
+static void cmd_auth(int ctl, int hdev, char *opt)
+{
+ struct hci_dev_req dr;
+
+ dr.dev_id = hdev;
+ if (!strcmp(opt, "auth"))
+ dr.dev_opt = AUTH_ENABLED;
+ else
+ dr.dev_opt = AUTH_DISABLED;
+
+ if (ioctl(ctl, HCISETAUTH, (unsigned long) &dr) < 0) {
+ fprintf(stderr, "Can't set auth on hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+}
+
+static void cmd_encrypt(int ctl, int hdev, char *opt)
+{
+ struct hci_dev_req dr;
+
+ dr.dev_id = hdev;
+ if (!strcmp(opt, "encrypt"))
+ dr.dev_opt = ENCRYPT_P2P;
+ else
+ dr.dev_opt = ENCRYPT_DISABLED;
+
+ if (ioctl(ctl, HCISETENCRYPT, (unsigned long) &dr) < 0) {
+ fprintf(stderr, "Can't set encrypt on hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+}
+
+static void cmd_up(int ctl, int hdev, char *opt)
+{
+ /* Start HCI device */
+ if (ioctl(ctl, HCIDEVUP, hdev) < 0) {
+ if (errno == EALREADY)
+ return;
+ fprintf(stderr, "Can't init device hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+}
+
+static void cmd_down(int ctl, int hdev, char *opt)
+{
+ /* Stop HCI device */
+ if (ioctl(ctl, HCIDEVDOWN, hdev) < 0) {
+ fprintf(stderr, "Can't down device hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+}
+
+static void cmd_reset(int ctl, int hdev, char *opt)
+{
+ /* Reset HCI device */
+#if 0
+ if (ioctl(ctl, HCIDEVRESET, hdev) < 0 ){
+ fprintf(stderr, "Reset failed for device hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+#endif
+ cmd_down(ctl, hdev, "down");
+ cmd_up(ctl, hdev, "up");
+}
+
+static void cmd_ptype(int ctl, int hdev, char *opt)
+{
+ struct hci_dev_req dr;
+
+ dr.dev_id = hdev;
+
+ if (hci_strtoptype(opt, &dr.dev_opt)) {
+ if (ioctl(ctl, HCISETPTYPE, (unsigned long) &dr) < 0) {
+ fprintf(stderr, "Can't set pkttype on hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+ } else {
+ print_dev_hdr(&di);
+ print_pkt_type(&di);
+ }
+}
+
+static void cmd_lp(int ctl, int hdev, char *opt)
+{
+ struct hci_dev_req dr;
+
+ dr.dev_id = hdev;
+
+ if (hci_strtolp(opt, &dr.dev_opt)) {
+ if (ioctl(ctl, HCISETLINKPOL, (unsigned long) &dr) < 0) {
+ fprintf(stderr, "Can't set link policy on hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+ } else {
+ print_dev_hdr(&di);
+ print_link_policy(&di);
+ }
+}
+
+static void cmd_lm(int ctl, int hdev, char *opt)
+{
+ struct hci_dev_req dr;
+
+ dr.dev_id = hdev;
+
+ if (hci_strtolm(opt, &dr.dev_opt)) {
+ if (ioctl(ctl, HCISETLINKMODE, (unsigned long) &dr) < 0) {
+ fprintf(stderr, "Can't set default link mode on hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+ } else {
+ print_dev_hdr(&di);
+ print_link_mode(&di);
+ }
+}
+
+static void cmd_aclmtu(int ctl, int hdev, char *opt)
+{
+ struct hci_dev_req dr = { dev_id: hdev };
+ uint16_t mtu, mpkt;
+
+ if (!opt)
+ return;
+
+ if (sscanf(opt, "%4hu:%4hu", &mtu, &mpkt) != 2)
+ return;
+
+ dr.dev_opt = htobl(htobs(mpkt) | (htobs(mtu) << 16));
+
+ if (ioctl(ctl, HCISETACLMTU, (unsigned long) &dr) < 0) {
+ fprintf(stderr, "Can't set ACL mtu on hci%d: %s(%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+}
+
+static void cmd_scomtu(int ctl, int hdev, char *opt)
+{
+ struct hci_dev_req dr = { dev_id: hdev };
+ uint16_t mtu, mpkt;
+
+ if (!opt)
+ return;
+
+ if (sscanf(opt, "%4hu:%4hu", &mtu, &mpkt) != 2)
+ return;
+
+ dr.dev_opt = htobl(htobs(mpkt) | (htobs(mtu) << 16));
+
+ if (ioctl(ctl, HCISETSCOMTU, (unsigned long) &dr) < 0) {
+ fprintf(stderr, "Can't set SCO mtu on hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+}
+
+static void cmd_features(int ctl, int hdev, char *opt)
+{
+ uint8_t features[8], max_page = 0;
+ char *tmp;
+ int i, dd;
+
+ if (!(di.features[7] & LMP_EXT_FEAT)) {
+ print_dev_hdr(&di);
+ print_dev_features(&di, 1);
+ return;
+ }
+
+ dd = hci_open_dev(hdev);
+ if (dd < 0) {
+ fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ if (hci_read_local_ext_features(dd, 0, &max_page, features, 1000) < 0) {
+ fprintf(stderr, "Can't read extended features hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ print_dev_hdr(&di);
+ printf("\tFeatures%s: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
+ "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n",
+ (max_page > 0) ? " page 0" : "",
+ features[0], features[1], features[2], features[3],
+ features[4], features[5], features[6], features[7]);
+
+ tmp = lmp_featurestostr(di.features, "\t\t", 63);
+ printf("%s\n", tmp);
+ bt_free(tmp);
+
+ for (i = 1; i <= max_page; i++) {
+ if (hci_read_local_ext_features(dd, i, NULL,
+ features, 1000) < 0)
+ continue;
+
+ printf("\tFeatures page %d: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
+ "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n", i,
+ features[0], features[1], features[2], features[3],
+ features[4], features[5], features[6], features[7]);
+ }
+
+ hci_close_dev(dd);
+}
+
+static void cmd_name(int ctl, int hdev, char *opt)
+{
+ int dd;
+
+ dd = hci_open_dev(hdev);
+ if (dd < 0) {
+ fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ if (opt) {
+ if (hci_write_local_name(dd, opt, 2000) < 0) {
+ fprintf(stderr, "Can't change local name on hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+ } else {
+ char name[249];
+ int i;
+
+ if (hci_read_local_name(dd, sizeof(name), name, 1000) < 0) {
+ fprintf(stderr, "Can't read local name on hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ for (i = 0; i < 248 && name[i]; i++) {
+ if ((unsigned char) name[i] < 32 || name[i] == 127)
+ name[i] = '.';
+ }
+
+ name[248] = '\0';
+
+ print_dev_hdr(&di);
+ printf("\tName: '%s'\n", name);
+ }
+
+ hci_close_dev(dd);
+}
+
+/*
+ * see http://www.bluetooth.org/assigned-numbers/baseband.htm --- all
+ * strings are reproduced verbatim
+ */
+static char *get_minor_device_name(int major, int minor)
+{
+ switch (major) {
+ case 0: /* misc */
+ return "";
+ case 1: /* computer */
+ switch (minor) {
+ case 0:
+ return "Uncategorized";
+ case 1:
+ return "Desktop workstation";
+ case 2:
+ return "Server";
+ case 3:
+ return "Laptop";
+ case 4:
+ return "Handheld";
+ case 5:
+ return "Palm";
+ case 6:
+ return "Wearable";
+ }
+ break;
+ case 2: /* phone */
+ switch (minor) {
+ case 0:
+ return "Uncategorized";
+ case 1:
+ return "Cellular";
+ case 2:
+ return "Cordless";
+ case 3:
+ return "Smart phone";
+ case 4:
+ return "Wired modem or voice gateway";
+ case 5:
+ return "Common ISDN Access";
+ case 6:
+ return "Sim Card Reader";
+ }
+ break;
+ case 3: /* lan access */
+ if (minor == 0)
+ return "Uncategorized";
+ switch (minor / 8) {
+ case 0:
+ return "Fully available";
+ case 1:
+ return "1-17% utilized";
+ case 2:
+ return "17-33% utilized";
+ case 3:
+ return "33-50% utilized";
+ case 4:
+ return "50-67% utilized";
+ case 5:
+ return "67-83% utilized";
+ case 6:
+ return "83-99% utilized";
+ case 7:
+ return "No service available";
+ }
+ break;
+ case 4: /* audio/video */
+ switch (minor) {
+ case 0:
+ return "Uncategorized";
+ case 1:
+ return "Device conforms to the Headset profile";
+ case 2:
+ return "Hands-free";
+ /* 3 is reserved */
+ case 4:
+ return "Microphone";
+ case 5:
+ return "Loudspeaker";
+ case 6:
+ return "Headphones";
+ case 7:
+ return "Portable Audio";
+ case 8:
+ return "Car Audio";
+ case 9:
+ return "Set-top box";
+ case 10:
+ return "HiFi Audio Device";
+ case 11:
+ return "VCR";
+ case 12:
+ return "Video Camera";
+ case 13:
+ return "Camcorder";
+ case 14:
+ return "Video Monitor";
+ case 15:
+ return "Video Display and Loudspeaker";
+ case 16:
+ return "Video Conferencing";
+ /* 17 is reserved */
+ case 18:
+ return "Gaming/Toy";
+ }
+ break;
+ case 5: /* peripheral */ {
+ static char cls_str[48];
+
+ cls_str[0] = '\0';
+
+ switch (minor & 48) {
+ case 16:
+ strncpy(cls_str, "Keyboard", sizeof(cls_str));
+ break;
+ case 32:
+ strncpy(cls_str, "Pointing device", sizeof(cls_str));
+ break;
+ case 48:
+ strncpy(cls_str, "Combo keyboard/pointing device", sizeof(cls_str));
+ break;
+ }
+ if ((minor & 15) && (strlen(cls_str) > 0))
+ strcat(cls_str, "/");
+
+ switch (minor & 15) {
+ case 0:
+ break;
+ case 1:
+ strncat(cls_str, "Joystick", sizeof(cls_str) - strlen(cls_str));
+ break;
+ case 2:
+ strncat(cls_str, "Gamepad", sizeof(cls_str) - strlen(cls_str));
+ break;
+ case 3:
+ strncat(cls_str, "Remote control", sizeof(cls_str) - strlen(cls_str));
+ break;
+ case 4:
+ strncat(cls_str, "Sensing device", sizeof(cls_str) - strlen(cls_str));
+ break;
+ case 5:
+ strncat(cls_str, "Digitizer tablet", sizeof(cls_str) - strlen(cls_str));
+ break;
+ case 6:
+ strncat(cls_str, "Card reader", sizeof(cls_str) - strlen(cls_str));
+ break;
+ default:
+ strncat(cls_str, "(reserved)", sizeof(cls_str) - strlen(cls_str));
+ break;
+ }
+ if (strlen(cls_str) > 0)
+ return cls_str;
+ }
+ case 6: /* imaging */
+ if (minor & 4)
+ return "Display";
+ if (minor & 8)
+ return "Camera";
+ if (minor & 16)
+ return "Scanner";
+ if (minor & 32)
+ return "Printer";
+ break;
+ case 7: /* wearable */
+ switch (minor) {
+ case 1:
+ return "Wrist Watch";
+ case 2:
+ return "Pager";
+ case 3:
+ return "Jacket";
+ case 4:
+ return "Helmet";
+ case 5:
+ return "Glasses";
+ }
+ break;
+ case 8: /* toy */
+ switch (minor) {
+ case 1:
+ return "Robot";
+ case 2:
+ return "Vehicle";
+ case 3:
+ return "Doll / Action Figure";
+ case 4:
+ return "Controller";
+ case 5:
+ return "Game";
+ }
+ break;
+ case 63: /* uncategorised */
+ return "";
+ }
+ return "Unknown (reserved) minor device class";
+}
+
+static void cmd_class(int ctl, int hdev, char *opt)
+{
+ static const char *services[] = { "Positioning",
+ "Networking",
+ "Rendering",
+ "Capturing",
+ "Object Transfer",
+ "Audio",
+ "Telephony",
+ "Information" };
+ static const char *major_devices[] = { "Miscellaneous",
+ "Computer",
+ "Phone",
+ "LAN Access",
+ "Audio/Video",
+ "Peripheral",
+ "Imaging",
+ "Uncategorized" };
+ int s = hci_open_dev(hdev);
+
+ if (s < 0) {
+ fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+ if (opt) {
+ uint32_t cod = strtoul(opt, NULL, 16);
+ if (hci_write_class_of_dev(s, cod, 2000) < 0) {
+ fprintf(stderr, "Can't write local class of device on hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+ } else {
+ uint8_t cls[3];
+ if (hci_read_class_of_dev(s, cls, 1000) < 0) {
+ fprintf(stderr, "Can't read class of device on hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+ print_dev_hdr(&di);
+ printf("\tClass: 0x%02x%02x%02x\n", cls[2], cls[1], cls[0]);
+ printf("\tService Classes: ");
+ if (cls[2]) {
+ unsigned int i;
+ int first = 1;
+ for (i = 0; i < (sizeof(services) / sizeof(*services)); i++)
+ if (cls[2] & (1 << i)) {
+ if (!first)
+ printf(", ");
+ printf("%s", services[i]);
+ first = 0;
+ }
+ } else
+ printf("Unspecified");
+ printf("\n\tDevice Class: ");
+ if ((cls[1] & 0x1f) >= sizeof(major_devices) / sizeof(*major_devices))
+ printf("Invalid Device Class!\n");
+ else
+ printf("%s, %s\n", major_devices[cls[1] & 0x1f],
+ get_minor_device_name(cls[1] & 0x1f, cls[0] >> 2));
+ }
+}
+
+static void cmd_voice(int ctl, int hdev, char *opt)
+{
+ static char *icf[] = { "Linear",
+ "u-Law",
+ "A-Law",
+ "Reserved" };
+
+ static char *idf[] = { "1's complement",
+ "2's complement",
+ "Sign-Magnitude",
+ "Reserved" };
+
+ static char *iss[] = { "8 bit",
+ "16 bit" };
+
+ static char *acf[] = { "CVSD",
+ "u-Law",
+ "A-Law",
+ "Reserved" };
+
+ int s = hci_open_dev(hdev);
+
+ if (s < 0) {
+ fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+ if (opt) {
+ uint16_t vs = htobs(strtoul(opt, NULL, 16));
+ if (hci_write_voice_setting(s, vs, 2000) < 0) {
+ fprintf(stderr, "Can't write voice setting on hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+ } else {
+ uint16_t vs;
+ uint8_t ic;
+ if (hci_read_voice_setting(s, &vs, 1000) < 0) {
+ fprintf(stderr, "Can't read voice setting on hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+ vs = htobs(vs);
+ ic = (vs & 0x0300) >> 8;
+ print_dev_hdr(&di);
+ printf("\tVoice setting: 0x%04x%s\n", vs,
+ ((vs & 0x03fc) == 0x0060) ? " (Default Condition)" : "");
+ printf("\tInput Coding: %s\n", icf[ic]);
+ printf("\tInput Data Format: %s\n", idf[(vs & 0xc0) >> 6]);
+
+ if (!ic) {
+ printf("\tInput Sample Size: %s\n",
+ iss[(vs & 0x20) >> 5]);
+ printf("\t# of bits padding at MSB: %d\n",
+ (vs & 0x1c) >> 2);
+ }
+ printf("\tAir Coding Format: %s\n", acf[vs & 0x03]);
+ }
+}
+
+static int get_link_key(const bdaddr_t *local, const bdaddr_t *peer,
+ uint8_t *key)
+{
+ char filename[PATH_MAX + 1], addr[18], tmp[3], *str;
+ int i;
+
+ ba2str(local, addr);
+ create_name(filename, PATH_MAX, STORAGEDIR, addr, "linkkeys");
+
+ ba2str(peer, addr);
+ str = textfile_get(filename, addr);
+ if (!str)
+ return -EIO;
+
+ memset(tmp, 0, sizeof(tmp));
+ for (i = 0; i < 16; i++) {
+ memcpy(tmp, str + (i * 2), 2);
+ key[i] = (uint8_t) strtol(tmp, NULL, 16);
+ }
+
+ free(str);
+
+ return 0;
+}
+
+static void cmd_putkey(int ctl, int hdev, char *opt)
+{
+ struct hci_dev_info di;
+ bdaddr_t bdaddr;
+ uint8_t key[16];
+ int dd;
+
+ if (!opt)
+ return;
+
+ dd = hci_open_dev(hdev);
+ if (dd < 0) {
+ fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ if (hci_devinfo(hdev, &di) < 0) {
+ fprintf(stderr, "Can't get device info for hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ str2ba(opt, &bdaddr);
+ if (get_link_key(&di.bdaddr, &bdaddr, key) < 0) {
+ fprintf(stderr, "Can't find link key for %s on hci%d\n", opt, hdev);
+ exit(1);
+ }
+
+ if (hci_write_stored_link_key(dd, &bdaddr, key, 1000) < 0) {
+ fprintf(stderr, "Can't write stored link key on hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ hci_close_dev(dd);
+}
+
+static void cmd_delkey(int ctl, int hdev, char *opt)
+{
+ bdaddr_t bdaddr;
+ uint8_t all;
+ int dd;
+
+ if (!opt)
+ return;
+
+ dd = hci_open_dev(hdev);
+ if (dd < 0) {
+ fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ if (!strcasecmp(opt, "all")) {
+ bacpy(&bdaddr, BDADDR_ANY);
+ all = 1;
+ } else {
+ str2ba(opt, &bdaddr);
+ all = 0;
+ }
+
+ if (hci_delete_stored_link_key(dd, &bdaddr, all, 1000) < 0) {
+ fprintf(stderr, "Can't delete stored link key on hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ hci_close_dev(dd);
+}
+
+static void cmd_oob_data(int ctl, int hdev, char *opt)
+{
+ uint8_t hash[16], randomizer[16];
+ int i, dd;
+
+ dd = hci_open_dev(hdev);
+ if (dd < 0) {
+ fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ if (hci_read_local_oob_data(dd, hash, randomizer, 1000) < 0) {
+ fprintf(stderr, "Can't read local OOB data on hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ print_dev_hdr(&di);
+ printf("\tOOB Hash: ");
+ for (i = 0; i < 16; i++)
+ printf(" %02x", hash[i]);
+ printf("\n\tRandomizer:");
+ for (i = 0; i < 16; i++)
+ printf(" %02x", randomizer[i]);
+ printf("\n");
+
+ hci_close_dev(dd);
+}
+
+static void cmd_commands(int ctl, int hdev, char *opt)
+{
+ uint8_t cmds[64];
+ char *str;
+ int i, n, dd;
+
+ dd = hci_open_dev(hdev);
+ if (dd < 0) {
+ fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ if (hci_read_local_commands(dd, cmds, 1000) < 0) {
+ fprintf(stderr, "Can't read support commands on hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ print_dev_hdr(&di);
+ for (i = 0; i < 64; i++) {
+ if (!cmds[i])
+ continue;
+
+ printf("%s Octet %-2d = 0x%02x (Bit",
+ i ? "\t\t ": "\tCommands:", i, cmds[i]);
+ for (n = 0; n < 8; n++)
+ if (cmds[i] & (1 << n))
+ printf(" %d", n);
+ printf(")\n");
+ }
+
+ str = hci_commandstostr(cmds, "\t", 71);
+ printf("%s\n", str);
+ bt_free(str);
+
+ hci_close_dev(dd);
+}
+
+static void cmd_version(int ctl, int hdev, char *opt)
+{
+ struct hci_version ver;
+ char *hciver, *lmpver;
+ int dd;
+
+ dd = hci_open_dev(hdev);
+ if (dd < 0) {
+ fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ if (hci_read_local_version(dd, &ver, 1000) < 0) {
+ fprintf(stderr, "Can't read version info hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ hciver = hci_vertostr(ver.hci_ver);
+ lmpver = lmp_vertostr(ver.lmp_ver);
+
+ print_dev_hdr(&di);
+ printf("\tHCI Version: %s (0x%x) Revision: 0x%x\n"
+ "\tLMP Version: %s (0x%x) Subversion: 0x%x\n"
+ "\tManufacturer: %s (%d)\n",
+ hciver ? hciver : "n/a", ver.hci_ver, ver.hci_rev,
+ lmpver ? lmpver : "n/a", ver.lmp_ver, ver.lmp_subver,
+ bt_compidtostr(ver.manufacturer), ver.manufacturer);
+
+ if (hciver)
+ bt_free(hciver);
+ if (lmpver)
+ bt_free(lmpver);
+
+ hci_close_dev(dd);
+}
+
+static void cmd_inq_tpl(int ctl, int hdev, char *opt)
+{
+ int dd;
+
+ dd = hci_open_dev(hdev);
+ if (dd < 0) {
+ fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ if (opt) {
+ int8_t level = atoi(opt);
+
+ if (hci_write_inquiry_transmit_power_level(dd, level, 2000) < 0) {
+ fprintf(stderr, "Can't set inquiry transmit power level on hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+ } else {
+ int8_t level;
+
+ if (hci_read_inq_response_tx_power_level(dd, &level, 1000) < 0) {
+ fprintf(stderr, "Can't read inquiry transmit power level on hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ print_dev_hdr(&di);
+ printf("\tInquiry transmit power level: %d\n", level);
+ }
+
+ hci_close_dev(dd);
+}
+
+static void cmd_inq_mode(int ctl, int hdev, char *opt)
+{
+ int dd;
+
+ dd = hci_open_dev(hdev);
+ if (dd < 0) {
+ fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ if (opt) {
+ uint8_t mode = atoi(opt);
+
+ if (hci_write_inquiry_mode(dd, mode, 2000) < 0) {
+ fprintf(stderr, "Can't set inquiry mode on hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+ } else {
+ uint8_t mode;
+
+ if (hci_read_inquiry_mode(dd, &mode, 1000) < 0) {
+ fprintf(stderr, "Can't read inquiry mode on hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ print_dev_hdr(&di);
+ printf("\tInquiry mode: ");
+ switch (mode) {
+ case 0:
+ printf("Standard Inquiry\n");
+ break;
+ case 1:
+ printf("Inquiry with RSSI\n");
+ break;
+ case 2:
+ printf("Inquiry with RSSI or Extended Inquiry\n");
+ break;
+ default:
+ printf("Unknown (0x%02x)\n", mode);
+ break;
+ }
+ }
+
+ hci_close_dev(dd);
+}
+
+static void cmd_inq_data(int ctl, int hdev, char *opt)
+{
+ int i, dd;
+
+ dd = hci_open_dev(hdev);
+ if (dd < 0) {
+ fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ if (opt) {
+ uint8_t fec = 0, data[240];
+ char tmp[3];
+ int i, size;
+
+ memset(data, 0, sizeof(data));
+
+ memset(tmp, 0, sizeof(tmp));
+ size = (strlen(opt) + 1) / 2;
+ if (size > 240)
+ size = 240;
+
+ for (i = 0; i < size; i++) {
+ memcpy(tmp, opt + (i * 2), 2);
+ data[i] = strtol(tmp, NULL, 16);
+ }
+
+ if (hci_write_ext_inquiry_response(dd, fec, data, 2000) < 0) {
+ fprintf(stderr, "Can't set extended inquiry response on hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+ } else {
+ uint8_t fec, data[240], len, type, *ptr;
+ char *str;
+
+ if (hci_read_ext_inquiry_response(dd, &fec, data, 1000) < 0) {
+ fprintf(stderr, "Can't read extended inquiry response on hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ print_dev_hdr(&di);
+ printf("\tFEC %s\n\t\t", fec ? "enabled" : "disabled");
+ for (i = 0; i < 240; i++)
+ printf("%02x%s%s", data[i], (i + 1) % 8 ? "" : " ",
+ (i + 1) % 16 ? " " : (i < 239 ? "\n\t\t" : "\n"));
+
+ ptr = data;
+ while (*ptr) {
+ len = *ptr++;
+ type = *ptr++;
+ switch (type) {
+ case 0x01:
+ printf("\tFlags:");
+ for (i = 0; i < len - 1; i++)
+ printf(" 0x%2.2x", *((uint8_t *) (ptr + i)));
+ printf("\n");
+ break;
+ case 0x02:
+ case 0x03:
+ printf("\t%s service classes:",
+ type == 0x02 ? "Shortened" : "Complete");
+ for (i = 0; i < (len - 1) / 2; i++) {
+ uint16_t val = btohs(bt_get_unaligned((uint16_t *) (ptr + (i * 2))));
+ printf(" 0x%4.4x", val);
+ }
+ printf("\n");
+ break;
+ case 0x08:
+ case 0x09:
+ str = malloc(len);
+ if (str) {
+ snprintf(str, len, "%s", ptr);
+ for (i = 0; i < len - 1; i++) {
+ if ((unsigned char) str[i] < 32 || str[i] == 127)
+ str[i] = '.';
+ }
+ printf("\t%s local name: \'%s\'\n",
+ type == 0x08 ? "Shortened" : "Complete", str);
+ free(str);
+ }
+ break;
+ case 0x0a:
+ printf("\tTX power level: %d\n", *((int8_t *) ptr));
+ break;
+ case 0x10:
+ printf("\tDevice ID with %d bytes data\n",
+ len - 1);
+ break;
+ default:
+ printf("\tUnknown type 0x%02x with %d bytes data\n",
+ type, len - 1);
+ break;
+ }
+
+ ptr += (len - 1);
+ }
+
+ printf("\n");
+ }
+
+ hci_close_dev(dd);
+}
+
+static void cmd_inq_type(int ctl, int hdev, char *opt)
+{
+ int dd;
+
+ dd = hci_open_dev(hdev);
+ if (dd < 0) {
+ fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ if (opt) {
+ uint8_t type = atoi(opt);
+
+ if (hci_write_inquiry_scan_type(dd, type, 2000) < 0) {
+ fprintf(stderr, "Can't set inquiry scan type on hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+ } else {
+ uint8_t type;
+
+ if (hci_read_inquiry_scan_type(dd, &type, 1000) < 0) {
+ fprintf(stderr, "Can't read inquiry scan type on hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ print_dev_hdr(&di);
+ printf("\tInquiry scan type: %s\n",
+ type == 1 ? "Interlaced Inquiry Scan" : "Standard Inquiry Scan");
+ }
+
+ hci_close_dev(dd);
+}
+
+static void cmd_inq_parms(int ctl, int hdev, char *opt)
+{
+ struct hci_request rq;
+ int s;
+
+ if ((s = hci_open_dev(hdev)) < 0) {
+ fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ memset(&rq, 0, sizeof(rq));
+
+ if (opt) {
+ unsigned int window, interval;
+ write_inq_activity_cp cp;
+
+ if (sscanf(opt,"%4u:%4u", &window, &interval) != 2) {
+ printf("Invalid argument format\n");
+ exit(1);
+ }
+
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_WRITE_INQ_ACTIVITY;
+ rq.cparam = &cp;
+ rq.clen = WRITE_INQ_ACTIVITY_CP_SIZE;
+
+ cp.window = htobs((uint16_t) window);
+ cp.interval = htobs((uint16_t) interval);
+
+ if (window < 0x12 || window > 0x1000)
+ printf("Warning: inquiry window out of range!\n");
+
+ if (interval < 0x12 || interval > 0x1000)
+ printf("Warning: inquiry interval out of range!\n");
+
+ if (hci_send_req(s, &rq, 2000) < 0) {
+ fprintf(stderr, "Can't set inquiry parameters name on hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+ } else {
+ uint16_t window, interval;
+ read_inq_activity_rp rp;
+
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_READ_INQ_ACTIVITY;
+ rq.rparam = &rp;
+ rq.rlen = READ_INQ_ACTIVITY_RP_SIZE;
+
+ if (hci_send_req(s, &rq, 1000) < 0) {
+ fprintf(stderr, "Can't read inquiry parameters on hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+ if (rp.status) {
+ printf("Read inquiry parameters on hci%d returned status %d\n",
+ hdev, rp.status);
+ exit(1);
+ }
+ print_dev_hdr(&di);
+
+ window = btohs(rp.window);
+ interval = btohs(rp.interval);
+ printf("\tInquiry interval: %u slots (%.2f ms), window: %u slots (%.2f ms)\n",
+ interval, (float)interval * 0.625, window, (float)window * 0.625);
+ }
+}
+
+static void cmd_page_parms(int ctl, int hdev, char *opt)
+{
+ struct hci_request rq;
+ int s;
+
+ if ((s = hci_open_dev(hdev)) < 0) {
+ fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ memset(&rq, 0, sizeof(rq));
+
+ if (opt) {
+ unsigned int window, interval;
+ write_page_activity_cp cp;
+
+ if (sscanf(opt,"%4u:%4u", &window, &interval) != 2) {
+ printf("Invalid argument format\n");
+ exit(1);
+ }
+
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_WRITE_PAGE_ACTIVITY;
+ rq.cparam = &cp;
+ rq.clen = WRITE_PAGE_ACTIVITY_CP_SIZE;
+
+ cp.window = htobs((uint16_t) window);
+ cp.interval = htobs((uint16_t) interval);
+
+ if (window < 0x12 || window > 0x1000)
+ printf("Warning: page window out of range!\n");
+
+ if (interval < 0x12 || interval > 0x1000)
+ printf("Warning: page interval out of range!\n");
+
+ if (hci_send_req(s, &rq, 2000) < 0) {
+ fprintf(stderr, "Can't set page parameters name on hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+ } else {
+ uint16_t window, interval;
+ read_page_activity_rp rp;
+
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_READ_PAGE_ACTIVITY;
+ rq.rparam = &rp;
+ rq.rlen = READ_PAGE_ACTIVITY_RP_SIZE;
+
+ if (hci_send_req(s, &rq, 1000) < 0) {
+ fprintf(stderr, "Can't read page parameters on hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+ if (rp.status) {
+ printf("Read page parameters on hci%d returned status %d\n",
+ hdev, rp.status);
+ exit(1);
+ }
+ print_dev_hdr(&di);
+
+ window = btohs(rp.window);
+ interval = btohs(rp.interval);
+ printf("\tPage interval: %u slots (%.2f ms), "
+ "window: %u slots (%.2f ms)\n",
+ interval, (float)interval * 0.625,
+ window, (float)window * 0.625);
+ }
+}
+
+static void cmd_page_to(int ctl, int hdev, char *opt)
+{
+ struct hci_request rq;
+ int s;
+
+ if ((s = hci_open_dev(hdev)) < 0) {
+ fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ memset(&rq, 0, sizeof(rq));
+
+ if (opt) {
+ unsigned int timeout;
+ write_page_timeout_cp cp;
+
+ if (sscanf(opt,"%5u", &timeout) != 1) {
+ printf("Invalid argument format\n");
+ exit(1);
+ }
+
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_WRITE_PAGE_TIMEOUT;
+ rq.cparam = &cp;
+ rq.clen = WRITE_PAGE_TIMEOUT_CP_SIZE;
+
+ cp.timeout = htobs((uint16_t) timeout);
+
+ if (timeout < 0x01 || timeout > 0xFFFF)
+ printf("Warning: page timeout out of range!\n");
+
+ if (hci_send_req(s, &rq, 2000) < 0) {
+ fprintf(stderr, "Can't set page timeout on hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+ } else {
+ uint16_t timeout;
+ read_page_timeout_rp rp;
+
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_READ_PAGE_TIMEOUT;
+ rq.rparam = &rp;
+ rq.rlen = READ_PAGE_TIMEOUT_RP_SIZE;
+
+ if (hci_send_req(s, &rq, 1000) < 0) {
+ fprintf(stderr, "Can't read page timeout on hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+ if (rp.status) {
+ printf("Read page timeout on hci%d returned status %d\n",
+ hdev, rp.status);
+ exit(1);
+ }
+ print_dev_hdr(&di);
+
+ timeout = btohs(rp.timeout);
+ printf("\tPage timeout: %u slots (%.2f ms)\n",
+ timeout, (float)timeout * 0.625);
+ }
+}
+
+static void cmd_afh_mode(int ctl, int hdev, char *opt)
+{
+ int dd;
+
+ dd = hci_open_dev(hdev);
+ if (dd < 0) {
+ fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ if (opt) {
+ uint8_t mode = atoi(opt);
+
+ if (hci_write_afh_mode(dd, mode, 2000) < 0) {
+ fprintf(stderr, "Can't set AFH mode on hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+ } else {
+ uint8_t mode;
+
+ if (hci_read_afh_mode(dd, &mode, 1000) < 0) {
+ fprintf(stderr, "Can't read AFH mode on hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ print_dev_hdr(&di);
+ printf("\tAFH mode: %s\n", mode == 1 ? "Enabled" : "Disabled");
+ }
+}
+
+static void cmd_ssp_mode(int ctl, int hdev, char *opt)
+{
+ int dd;
+
+ dd = hci_open_dev(hdev);
+ if (dd < 0) {
+ fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ if (opt) {
+ uint8_t mode = atoi(opt);
+
+ if (hci_write_simple_pairing_mode(dd, mode, 2000) < 0) {
+ fprintf(stderr, "Can't set Simple Pairing mode on hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+ } else {
+ uint8_t mode;
+
+ if (hci_read_simple_pairing_mode(dd, &mode, 1000) < 0) {
+ fprintf(stderr, "Can't read Simple Pairing mode on hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ print_dev_hdr(&di);
+ printf("\tSimple Pairing mode: %s\n",
+ mode == 1 ? "Enabled" : "Disabled");
+ }
+}
+
+static void print_rev_ericsson(int dd)
+{
+ struct hci_request rq;
+ unsigned char buf[102];
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_VENDOR_CMD;
+ rq.ocf = 0x000f;
+ rq.cparam = NULL;
+ rq.clen = 0;
+ rq.rparam = &buf;
+ rq.rlen = sizeof(buf);
+
+ if (hci_send_req(dd, &rq, 1000) < 0) {
+ printf("\nCan't read revision info: %s (%d)\n",
+ strerror(errno), errno);
+ return;
+ }
+
+ printf("\t%s\n", buf + 1);
+}
+
+static void print_rev_csr(int dd, uint16_t rev)
+{
+ uint16_t buildid, chipver, chiprev, maxkeylen, mapsco;
+
+ if (csr_read_varid_uint16(dd, 0, CSR_VARID_BUILDID, &buildid) < 0) {
+ printf("\t%s\n", csr_buildidtostr(rev));
+ return;
+ }
+
+ printf("\t%s\n", csr_buildidtostr(buildid));
+
+ if (!csr_read_varid_uint16(dd, 1, CSR_VARID_CHIPVER, &chipver)) {
+ if (csr_read_varid_uint16(dd, 2, CSR_VARID_CHIPREV, &chiprev) < 0)
+ chiprev = 0;
+ printf("\tChip version: %s\n", csr_chipvertostr(chipver, chiprev));
+ }
+
+ if (!csr_read_varid_uint16(dd, 3, CSR_VARID_MAX_CRYPT_KEY_LENGTH, &maxkeylen))
+ printf("\tMax key size: %d bit\n", maxkeylen * 8);
+
+ if (!csr_read_pskey_uint16(dd, 4, CSR_PSKEY_HOSTIO_MAP_SCO_PCM, 0x0000, &mapsco))
+ printf("\tSCO mapping: %s\n", mapsco ? "PCM" : "HCI");
+}
+
+static void print_rev_digianswer(int dd)
+{
+ struct hci_request rq;
+ unsigned char req[] = { 0x07 };
+ unsigned char buf[102];
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_VENDOR_CMD;
+ rq.ocf = 0x000e;
+ rq.cparam = req;
+ rq.clen = sizeof(req);
+ rq.rparam = &buf;
+ rq.rlen = sizeof(buf);
+
+ if (hci_send_req(dd, &rq, 1000) < 0) {
+ printf("\nCan't read revision info: %s (%d)\n",
+ strerror(errno), errno);
+ return;
+ }
+
+ printf("\t%s\n", buf + 1);
+}
+
+static void print_rev_broadcom(uint16_t hci_rev, uint16_t lmp_subver)
+{
+ printf("\tFirmware %d.%d / %d\n",
+ hci_rev & 0xff, lmp_subver >> 8, lmp_subver & 0xff);
+}
+
+static void print_rev_avm(uint16_t hci_rev, uint16_t lmp_subver)
+{
+ if (lmp_subver == 0x01)
+ printf("\tFirmware 03.%d.%d\n", hci_rev >> 8, hci_rev & 0xff);
+ else
+ printf("\tUnknown type\n");
+}
+
+static void cmd_revision(int ctl, int hdev, char *opt)
+{
+ struct hci_version ver;
+ int dd;
+
+ dd = hci_open_dev(hdev);
+ if (dd < 0) {
+ fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ return;
+ }
+
+ if (hci_read_local_version(dd, &ver, 1000) < 0) {
+ fprintf(stderr, "Can't read version info for hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ return;
+ }
+
+ print_dev_hdr(&di);
+ switch (ver.manufacturer) {
+ case 0:
+ case 37:
+ case 48:
+ print_rev_ericsson(dd);
+ break;
+ case 10:
+ print_rev_csr(dd, ver.hci_rev);
+ break;
+ case 12:
+ print_rev_digianswer(dd);
+ break;
+ case 15:
+ print_rev_broadcom(ver.hci_rev, ver.lmp_subver);
+ break;
+ case 31:
+ print_rev_avm(ver.hci_rev, ver.lmp_subver);
+ break;
+ default:
+ printf("\tUnsupported manufacturer\n");
+ break;
+ }
+ return;
+}
+
+static void cmd_block(int ctl, int hdev, char *opt)
+{
+ bdaddr_t bdaddr;
+ int dd;
+
+ if (!opt)
+ return;
+
+ dd = hci_open_dev(hdev);
+ if (dd < 0) {
+ fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ str2ba(opt, &bdaddr);
+
+ if (ioctl(dd, HCIBLOCKADDR, &bdaddr) < 0) {
+ perror("ioctl(HCIBLOCKADDR)");
+ exit(1);
+ }
+
+ hci_close_dev(dd);
+}
+
+static void cmd_unblock(int ctl, int hdev, char *opt)
+{
+ bdaddr_t bdaddr;
+ int dd;
+
+ if (!opt)
+ return;
+
+ dd = hci_open_dev(hdev);
+ if (dd < 0) {
+ fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ if (!strcasecmp(opt, "all"))
+ bacpy(&bdaddr, BDADDR_ANY);
+ else
+ str2ba(opt, &bdaddr);
+
+ if (ioctl(dd, HCIUNBLOCKADDR, &bdaddr) < 0) {
+ perror("ioctl(HCIUNBLOCKADDR)");
+ exit(1);
+ }
+
+ hci_close_dev(dd);
+}
+
+static void print_dev_hdr(struct hci_dev_info *di)
+{
+ static int hdr = -1;
+ char addr[18];
+
+ if (hdr == di->dev_id)
+ return;
+ hdr = di->dev_id;
+
+ ba2str(&di->bdaddr, addr);
+
+ printf("%s:\tType: %s Bus: %s\n", di->name,
+ hci_typetostr(di->type >> 4),
+ hci_bustostr(di->type & 0x0f));
+ printf("\tBD Address: %s ACL MTU: %d:%d SCO MTU: %d:%d\n",
+ addr, di->acl_mtu, di->acl_pkts,
+ di->sco_mtu, di->sco_pkts);
+}
+
+static void print_dev_info(int ctl, struct hci_dev_info *di)
+{
+ struct hci_dev_stats *st = &di->stat;
+ char *str;
+
+ print_dev_hdr(di);
+
+ str = hci_dflagstostr(di->flags);
+ printf("\t%s\n", str);
+ bt_free(str);
+
+ printf("\tRX bytes:%d acl:%d sco:%d events:%d errors:%d\n",
+ st->byte_rx, st->acl_rx, st->sco_rx, st->evt_rx, st->err_rx);
+
+ printf("\tTX bytes:%d acl:%d sco:%d commands:%d errors:%d\n",
+ st->byte_tx, st->acl_tx, st->sco_tx, st->cmd_tx, st->err_tx);
+
+ if (all && !hci_test_bit(HCI_RAW, &di->flags) &&
+ bacmp(&di->bdaddr, BDADDR_ANY)) {
+ print_dev_features(di, 0);
+ print_pkt_type(di);
+ print_link_policy(di);
+ print_link_mode(di);
+
+ if (hci_test_bit(HCI_UP, &di->flags)) {
+ cmd_name(ctl, di->dev_id, NULL);
+ cmd_class(ctl, di->dev_id, NULL);
+ cmd_version(ctl, di->dev_id, NULL);
+ }
+ }
+
+ printf("\n");
+}
+
+static struct {
+ char *cmd;
+ void (*func)(int ctl, int hdev, char *opt);
+ char *opt;
+ char *doc;
+} command[] = {
+ { "up", cmd_up, 0, "Open and initialize HCI device" },
+ { "down", cmd_down, 0, "Close HCI device" },
+ { "reset", cmd_reset, 0, "Reset HCI device" },
+ { "rstat", cmd_rstat, 0, "Reset statistic counters" },
+ { "auth", cmd_auth, 0, "Enable Authentication" },
+ { "noauth", cmd_auth, 0, "Disable Authentication" },
+ { "encrypt", cmd_encrypt, 0, "Enable Encryption" },
+ { "noencrypt", cmd_encrypt, 0, "Disable Encryption" },
+ { "piscan", cmd_scan, 0, "Enable Page and Inquiry scan" },
+ { "noscan", cmd_scan, 0, "Disable scan" },
+ { "iscan", cmd_scan, 0, "Enable Inquiry scan" },
+ { "pscan", cmd_scan, 0, "Enable Page scan" },
+ { "ptype", cmd_ptype, "[type]", "Get/Set default packet type" },
+ { "lm", cmd_lm, "[mode]", "Get/Set default link mode" },
+ { "lp", cmd_lp, "[policy]", "Get/Set default link policy" },
+ { "name", cmd_name, "[name]", "Get/Set local name" },
+ { "class", cmd_class, "[class]", "Get/Set class of device" },
+ { "voice", cmd_voice, "[voice]", "Get/Set voice setting" },
+ { "iac", cmd_iac, "[iac]", "Get/Set inquiry access code" },
+ { "inqtpl", cmd_inq_tpl, "[level]", "Get/Set inquiry transmit power level" },
+ { "inqmode", cmd_inq_mode, "[mode]", "Get/Set inquiry mode" },
+ { "inqdata", cmd_inq_data, "[data]", "Get/Set inquiry data" },
+ { "inqtype", cmd_inq_type, "[type]", "Get/Set inquiry scan type" },
+ { "inqparms", cmd_inq_parms, "[win:int]", "Get/Set inquiry scan window and interval" },
+ { "pageparms", cmd_page_parms, "[win:int]", "Get/Set page scan window and interval" },
+ { "pageto", cmd_page_to, "[to]", "Get/Set page timeout" },
+ { "afhmode", cmd_afh_mode, "[mode]", "Get/Set AFH mode" },
+ { "sspmode", cmd_ssp_mode, "[mode]", "Get/Set Simple Pairing Mode" },
+ { "aclmtu", cmd_aclmtu, "<mtu:pkt>", "Set ACL MTU and number of packets" },
+ { "scomtu", cmd_scomtu, "<mtu:pkt>", "Set SCO MTU and number of packets" },
+ { "putkey", cmd_putkey, "<bdaddr>", "Store link key on the device" },
+ { "delkey", cmd_delkey, "<bdaddr>", "Delete link key from the device" },
+ { "oobdata", cmd_oob_data, 0, "Display local OOB data" },
+ { "commands", cmd_commands, 0, "Display supported commands" },
+ { "features", cmd_features, 0, "Display device features" },
+ { "version", cmd_version, 0, "Display version information" },
+ { "revision", cmd_revision, 0, "Display revision information" },
+ { "block", cmd_block, "<bdaddr>", "Add a device to the blacklist" },
+ { "unblock", cmd_unblock, "<bdaddr>", "Remove a device from the blacklist" },
+ { "lerandaddr", cmd_le_addr, "<bdaddr>", "Set LE Random Address" },
+ { "leadv", cmd_le_adv, 0, "Enable LE advertising" },
+ { "noleadv", cmd_le_adv, 0, "Disable LE advertising" },
+ { "lestates", cmd_le_states, 0, "Display the supported LE states" },
+ { NULL, NULL, 0 }
+};
+
+static void usage(void)
+{
+ int i;
+
+ printf("hciconfig - HCI device configuration utility\n");
+ printf("Usage:\n"
+ "\thciconfig\n"
+ "\thciconfig [-a] hciX [command ...]\n");
+ printf("Commands:\n");
+ for (i = 0; command[i].cmd; i++)
+ printf("\t%-10s %-8s\t%s\n", command[i].cmd,
+ command[i].opt ? command[i].opt : " ",
+ command[i].doc);
+}
+
+static struct option main_options[] = {
+ { "help", 0, 0, 'h' },
+ { "all", 0, 0, 'a' },
+ { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ int opt, ctl, i, cmd = 0;
+
+ while ((opt = getopt_long(argc, argv, "ah", main_options, NULL)) != -1) {
+ switch (opt) {
+ case 'a':
+ all = 1;
+ break;
+
+ case 'h':
+ default:
+ usage();
+ exit(0);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ /* Open HCI socket */
+ if ((ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI)) < 0) {
+ perror("Can't open HCI socket.");
+ exit(1);
+ }
+
+ if (argc < 1) {
+ print_dev_list(ctl, 0);
+ exit(0);
+ }
+
+ di.dev_id = atoi(argv[0] + 3);
+ argc--; argv++;
+
+ if (ioctl(ctl, HCIGETDEVINFO, (void *) &di)) {
+ perror("Can't get device info");
+ exit(1);
+ }
+
+ if (hci_test_bit(HCI_RAW, &di.flags) &&
+ !bacmp(&di.bdaddr, BDADDR_ANY)) {
+ int dd = hci_open_dev(di.dev_id);
+ hci_read_bd_addr(dd, &di.bdaddr, 1000);
+ hci_close_dev(dd);
+ }
+
+ while (argc > 0) {
+ for (i = 0; command[i].cmd; i++) {
+ if (strncmp(command[i].cmd,
+ *argv, strlen(command[i].cmd)))
+ continue;
+
+ if (command[i].opt) {
+ argc--; argv++;
+ }
+
+ command[i].func(ctl, di.dev_id, *argv);
+ cmd = 1;
+ break;
+ }
+
+ if (command[i].cmd == 0)
+ fprintf(stderr, "Warning: unknown command - \"%s\"\n",
+ *argv);
+
+ argc--; argv++;
+ }
+
+ if (!cmd)
+ print_dev_info(ctl, &di);
+
+ close(ctl);
+ return 0;
+}
diff --git a/tools/hcieventmask.c b/tools/hcieventmask.c
new file mode 100644
index 0000000..87beac9
--- /dev/null
+++ b/tools/hcieventmask.c
@@ -0,0 +1,130 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2010 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/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+static struct option main_options[] = {
+ { "device", 1, 0, 'i' },
+ { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ uint8_t events[8] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00 };
+ struct hci_dev_info di;
+ struct hci_version ver;
+ int dd, opt, dev = 0;
+
+ while ((opt=getopt_long(argc, argv, "+i:", main_options, NULL)) != -1) {
+ switch (opt) {
+ case 'i':
+ dev = hci_devid(optarg);
+ if (dev < 0) {
+ perror("Invalid device");
+ exit(1);
+ }
+ break;
+ }
+ }
+
+ 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);
+ }
+
+ hci_close_dev(dd);
+
+ if (ver.hci_ver > 1) {
+ if (di.features[5] & LMP_SNIFF_SUBR)
+ events[5] |= 0x20;
+
+ if (di.features[5] & LMP_PAUSE_ENC)
+ events[5] |= 0x80;
+
+ if (di.features[6] & LMP_EXT_INQ)
+ events[5] |= 0x40;
+
+ if (di.features[6] & LMP_NFLUSH_PKTS)
+ events[7] |= 0x01;
+
+ if (di.features[7] & LMP_LSTO)
+ events[6] |= 0x80;
+
+ if (di.features[6] & LMP_SIMPLE_PAIR) {
+ events[6] |= 0x01; /* IO Capability Request */
+ events[6] |= 0x02; /* IO Capability Response */
+ events[6] |= 0x04; /* User Confirmation Request */
+ events[6] |= 0x08; /* User Passkey Request */
+ events[6] |= 0x10; /* Remote OOB Data Request */
+ events[6] |= 0x20; /* Simple Pairing Complete */
+ events[7] |= 0x04; /* User Passkey Notification */
+ events[7] |= 0x08; /* Keypress Notification */
+ events[7] |= 0x10; /* Remote Host Supported
+ * Features Notification */
+ }
+
+ if (di.features[4] & LMP_LE)
+ events[7] |= 0x20;
+
+ if (di.features[6] & LMP_LE_BREDR)
+ events[7] |= 0x20;
+ }
+
+ printf("Setting event mask:\n");
+ printf("\thcitool cmd 0x%02x 0x%04x "
+ "0x%02x 0x%02x 0x%02x 0x%02x "
+ "0x%02x 0x%02x 0x%02x 0x%02x\n",
+ OGF_HOST_CTL, OCF_SET_EVENT_MASK,
+ events[0], events[1], events[2], events[3],
+ events[4], events[5], events[6], events[7]);
+
+ return 0;
+}
diff --git a/tools/hcisecfilter.c b/tools/hcisecfilter.c
new file mode 100644
index 0000000..9ad4ce0
--- /dev/null
+++ b/tools/hcisecfilter.c
@@ -0,0 +1,155 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 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 <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+int main(void)
+{
+ uint32_t type_mask;
+ uint32_t event_mask[2];
+ uint32_t ocf_mask[4];
+
+ /* Packet types */
+ memset(&type_mask, 0, sizeof(type_mask));
+ hci_set_bit(HCI_EVENT_PKT, &type_mask);
+
+ printf("Type mask: { 0x%02x }\n", type_mask);
+
+ /* Events */
+ memset(event_mask, 0, sizeof(event_mask));
+ hci_set_bit(EVT_INQUIRY_COMPLETE, event_mask);
+ hci_set_bit(EVT_INQUIRY_RESULT, event_mask);
+ hci_set_bit(EVT_CONN_COMPLETE, event_mask);
+ hci_set_bit(EVT_CONN_REQUEST, event_mask);
+ hci_set_bit(EVT_DISCONN_COMPLETE, event_mask);
+ hci_set_bit(EVT_AUTH_COMPLETE, event_mask);
+ hci_set_bit(EVT_REMOTE_NAME_REQ_COMPLETE, event_mask);
+ hci_set_bit(EVT_ENCRYPT_CHANGE, event_mask);
+ hci_set_bit(EVT_READ_REMOTE_FEATURES_COMPLETE, event_mask);
+ hci_set_bit(EVT_READ_REMOTE_VERSION_COMPLETE, event_mask);
+ hci_set_bit(EVT_CMD_COMPLETE, event_mask);
+ hci_set_bit(EVT_CMD_STATUS, event_mask);
+ hci_set_bit(EVT_READ_CLOCK_OFFSET_COMPLETE, event_mask);
+ hci_set_bit(EVT_INQUIRY_RESULT_WITH_RSSI, event_mask);
+ hci_set_bit(EVT_READ_REMOTE_EXT_FEATURES_COMPLETE, event_mask);
+ hci_set_bit(EVT_SYNC_CONN_COMPLETE, event_mask);
+ hci_set_bit(EVT_SYNC_CONN_CHANGED, event_mask);
+ hci_set_bit(EVT_EXTENDED_INQUIRY_RESULT, event_mask);
+
+ printf("Event mask: { 0x%08x, 0x%08x }\n",
+ event_mask[0], event_mask[1]);
+
+ /* OGF_LINK_CTL */
+ memset(ocf_mask, 0, sizeof(ocf_mask));
+ hci_set_bit(OCF_INQUIRY, ocf_mask);
+ hci_set_bit(OCF_INQUIRY_CANCEL, ocf_mask);
+ hci_set_bit(OCF_REMOTE_NAME_REQ, ocf_mask);
+ hci_set_bit(OCF_REMOTE_NAME_REQ_CANCEL, ocf_mask);
+ hci_set_bit(OCF_READ_REMOTE_FEATURES, ocf_mask);
+ hci_set_bit(OCF_READ_REMOTE_EXT_FEATURES, ocf_mask);
+ hci_set_bit(OCF_READ_REMOTE_VERSION, ocf_mask);
+ hci_set_bit(OCF_READ_CLOCK_OFFSET, ocf_mask);
+ hci_set_bit(OCF_READ_LMP_HANDLE, ocf_mask);
+
+ printf("OGF_LINK_CTL: { 0x%08x, 0x%08x, 0x%08x, 0x%02x }\n",
+ ocf_mask[0], ocf_mask[1], ocf_mask[2], ocf_mask[3]);
+
+ /* OGF_LINK_POLICY */
+ memset(ocf_mask, 0, sizeof(ocf_mask));
+ hci_set_bit(OCF_ROLE_DISCOVERY, ocf_mask);
+ hci_set_bit(OCF_READ_LINK_POLICY, ocf_mask);
+ hci_set_bit(OCF_READ_DEFAULT_LINK_POLICY, ocf_mask);
+
+ printf("OGF_LINK_POLICY: { 0x%08x, 0x%08x, 0x%08x, 0x%02x }\n",
+ ocf_mask[0], ocf_mask[1], ocf_mask[2], ocf_mask[3]);
+
+ /* OGF_HOST_CTL */
+ memset(ocf_mask, 0, sizeof(ocf_mask));
+ hci_set_bit(OCF_READ_PIN_TYPE, ocf_mask);
+ hci_set_bit(OCF_READ_LOCAL_NAME, ocf_mask);
+ hci_set_bit(OCF_READ_CONN_ACCEPT_TIMEOUT, ocf_mask);
+ hci_set_bit(OCF_READ_PAGE_TIMEOUT, ocf_mask);
+ hci_set_bit(OCF_READ_SCAN_ENABLE, ocf_mask);
+ hci_set_bit(OCF_READ_PAGE_ACTIVITY, ocf_mask);
+ hci_set_bit(OCF_READ_INQ_ACTIVITY, ocf_mask);
+ hci_set_bit(OCF_READ_AUTH_ENABLE, ocf_mask);
+ hci_set_bit(OCF_READ_ENCRYPT_MODE, ocf_mask);
+ hci_set_bit(OCF_READ_CLASS_OF_DEV, ocf_mask);
+ hci_set_bit(OCF_READ_VOICE_SETTING, ocf_mask);
+ hci_set_bit(OCF_READ_AUTOMATIC_FLUSH_TIMEOUT, ocf_mask);
+ hci_set_bit(OCF_READ_NUM_BROADCAST_RETRANS, ocf_mask);
+ hci_set_bit(OCF_READ_HOLD_MODE_ACTIVITY, ocf_mask);
+ hci_set_bit(OCF_READ_TRANSMIT_POWER_LEVEL, ocf_mask);
+ hci_set_bit(OCF_READ_LINK_SUPERVISION_TIMEOUT, ocf_mask);
+ hci_set_bit(OCF_READ_NUM_SUPPORTED_IAC, ocf_mask);
+ hci_set_bit(OCF_READ_CURRENT_IAC_LAP, ocf_mask);
+ hci_set_bit(OCF_READ_PAGE_SCAN_PERIOD_MODE, ocf_mask);
+ hci_set_bit(OCF_READ_PAGE_SCAN_MODE, ocf_mask);
+ hci_set_bit(OCF_READ_INQUIRY_SCAN_TYPE, ocf_mask);
+ hci_set_bit(OCF_READ_INQUIRY_MODE, ocf_mask);
+ hci_set_bit(OCF_READ_PAGE_SCAN_TYPE, ocf_mask);
+ hci_set_bit(OCF_READ_AFH_MODE, ocf_mask);
+ hci_set_bit(OCF_READ_EXT_INQUIRY_RESPONSE, ocf_mask);
+ hci_set_bit(OCF_READ_SIMPLE_PAIRING_MODE, ocf_mask);
+ hci_set_bit(OCF_READ_INQ_RESPONSE_TX_POWER_LEVEL, ocf_mask);
+ hci_set_bit(OCF_READ_DEFAULT_ERROR_DATA_REPORTING, ocf_mask);
+
+ printf("OGF_HOST_CTL: { 0x%08x, 0x%08x, 0x%08x, 0x%02x }\n",
+ ocf_mask[0], ocf_mask[1], ocf_mask[2], ocf_mask[3]);
+
+ /* OGF_INFO_PARAM */
+ memset(ocf_mask, 0, sizeof(ocf_mask));
+ hci_set_bit(OCF_READ_LOCAL_VERSION, ocf_mask);
+ hci_set_bit(OCF_READ_LOCAL_COMMANDS, ocf_mask);
+ hci_set_bit(OCF_READ_LOCAL_FEATURES, ocf_mask);
+ hci_set_bit(OCF_READ_LOCAL_EXT_FEATURES, ocf_mask);
+ hci_set_bit(OCF_READ_BUFFER_SIZE, ocf_mask);
+ hci_set_bit(OCF_READ_COUNTRY_CODE, ocf_mask);
+ hci_set_bit(OCF_READ_BD_ADDR, ocf_mask);
+
+ printf("OGF_INFO_PARAM: { 0x%08x, 0x%08x, 0x%08x, 0x%02x }\n",
+ ocf_mask[0], ocf_mask[1], ocf_mask[2], ocf_mask[3]);
+
+ /* OGF_STATUS_PARAM */
+ memset(ocf_mask, 0, sizeof(ocf_mask));
+ hci_set_bit(OCF_READ_FAILED_CONTACT_COUNTER, ocf_mask);
+ hci_set_bit(OCF_READ_LINK_QUALITY, ocf_mask);
+ hci_set_bit(OCF_READ_RSSI, ocf_mask);
+ hci_set_bit(OCF_READ_AFH_MAP, ocf_mask);
+ hci_set_bit(OCF_READ_CLOCK, ocf_mask);
+
+ printf("OGF_STATUS_PARAM: { 0x%08x, 0x%08x, 0x%08x, 0x%02x }\n",
+ ocf_mask[0], ocf_mask[1], ocf_mask[2], ocf_mask[3]);
+
+ return 0;
+}
diff --git a/tools/hcitool.1 b/tools/hcitool.1
new file mode 100644
index 0000000..85498dc
--- /dev/null
+++ b/tools/hcitool.1
@@ -0,0 +1,209 @@
+.TH HCITOOL 1 "Nov 12 2002" BlueZ "Linux System Administration"
+.SH NAME
+hcitool \- configure Bluetooth connections
+.SH SYNOPSIS
+.B hcitool [-h]
+.br
+.B hcitool [-i <hciX>] [command [command parameters]]
+
+.SH DESCRIPTION
+.LP
+.B
+hcitool
+is used to configure Bluetooth connections and send some special command to
+Bluetooth devices. If no
+.B
+command
+is given, or if the option
+.B
+-h
+is used,
+.B
+hcitool
+prints some usage information and exits.
+.SH OPTIONS
+.TP
+.BI -h
+Gives a list of possible commands
+.TP
+.BI -i " <hciX>"
+The command is applied to device
+.I
+hciX
+, which must be the name of an installed Bluetooth device. If not specified,
+the command will be sent to the first available Bluetooth device.
+.SH COMMANDS
+.TP
+.BI dev
+Display local devices
+.TP
+.BI inq
+Inquire remote devices. For each discovered device, Bluetooth device address,
+clock offset and class are printed.
+.TP
+.BI scan
+Inquire remote devices. For each discovered device, device name are printed.
+.TP
+.BI name " <bdaddr>"
+Print device name of remote device with Bluetooth address
+.IR bdaddr .
+.TP
+.BI info " <bdaddr>"
+Print device name, version and supported features of remote device with
+Bluetooth address
+.IR bdaddr .
+.TP
+.BI spinq
+Start periodic inquiry process. No inquiry results are printed.
+.TP
+.BI epinq
+Exit periodic inquiry process.
+.TP
+.BI cmd " <ogf> <ocf> [parameters]"
+Submit an arbitrary HCI command to local device.
+.IR ogf ,
+.IR ocf
+and
+.IR parameters
+are hexadecimal bytes.
+.TP
+.BI con
+Display active baseband connections
+.TP
+.BI cc " [--role=m|s] [--pkt-type=<ptype>] <bdaddr>"
+Create baseband connection to remote device with Bluetooth address
+.IR bdaddr .
+Option
+.I
+--pkt-type
+specifies a list of allowed packet types.
+.I
+<ptype>
+is a comma-separated list of packet types, where the possible packet types are
+.BR DM1 ,
+.BR DM3 ,
+.BR DM5 ,
+.BR DH1 ,
+.BR DH3 ,
+.BR DH5 ,
+.BR HV1 ,
+.BR HV2 ,
+.BR HV3 .
+Default is to allow all packet types. Option
+.I
+--role
+can have value
+.I
+m
+(do not allow role switch, stay master) or
+.I
+s
+(allow role switch, become slave if the peer asks to become master). Default is
+.IR m .
+.TP
+.BI dc " <bdaddr> [reason]"
+Delete baseband connection from remote device with Bluetooth address
+.IR bdaddr .
+The reason can be one of the Bluetooth HCI error codes. Default is
+.IR 19
+for user ended connections. The value must be given in decimal.
+.TP
+.BI sr " <bdaddr> <role>"
+Switch role for the baseband connection from the remote device to
+.BR master
+or
+.BR slave .
+.TP
+.BI cpt " <bdaddr> <packet types>"
+Change packet types for baseband connection to device with Bluetooth address
+.IR bdaddr .
+.I
+packet types
+is a comma-separated list of packet types, where the possible packet types are
+.BR DM1 ,
+.BR DM3 ,
+.BR DM5 ,
+.BR DH1 ,
+.BR DH3 ,
+.BR DH5 ,
+.BR HV1 ,
+.BR HV2 ,
+.BR HV3 .
+.TP
+.BI rssi " <bdaddr>"
+Display received signal strength information for the connection to the device
+with Bluetooth address
+.IR bdaddr .
+.TP
+.BI lq " <bdaddr>"
+Display link quality for the connection to the device with Bluetooth address
+.IR bdaddr .
+.TP
+.BI tpl " <bdaddr> [type]"
+Display transmit power level for the connection to the device with Bluetooth address
+.IR bdaddr .
+The type can be
+.BR 0
+for the current transmit power level (which is default) or
+.BR 1
+for the maximum transmit power level.
+.TP
+.BI afh " <bdaddr>"
+Display AFH channel map for the connection to the device with Bluetooth address
+.IR bdaddr .
+.TP
+.BI lp " <bdaddr> [value]"
+With no
+.IR value ,
+displays link policy settings for the connection to the device with Bluetooth address
+.IR bdaddr .
+If
+.IR value
+is given, sets the link policy settings for that connection to
+.IR value .
+Possible values are RSWITCH, HOLD, SNIFF and PARK.
+.TP
+.BI lst " <bdaddr> [value]"
+With no
+.IR value ,
+displays link supervision timeout for the connection to the device with Bluetooth address
+.IR bdaddr .
+If
+.I
+value
+is given, sets the link supervision timeout for that connection to
+.I
+value
+slots, or to infinite if
+.I
+value
+is 0.
+.TP
+.BI auth " <bdaddr>"
+Request authentication for the device with Bluetooth address
+.IR bdaddr .
+.TP
+.BI enc " <bdaddr> [encrypt enable]"
+Enable or disable the encryption for the device with Bluetooth address
+.IR bdaddr .
+.TP
+.BI key " <bdaddr>"
+Change the connection link key for the device with Bluetooth address
+.IR bdaddr .
+.TP
+.BI clkoff " <bdaddr>"
+Read the clock offset for the device with Bluetooth address
+.IR bdaddr .
+.TP
+.BI clock " [bdaddr] [which clock]"
+Read the clock for the device with Bluetooth address
+.IR bdaddr .
+The clock can be
+.BR 0
+for the local clock or
+.BR 1
+for the piconet clock (which is default).
+.SH AUTHORS
+Written by Maxim Krasnyansky <maxk@qualcomm.com> and Marcel Holtmann <marcel@holtmann.org>
+.PP
+man page by Fabrizio Gennari <fabrizio.gennari@philips.com>
diff --git a/tools/hcitool.c b/tools/hcitool.c
new file mode 100644
index 0000000..a117449
--- /dev/null
+++ b/tools/hcitool.c
@@ -0,0 +1,3007 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2000-2001 Qualcomm Incorporated
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 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 <string.h>
+#include <getopt.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "textfile.h"
+#include "oui.h"
+
+/* Unofficial value, might still change */
+#define LE_LINK 0x03
+
+#define FLAGS_AD_TYPE 0x01
+#define FLAGS_LIMITED_MODE_BIT 0x01
+#define FLAGS_GENERAL_MODE_BIT 0x02
+
+#define for_each_opt(opt, long, short) while ((opt=getopt_long(argc, argv, short ? short:"+", long, NULL)) != -1)
+
+static void usage(void);
+
+static int dev_info(int s, int dev_id, long arg)
+{
+ struct hci_dev_info di = { dev_id: dev_id };
+ char addr[18];
+
+ if (ioctl(s, HCIGETDEVINFO, (void *) &di))
+ return 0;
+
+ ba2str(&di.bdaddr, addr);
+ printf("\t%s\t%s\n", di.name, addr);
+ return 0;
+}
+
+static void helper_arg(int min_num_arg, int max_num_arg, int *argc,
+ char ***argv, const char *usage)
+{
+ *argc -= optind;
+ /* too many arguments, but when "max_num_arg < min_num_arg" then no
+ limiting (prefer "max_num_arg=-1" to gen infinity)
+ */
+ if ( (*argc > max_num_arg) && (max_num_arg >= min_num_arg ) ) {
+ fprintf(stderr, "%s: too many arguments (maximal: %i)\n",
+ *argv[0], max_num_arg);
+ printf("%s", usage);
+ exit(1);
+ }
+
+ /* print usage */
+ if (*argc < min_num_arg) {
+ fprintf(stderr, "%s: too few arguments (minimal: %i)\n",
+ *argv[0], min_num_arg);
+ printf("%s", usage);
+ exit(0);
+ }
+
+ *argv += optind;
+}
+
+static char *type2str(uint8_t type)
+{
+ switch (type) {
+ case SCO_LINK:
+ return "SCO";
+ case ACL_LINK:
+ return "ACL";
+ case ESCO_LINK:
+ return "eSCO";
+ case LE_LINK:
+ return "LE";
+ default:
+ return "Unknown";
+ }
+}
+
+static int conn_list(int s, int dev_id, long arg)
+{
+ struct hci_conn_list_req *cl;
+ struct hci_conn_info *ci;
+ int id = arg;
+ int i;
+
+ if (id != -1 && dev_id != id)
+ return 0;
+
+ if (!(cl = malloc(10 * sizeof(*ci) + sizeof(*cl)))) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+ cl->dev_id = dev_id;
+ cl->conn_num = 10;
+ ci = cl->conn_info;
+
+ if (ioctl(s, HCIGETCONNLIST, (void *) cl)) {
+ perror("Can't get connection list");
+ exit(1);
+ }
+
+ for (i = 0; i < cl->conn_num; i++, ci++) {
+ char addr[18];
+ char *str;
+ ba2str(&ci->bdaddr, addr);
+ str = hci_lmtostr(ci->link_mode);
+ printf("\t%s %s %s handle %d state %d lm %s\n",
+ ci->out ? "<" : ">", type2str(ci->type),
+ addr, ci->handle, ci->state, str);
+ bt_free(str);
+ }
+
+ free(cl);
+ return 0;
+}
+
+static int find_conn(int s, int dev_id, long arg)
+{
+ struct hci_conn_list_req *cl;
+ struct hci_conn_info *ci;
+ int i;
+
+ if (!(cl = malloc(10 * sizeof(*ci) + sizeof(*cl)))) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+ cl->dev_id = dev_id;
+ cl->conn_num = 10;
+ ci = cl->conn_info;
+
+ if (ioctl(s, HCIGETCONNLIST, (void *) cl)) {
+ perror("Can't get connection list");
+ exit(1);
+ }
+
+ for (i = 0; i < cl->conn_num; i++, ci++)
+ if (!bacmp((bdaddr_t *) arg, &ci->bdaddr)) {
+ free(cl);
+ return 1;
+ }
+
+ free(cl);
+ return 0;
+}
+
+static void hex_dump(char *pref, int width, unsigned char *buf, int len)
+{
+ register int i,n;
+
+ for (i = 0, n = 1; i < len; i++, n++) {
+ if (n == 1)
+ printf("%s", pref);
+ printf("%2.2X ", buf[i]);
+ if (n == width) {
+ printf("\n");
+ n = 0;
+ }
+ }
+ if (i && n!=1)
+ printf("\n");
+}
+
+static char *get_minor_device_name(int major, int minor)
+{
+ switch (major) {
+ case 0: /* misc */
+ return "";
+ case 1: /* computer */
+ switch (minor) {
+ case 0:
+ return "Uncategorized";
+ case 1:
+ return "Desktop workstation";
+ case 2:
+ return "Server";
+ case 3:
+ return "Laptop";
+ case 4:
+ return "Handheld";
+ case 5:
+ return "Palm";
+ case 6:
+ return "Wearable";
+ }
+ break;
+ case 2: /* phone */
+ switch (minor) {
+ case 0:
+ return "Uncategorized";
+ case 1:
+ return "Cellular";
+ case 2:
+ return "Cordless";
+ case 3:
+ return "Smart phone";
+ case 4:
+ return "Wired modem or voice gateway";
+ case 5:
+ return "Common ISDN Access";
+ case 6:
+ return "Sim Card Reader";
+ }
+ break;
+ case 3: /* lan access */
+ if (minor == 0)
+ return "Uncategorized";
+ switch (minor / 8) {
+ case 0:
+ return "Fully available";
+ case 1:
+ return "1-17% utilized";
+ case 2:
+ return "17-33% utilized";
+ case 3:
+ return "33-50% utilized";
+ case 4:
+ return "50-67% utilized";
+ case 5:
+ return "67-83% utilized";
+ case 6:
+ return "83-99% utilized";
+ case 7:
+ return "No service available";
+ }
+ break;
+ case 4: /* audio/video */
+ switch (minor) {
+ case 0:
+ return "Uncategorized";
+ case 1:
+ return "Device conforms to the Headset profile";
+ case 2:
+ return "Hands-free";
+ /* 3 is reserved */
+ case 4:
+ return "Microphone";
+ case 5:
+ return "Loudspeaker";
+ case 6:
+ return "Headphones";
+ case 7:
+ return "Portable Audio";
+ case 8:
+ return "Car Audio";
+ case 9:
+ return "Set-top box";
+ case 10:
+ return "HiFi Audio Device";
+ case 11:
+ return "VCR";
+ case 12:
+ return "Video Camera";
+ case 13:
+ return "Camcorder";
+ case 14:
+ return "Video Monitor";
+ case 15:
+ return "Video Display and Loudspeaker";
+ case 16:
+ return "Video Conferencing";
+ /* 17 is reserved */
+ case 18:
+ return "Gaming/Toy";
+ }
+ break;
+ case 5: /* peripheral */ {
+ static char cls_str[48]; cls_str[0] = 0;
+
+ switch (minor & 48) {
+ case 16:
+ strncpy(cls_str, "Keyboard", sizeof(cls_str));
+ break;
+ case 32:
+ strncpy(cls_str, "Pointing device", sizeof(cls_str));
+ break;
+ case 48:
+ strncpy(cls_str, "Combo keyboard/pointing device", sizeof(cls_str));
+ break;
+ }
+ if ((minor & 15) && (strlen(cls_str) > 0))
+ strcat(cls_str, "/");
+
+ switch (minor & 15) {
+ case 0:
+ break;
+ case 1:
+ strncat(cls_str, "Joystick", sizeof(cls_str) - strlen(cls_str));
+ break;
+ case 2:
+ strncat(cls_str, "Gamepad", sizeof(cls_str) - strlen(cls_str));
+ break;
+ case 3:
+ strncat(cls_str, "Remote control", sizeof(cls_str) - strlen(cls_str));
+ break;
+ case 4:
+ strncat(cls_str, "Sensing device", sizeof(cls_str) - strlen(cls_str));
+ break;
+ case 5:
+ strncat(cls_str, "Digitizer tablet", sizeof(cls_str) - strlen(cls_str));
+ break;
+ case 6:
+ strncat(cls_str, "Card reader", sizeof(cls_str) - strlen(cls_str));
+ break;
+ default:
+ strncat(cls_str, "(reserved)", sizeof(cls_str) - strlen(cls_str));
+ break;
+ }
+ if (strlen(cls_str) > 0)
+ return cls_str;
+ }
+ case 6: /* imaging */
+ if (minor & 4)
+ return "Display";
+ if (minor & 8)
+ return "Camera";
+ if (minor & 16)
+ return "Scanner";
+ if (minor & 32)
+ return "Printer";
+ break;
+ case 7: /* wearable */
+ switch (minor) {
+ case 1:
+ return "Wrist Watch";
+ case 2:
+ return "Pager";
+ case 3:
+ return "Jacket";
+ case 4:
+ return "Helmet";
+ case 5:
+ return "Glasses";
+ }
+ break;
+ case 8: /* toy */
+ switch (minor) {
+ case 1:
+ return "Robot";
+ case 2:
+ return "Vehicle";
+ case 3:
+ return "Doll / Action Figure";
+ case 4:
+ return "Controller";
+ case 5:
+ return "Game";
+ }
+ break;
+ case 63: /* uncategorised */
+ return "";
+ }
+ return "Unknown (reserved) minor device class";
+}
+
+static char *major_classes[] = {
+ "Miscellaneous", "Computer", "Phone", "LAN Access",
+ "Audio/Video", "Peripheral", "Imaging", "Uncategorized"
+};
+
+static char *get_device_name(const bdaddr_t *local, const bdaddr_t *peer)
+{
+ char filename[PATH_MAX + 1], addr[18];
+
+ ba2str(local, addr);
+ create_name(filename, PATH_MAX, STORAGEDIR, addr, "names");
+
+ ba2str(peer, addr);
+ return textfile_get(filename, addr);
+}
+
+/* Display local devices */
+
+static struct option dev_options[] = {
+ { "help", 0, 0, 'h' },
+ {0, 0, 0, 0 }
+};
+
+static const char *dev_help =
+ "Usage:\n"
+ "\tdev\n";
+
+static void cmd_dev(int dev_id, int argc, char **argv)
+{
+ int opt;
+
+ for_each_opt(opt, dev_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", dev_help);
+ return;
+ }
+ }
+ helper_arg(0, 0, &argc, &argv, dev_help);
+
+ printf("Devices:\n");
+
+ hci_for_each_dev(HCI_UP, dev_info, 0);
+}
+
+/* Inquiry */
+
+static struct option inq_options[] = {
+ { "help", 0, 0, 'h' },
+ { "length", 1, 0, 'l' },
+ { "numrsp", 1, 0, 'n' },
+ { "iac", 1, 0, 'i' },
+ { "flush", 0, 0, 'f' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *inq_help =
+ "Usage:\n"
+ "\tinq [--length=N] maximum inquiry duration in 1.28 s units\n"
+ "\t [--numrsp=N] specify maximum number of inquiry responses\n"
+ "\t [--iac=lap] specify the inquiry access code\n"
+ "\t [--flush] flush the inquiry cache\n";
+
+static void cmd_inq(int dev_id, int argc, char **argv)
+{
+ inquiry_info *info = NULL;
+ uint8_t lap[3] = { 0x33, 0x8b, 0x9e };
+ int num_rsp, length, flags;
+ char addr[18];
+ int i, l, opt;
+
+ length = 8; /* ~10 seconds */
+ num_rsp = 0;
+ flags = 0;
+
+ for_each_opt(opt, inq_options, NULL) {
+ switch (opt) {
+ case 'l':
+ length = atoi(optarg);
+ break;
+
+ case 'n':
+ num_rsp = atoi(optarg);
+ break;
+
+ case 'i':
+ l = strtoul(optarg, 0, 16);
+ if (!strcasecmp(optarg, "giac")) {
+ l = 0x9e8b33;
+ } else if (!strcasecmp(optarg, "liac")) {
+ l = 0x9e8b00;
+ } if (l < 0x9e8b00 || l > 0x9e8b3f) {
+ printf("Invalid access code 0x%x\n", l);
+ exit(1);
+ }
+ lap[0] = (l & 0xff);
+ lap[1] = (l >> 8) & 0xff;
+ lap[2] = (l >> 16) & 0xff;
+ break;
+
+ case 'f':
+ flags |= IREQ_CACHE_FLUSH;
+ break;
+
+ default:
+ printf("%s", inq_help);
+ return;
+ }
+ }
+ helper_arg(0, 0, &argc, &argv, inq_help);
+
+ printf("Inquiring ...\n");
+
+ num_rsp = hci_inquiry(dev_id, length, num_rsp, lap, &info, flags);
+ if (num_rsp < 0) {
+ perror("Inquiry failed.");
+ exit(1);
+ }
+
+ for (i = 0; i < num_rsp; i++) {
+ ba2str(&(info+i)->bdaddr, addr);
+ printf("\t%s\tclock offset: 0x%4.4x\tclass: 0x%2.2x%2.2x%2.2x\n",
+ addr, btohs((info+i)->clock_offset),
+ (info+i)->dev_class[2],
+ (info+i)->dev_class[1],
+ (info+i)->dev_class[0]);
+ }
+
+ bt_free(info);
+}
+
+/* Device scanning */
+
+static struct option scan_options[] = {
+ { "help", 0, 0, 'h' },
+ { "length", 1, 0, 'l' },
+ { "numrsp", 1, 0, 'n' },
+ { "iac", 1, 0, 'i' },
+ { "flush", 0, 0, 'f' },
+ { "refresh", 0, 0, 'r' },
+ { "class", 0, 0, 'C' },
+ { "info", 0, 0, 'I' },
+ { "oui", 0, 0, 'O' },
+ { "all", 0, 0, 'A' },
+ { "ext", 0, 0, 'A' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *scan_help =
+ "Usage:\n"
+ "\tscan [--length=N] [--numrsp=N] [--iac=lap] [--flush] [--class] [--info] [--oui] [--refresh]\n";
+
+static void cmd_scan(int dev_id, int argc, char **argv)
+{
+ inquiry_info *info = NULL;
+ uint8_t lap[3] = { 0x33, 0x8b, 0x9e };
+ int num_rsp, length, flags;
+ uint8_t cls[3], features[8];
+ char addr[18], name[249], oui[9], *comp, *tmp;
+ struct hci_version version;
+ struct hci_dev_info di;
+ struct hci_conn_info_req *cr;
+ int refresh = 0, extcls = 0, extinf = 0, extoui = 0;
+ int i, n, l, opt, dd, cc, nc;
+
+ length = 8; /* ~10 seconds */
+ num_rsp = 0;
+ flags = 0;
+
+ for_each_opt(opt, scan_options, NULL) {
+ switch (opt) {
+ case 'l':
+ length = atoi(optarg);
+ break;
+
+ case 'n':
+ num_rsp = atoi(optarg);
+ break;
+
+ case 'i':
+ l = strtoul(optarg, 0, 16);
+ if (!strcasecmp(optarg, "giac")) {
+ l = 0x9e8b33;
+ } else if (!strcasecmp(optarg, "liac")) {
+ l = 0x9e8b00;
+ } else if (l < 0x9e8b00 || l > 0x9e8b3f) {
+ printf("Invalid access code 0x%x\n", l);
+ exit(1);
+ }
+ lap[0] = (l & 0xff);
+ lap[1] = (l >> 8) & 0xff;
+ lap[2] = (l >> 16) & 0xff;
+ break;
+
+ case 'f':
+ flags |= IREQ_CACHE_FLUSH;
+ break;
+
+ case 'r':
+ refresh = 1;
+ break;
+
+ case 'C':
+ extcls = 1;
+ break;
+
+ case 'I':
+ extinf = 1;
+ break;
+
+ case 'O':
+ extoui = 1;
+ break;
+
+ case 'A':
+ extcls = 1;
+ extinf = 1;
+ extoui = 1;
+ break;
+
+ default:
+ printf("%s", scan_help);
+ return;
+ }
+ }
+ helper_arg(0, 0, &argc, &argv, scan_help);
+
+ if (dev_id < 0) {
+ dev_id = hci_get_route(NULL);
+ if (dev_id < 0) {
+ perror("Device is not available");
+ exit(1);
+ }
+ }
+
+ if (hci_devinfo(dev_id, &di) < 0) {
+ perror("Can't get device info");
+ exit(1);
+ }
+
+ printf("Scanning ...\n");
+ num_rsp = hci_inquiry(dev_id, length, num_rsp, lap, &info, flags);
+ if (num_rsp < 0) {
+ perror("Inquiry failed");
+ exit(1);
+ }
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ free(info);
+ exit(1);
+ }
+
+ if (extcls || extinf || extoui)
+ printf("\n");
+
+ for (i = 0; i < num_rsp; i++) {
+ uint16_t handle = 0;
+
+ if (!refresh) {
+ memset(name, 0, sizeof(name));
+ tmp = get_device_name(&di.bdaddr, &(info+i)->bdaddr);
+ if (tmp) {
+ strncpy(name, tmp, 249);
+ free(tmp);
+ nc = 1;
+ } else
+ nc = 0;
+ } else
+ nc = 0;
+
+ if (!extcls && !extinf && !extoui) {
+ ba2str(&(info+i)->bdaddr, addr);
+
+ if (nc) {
+ printf("\t%s\t%s\n", addr, name);
+ continue;
+ }
+
+ if (hci_read_remote_name_with_clock_offset(dd,
+ &(info+i)->bdaddr,
+ (info+i)->pscan_rep_mode,
+ (info+i)->clock_offset | 0x8000,
+ sizeof(name), name, 100000) < 0)
+ strcpy(name, "n/a");
+
+ for (n = 0; n < 248 && name[n]; n++) {
+ if ((unsigned char) name[i] < 32 || name[i] == 127)
+ name[i] = '.';
+ }
+
+ name[248] = '\0';
+
+ printf("\t%s\t%s\n", addr, name);
+ continue;
+ }
+
+ ba2str(&(info+i)->bdaddr, addr);
+ printf("BD Address:\t%s [mode %d, clkoffset 0x%4.4x]\n", addr,
+ (info+i)->pscan_rep_mode, btohs((info+i)->clock_offset));
+
+ if (extoui) {
+ ba2oui(&(info+i)->bdaddr, oui);
+ comp = ouitocomp(oui);
+ if (comp) {
+ printf("OUI company:\t%s (%s)\n", comp, oui);
+ free(comp);
+ }
+ }
+
+ cc = 0;
+
+ if (extinf) {
+ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+ if (cr) {
+ bacpy(&cr->bdaddr, &(info+i)->bdaddr);
+ cr->type = ACL_LINK;
+ if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+ handle = 0;
+ cc = 1;
+ } else {
+ handle = htobs(cr->conn_info->handle);
+ cc = 0;
+ }
+ free(cr);
+ }
+
+ if (cc) {
+ if (hci_create_connection(dd, &(info+i)->bdaddr,
+ htobs(di.pkt_type & ACL_PTYPE_MASK),
+ (info+i)->clock_offset | 0x8000,
+ 0x01, &handle, 25000) < 0) {
+ handle = 0;
+ cc = 0;
+ }
+ }
+ }
+
+ if (handle > 0 || !nc) {
+ if (hci_read_remote_name_with_clock_offset(dd,
+ &(info+i)->bdaddr,
+ (info+i)->pscan_rep_mode,
+ (info+i)->clock_offset | 0x8000,
+ sizeof(name), name, 100000) < 0) {
+ if (!nc)
+ strcpy(name, "n/a");
+ } else {
+ for (n = 0; n < 248 && name[n]; n++) {
+ if ((unsigned char) name[i] < 32 || name[i] == 127)
+ name[i] = '.';
+ }
+
+ name[248] = '\0';
+ nc = 0;
+ }
+ }
+
+ if (strlen(name) > 0)
+ printf("Device name:\t%s%s\n", name, nc ? " [cached]" : "");
+
+ if (extcls) {
+ memcpy(cls, (info+i)->dev_class, 3);
+ printf("Device class:\t");
+ if ((cls[1] & 0x1f) > sizeof(major_classes) / sizeof(char *))
+ printf("Invalid");
+ else
+ printf("%s, %s", major_classes[cls[1] & 0x1f],
+ get_minor_device_name(cls[1] & 0x1f, cls[0] >> 2));
+ printf(" (0x%2.2x%2.2x%2.2x)\n", cls[2], cls[1], cls[0]);
+ }
+
+ if (extinf && handle > 0) {
+ if (hci_read_remote_version(dd, handle, &version, 20000) == 0) {
+ char *ver = lmp_vertostr(version.lmp_ver);
+ printf("Manufacturer:\t%s (%d)\n",
+ bt_compidtostr(version.manufacturer),
+ version.manufacturer);
+ printf("LMP version:\t%s (0x%x) [subver 0x%x]\n",
+ ver ? ver : "n/a",
+ version.lmp_ver, version.lmp_subver);
+ if (ver)
+ bt_free(ver);
+ }
+
+ if (hci_read_remote_features(dd, handle, features, 20000) == 0) {
+ char *tmp = lmp_featurestostr(features, "\t\t", 63);
+ printf("LMP features:\t0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x"
+ " 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n",
+ features[0], features[1],
+ features[2], features[3],
+ features[4], features[5],
+ features[6], features[7]);
+ printf("%s\n", tmp);
+ bt_free(tmp);
+ }
+
+ if (cc) {
+ usleep(10000);
+ hci_disconnect(dd, handle, HCI_OE_USER_ENDED_CONNECTION, 10000);
+ }
+ }
+
+ printf("\n");
+ }
+
+ bt_free(info);
+
+ hci_close_dev(dd);
+}
+
+/* Remote name */
+
+static struct option name_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *name_help =
+ "Usage:\n"
+ "\tname <bdaddr>\n";
+
+static void cmd_name(int dev_id, int argc, char **argv)
+{
+ bdaddr_t bdaddr;
+ char name[248];
+ int opt, dd;
+
+ for_each_opt(opt, name_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", name_help);
+ return;
+ }
+ }
+ helper_arg(1, 1, &argc, &argv, name_help);
+
+ str2ba(argv[0], &bdaddr);
+
+ if (dev_id < 0) {
+ dev_id = hci_get_route(&bdaddr);
+ if (dev_id < 0) {
+ fprintf(stderr, "Device is not available.\n");
+ exit(1);
+ }
+ }
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ exit(1);
+ }
+
+ if (hci_read_remote_name(dd, &bdaddr, sizeof(name), name, 25000) == 0)
+ printf("%s\n", name);
+
+ hci_close_dev(dd);
+}
+
+/* Info about remote device */
+
+static struct option info_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *info_help =
+ "Usage:\n"
+ "\tinfo <bdaddr>\n";
+
+static void cmd_info(int dev_id, int argc, char **argv)
+{
+ bdaddr_t bdaddr;
+ uint16_t handle;
+ uint8_t features[8], max_page = 0;
+ char name[249], oui[9], *comp, *tmp;
+ struct hci_version version;
+ struct hci_dev_info di;
+ struct hci_conn_info_req *cr;
+ int i, opt, dd, cc = 0;
+
+ for_each_opt(opt, info_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", info_help);
+ return;
+ }
+ }
+ helper_arg(1, 1, &argc, &argv, info_help);
+
+ str2ba(argv[0], &bdaddr);
+
+ if (dev_id < 0)
+ dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+
+ if (dev_id < 0)
+ dev_id = hci_get_route(&bdaddr);
+
+ if (dev_id < 0) {
+ fprintf(stderr, "Device is not available or not connected.\n");
+ exit(1);
+ }
+
+ if (hci_devinfo(dev_id, &di) < 0) {
+ perror("Can't get device info");
+ exit(1);
+ }
+
+ printf("Requesting information ...\n");
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ exit(1);
+ }
+
+ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+ if (!cr) {
+ perror("Can't get connection info");
+ close(dd);
+ exit(1);
+ }
+
+ bacpy(&cr->bdaddr, &bdaddr);
+ cr->type = ACL_LINK;
+ if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+ if (hci_create_connection(dd, &bdaddr,
+ htobs(di.pkt_type & ACL_PTYPE_MASK),
+ 0, 0x01, &handle, 25000) < 0) {
+ perror("Can't create connection");
+ close(dd);
+ exit(1);
+ }
+ sleep(1);
+ cc = 1;
+ } else
+ handle = htobs(cr->conn_info->handle);
+
+ printf("\tBD Address: %s\n", argv[0]);
+
+ ba2oui(&bdaddr, oui);
+ comp = ouitocomp(oui);
+ if (comp) {
+ printf("\tOUI Company: %s (%s)\n", comp, oui);
+ free(comp);
+ }
+
+ if (hci_read_remote_name(dd, &bdaddr, sizeof(name), name, 25000) == 0)
+ printf("\tDevice Name: %s\n", name);
+
+ if (hci_read_remote_version(dd, handle, &version, 20000) == 0) {
+ char *ver = lmp_vertostr(version.lmp_ver);
+ printf("\tLMP Version: %s (0x%x) LMP Subversion: 0x%x\n"
+ "\tManufacturer: %s (%d)\n",
+ ver ? ver : "n/a",
+ version.lmp_ver,
+ version.lmp_subver,
+ bt_compidtostr(version.manufacturer),
+ version.manufacturer);
+ if (ver)
+ bt_free(ver);
+ }
+
+ memset(features, 0, sizeof(features));
+ hci_read_remote_features(dd, handle, features, 20000);
+
+ if ((di.features[7] & LMP_EXT_FEAT) && (features[7] & LMP_EXT_FEAT))
+ hci_read_remote_ext_features(dd, handle, 0, &max_page,
+ features, 20000);
+
+ printf("\tFeatures%s: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
+ "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n",
+ (max_page > 0) ? " page 0" : "",
+ features[0], features[1], features[2], features[3],
+ features[4], features[5], features[6], features[7]);
+
+ tmp = lmp_featurestostr(features, "\t\t", 63);
+ printf("%s\n", tmp);
+ bt_free(tmp);
+
+ for (i = 1; i <= max_page; i++) {
+ if (hci_read_remote_ext_features(dd, handle, i, NULL,
+ features, 20000) < 0)
+ continue;
+
+ printf("\tFeatures page %d: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
+ "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n", i,
+ features[0], features[1], features[2], features[3],
+ features[4], features[5], features[6], features[7]);
+ }
+
+ if (cc) {
+ usleep(10000);
+ hci_disconnect(dd, handle, HCI_OE_USER_ENDED_CONNECTION, 10000);
+ }
+
+ hci_close_dev(dd);
+}
+
+/* Start periodic inquiry */
+
+static struct option spinq_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *spinq_help =
+ "Usage:\n"
+ "\tspinq\n";
+
+static void cmd_spinq(int dev_id, int argc, char **argv)
+{
+ uint8_t lap[3] = { 0x33, 0x8b, 0x9e };
+ struct hci_request rq;
+ periodic_inquiry_cp cp;
+ int opt, dd;
+
+ for_each_opt(opt, spinq_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", spinq_help);
+ return;
+ }
+ }
+ helper_arg(0, 0, &argc, &argv, spinq_help);
+
+ if (dev_id < 0)
+ dev_id = hci_get_route(NULL);
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("Device open failed");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(&cp, 0, sizeof(cp));
+ memcpy(cp.lap, lap, 3);
+ cp.max_period = htobs(16);
+ cp.min_period = htobs(10);
+ cp.length = 8;
+ cp.num_rsp = 0;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_LINK_CTL;
+ rq.ocf = OCF_PERIODIC_INQUIRY;
+ rq.cparam = &cp;
+ rq.clen = PERIODIC_INQUIRY_CP_SIZE;
+
+ if (hci_send_req(dd, &rq, 100) < 0) {
+ perror("Periodic inquiry failed");
+ exit(EXIT_FAILURE);
+ }
+
+ hci_close_dev(dd);
+}
+
+/* Exit periodic inquiry */
+
+static struct option epinq_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *epinq_help =
+ "Usage:\n"
+ "\tepinq\n";
+
+static void cmd_epinq(int dev_id, int argc, char **argv)
+{
+ int opt, dd;
+
+ for_each_opt(opt, epinq_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", epinq_help);
+ return;
+ }
+ }
+ helper_arg(0, 0, &argc, &argv, epinq_help);
+
+ if (dev_id < 0)
+ dev_id = hci_get_route(NULL);
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("Device open failed");
+ exit(EXIT_FAILURE);
+ }
+
+ if (hci_send_cmd(dd, OGF_LINK_CTL,
+ OCF_EXIT_PERIODIC_INQUIRY, 0, NULL) < 0) {
+ perror("Exit periodic inquiry failed");
+ exit(EXIT_FAILURE);
+ }
+
+ hci_close_dev(dd);
+}
+
+/* Send arbitrary HCI commands */
+
+static struct option cmd_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *cmd_help =
+ "Usage:\n"
+ "\tcmd <ogf> <ocf> [parameters]\n"
+ "Example:\n"
+ "\tcmd 0x03 0x0013 0x41 0x42 0x43 0x44\n";
+
+static void cmd_cmd(int dev_id, int argc, char **argv)
+{
+ unsigned char buf[HCI_MAX_EVENT_SIZE], *ptr = buf;
+ struct hci_filter flt;
+ hci_event_hdr *hdr;
+ int i, opt, len, dd;
+ uint16_t ocf;
+ uint8_t ogf;
+
+ for_each_opt(opt, cmd_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", cmd_help);
+ return;
+ }
+ }
+ helper_arg(2, -1, &argc, &argv, cmd_help);
+
+ if (dev_id < 0)
+ dev_id = hci_get_route(NULL);
+
+ errno = 0;
+ ogf = strtol(argv[0], NULL, 16);
+ ocf = strtol(argv[1], NULL, 16);
+ if (errno == ERANGE || (ogf > 0x3f) || (ocf > 0x3ff)) {
+ printf("%s", cmd_help);
+ return;
+ }
+
+ for (i = 2, len = 0; i < argc && len < (int) sizeof(buf); i++, len++)
+ *ptr++ = (uint8_t) strtol(argv[i], NULL, 16);
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("Device open failed");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Setup filter */
+ hci_filter_clear(&flt);
+ hci_filter_set_ptype(HCI_EVENT_PKT, &flt);
+ hci_filter_all_events(&flt);
+ if (setsockopt(dd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
+ perror("HCI filter setup failed");
+ exit(EXIT_FAILURE);
+ }
+
+ printf("< HCI Command: ogf 0x%02x, ocf 0x%04x, plen %d\n", ogf, ocf, len);
+ hex_dump(" ", 20, buf, len); fflush(stdout);
+
+ if (hci_send_cmd(dd, ogf, ocf, len, buf) < 0) {
+ perror("Send failed");
+ exit(EXIT_FAILURE);
+ }
+
+ len = read(dd, buf, sizeof(buf));
+ if (len < 0) {
+ perror("Read failed");
+ exit(EXIT_FAILURE);
+ }
+
+ hdr = (void *)(buf + 1);
+ ptr = buf + (1 + HCI_EVENT_HDR_SIZE);
+ len -= (1 + HCI_EVENT_HDR_SIZE);
+
+ printf("> HCI Event: 0x%02x plen %d\n", hdr->evt, hdr->plen);
+ hex_dump(" ", 20, ptr, len); fflush(stdout);
+
+ hci_close_dev(dd);
+}
+
+/* Display active connections */
+
+static struct option con_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *con_help =
+ "Usage:\n"
+ "\tcon\n";
+
+static void cmd_con(int dev_id, int argc, char **argv)
+{
+ int opt;
+
+ for_each_opt(opt, con_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", con_help);
+ return;
+ }
+ }
+ helper_arg(0, 0, &argc, &argv, con_help);
+
+ printf("Connections:\n");
+
+ hci_for_each_dev(HCI_UP, conn_list, dev_id);
+}
+
+/* Create connection */
+
+static struct option cc_options[] = {
+ { "help", 0, 0, 'h' },
+ { "role", 1, 0, 'r' },
+ { "ptype", 1, 0, 'p' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *cc_help =
+ "Usage:\n"
+ "\tcc [--role=m|s] [--ptype=pkt_types] <bdaddr>\n"
+ "Example:\n"
+ "\tcc --ptype=dm1,dh3,dh5 01:02:03:04:05:06\n"
+ "\tcc --role=m 01:02:03:04:05:06\n";
+
+static void cmd_cc(int dev_id, int argc, char **argv)
+{
+ bdaddr_t bdaddr;
+ uint16_t handle;
+ uint8_t role;
+ unsigned int ptype;
+ int dd, opt;
+
+ role = 0x01;
+ ptype = HCI_DM1 | HCI_DM3 | HCI_DM5 | HCI_DH1 | HCI_DH3 | HCI_DH5;
+
+ for_each_opt(opt, cc_options, NULL) {
+ switch (opt) {
+ case 'p':
+ hci_strtoptype(optarg, &ptype);
+ break;
+
+ case 'r':
+ role = optarg[0] == 'm' ? 0 : 1;
+ break;
+
+ default:
+ printf("%s", cc_help);
+ return;
+ }
+ }
+ helper_arg(1, 1, &argc, &argv, cc_help);
+
+ str2ba(argv[0], &bdaddr);
+
+ if (dev_id < 0) {
+ dev_id = hci_get_route(&bdaddr);
+ if (dev_id < 0) {
+ fprintf(stderr, "Device is not available.\n");
+ exit(1);
+ }
+ }
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ exit(1);
+ }
+
+ if (hci_create_connection(dd, &bdaddr, htobs(ptype),
+ htobs(0x0000), role, &handle, 25000) < 0)
+ perror("Can't create connection");
+
+ hci_close_dev(dd);
+}
+
+/* Close connection */
+
+static struct option dc_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *dc_help =
+ "Usage:\n"
+ "\tdc <bdaddr> [reason]\n";
+
+static void cmd_dc(int dev_id, int argc, char **argv)
+{
+ struct hci_conn_info_req *cr;
+ bdaddr_t bdaddr;
+ uint8_t reason;
+ int opt, dd;
+
+ for_each_opt(opt, dc_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", dc_help);
+ return;
+ }
+ }
+ helper_arg(1, 2, &argc, &argv, dc_help);
+
+ str2ba(argv[0], &bdaddr);
+ reason = (argc > 1) ? atoi(argv[1]) : HCI_OE_USER_ENDED_CONNECTION;
+
+ if (dev_id < 0) {
+ dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+ if (dev_id < 0) {
+ fprintf(stderr, "Not connected.\n");
+ exit(1);
+ }
+ }
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ exit(1);
+ }
+
+ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+ if (!cr) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+
+ bacpy(&cr->bdaddr, &bdaddr);
+ cr->type = ACL_LINK;
+ if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+ perror("Get connection info failed");
+ exit(1);
+ }
+
+ if (hci_disconnect(dd, htobs(cr->conn_info->handle),
+ reason, 10000) < 0)
+ perror("Disconnect failed");
+
+ free(cr);
+
+ hci_close_dev(dd);
+}
+
+/* Role switch */
+
+static struct option sr_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *sr_help =
+ "Usage:\n"
+ "\tsr <bdaddr> <role>\n";
+
+static void cmd_sr(int dev_id, int argc, char **argv)
+{
+ bdaddr_t bdaddr;
+ uint8_t role;
+ int opt, dd;
+
+ for_each_opt(opt, sr_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", sr_help);
+ return;
+ }
+ }
+ helper_arg(2, 2, &argc, &argv, sr_help);
+
+ str2ba(argv[0], &bdaddr);
+ switch (argv[1][0]) {
+ case 'm':
+ role = 0;
+ break;
+ case 's':
+ role = 1;
+ break;
+ default:
+ role = atoi(argv[1]);
+ break;
+ }
+
+ if (dev_id < 0) {
+ dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+ if (dev_id < 0) {
+ fprintf(stderr, "Not connected.\n");
+ exit(1);
+ }
+ }
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ exit(1);
+ }
+
+ if (hci_switch_role(dd, &bdaddr, role, 10000) < 0) {
+ perror("Switch role request failed");
+ exit(1);
+ }
+
+ hci_close_dev(dd);
+}
+
+/* Read RSSI */
+
+static struct option rssi_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *rssi_help =
+ "Usage:\n"
+ "\trssi <bdaddr>\n";
+
+static void cmd_rssi(int dev_id, int argc, char **argv)
+{
+ struct hci_conn_info_req *cr;
+ bdaddr_t bdaddr;
+ int8_t rssi;
+ int opt, dd;
+
+ for_each_opt(opt, rssi_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", rssi_help);
+ return;
+ }
+ }
+ helper_arg(1, 1, &argc, &argv, rssi_help);
+
+ str2ba(argv[0], &bdaddr);
+
+ if (dev_id < 0) {
+ dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+ if (dev_id < 0) {
+ fprintf(stderr, "Not connected.\n");
+ exit(1);
+ }
+ }
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ exit(1);
+ }
+
+ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+ if (!cr) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+
+ bacpy(&cr->bdaddr, &bdaddr);
+ cr->type = ACL_LINK;
+ if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+ perror("Get connection info failed");
+ exit(1);
+ }
+
+ if (hci_read_rssi(dd, htobs(cr->conn_info->handle), &rssi, 1000) < 0) {
+ perror("Read RSSI failed");
+ exit(1);
+ }
+
+ printf("RSSI return value: %d\n", rssi);
+
+ free(cr);
+
+ hci_close_dev(dd);
+}
+
+/* Get link quality */
+
+static struct option lq_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *lq_help =
+ "Usage:\n"
+ "\tlq <bdaddr>\n";
+
+static void cmd_lq(int dev_id, int argc, char **argv)
+{
+ struct hci_conn_info_req *cr;
+ bdaddr_t bdaddr;
+ uint8_t lq;
+ int opt, dd;
+
+ for_each_opt(opt, lq_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", lq_help);
+ return;
+ }
+ }
+ helper_arg(1, 1, &argc, &argv, lq_help);
+
+ str2ba(argv[0], &bdaddr);
+
+ if (dev_id < 0) {
+ dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+ if (dev_id < 0) {
+ fprintf(stderr, "Not connected.\n");
+ exit(1);
+ }
+ }
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ exit(1);
+ }
+
+ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+ if (!cr) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+
+ bacpy(&cr->bdaddr, &bdaddr);
+ cr->type = ACL_LINK;
+ if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+ perror("Get connection info failed");
+ exit(1);
+ }
+
+ if (hci_read_link_quality(dd, htobs(cr->conn_info->handle), &lq, 1000) < 0) {
+ perror("HCI read_link_quality request failed");
+ exit(1);
+ }
+
+ printf("Link quality: %d\n", lq);
+
+ free(cr);
+
+ hci_close_dev(dd);
+}
+
+/* Get transmit power level */
+
+static struct option tpl_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *tpl_help =
+ "Usage:\n"
+ "\ttpl <bdaddr> [type]\n";
+
+static void cmd_tpl(int dev_id, int argc, char **argv)
+{
+ struct hci_conn_info_req *cr;
+ bdaddr_t bdaddr;
+ uint8_t type;
+ int8_t level;
+ int opt, dd;
+
+ for_each_opt(opt, tpl_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", tpl_help);
+ return;
+ }
+ }
+ helper_arg(1, 2, &argc, &argv, tpl_help);
+
+ str2ba(argv[0], &bdaddr);
+ type = (argc > 1) ? atoi(argv[1]) : 0;
+
+ if (dev_id < 0) {
+ dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+ if (dev_id < 0) {
+ fprintf(stderr, "Not connected.\n");
+ exit(1);
+ }
+ }
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ exit(1);
+ }
+
+ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+ if (!cr) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+
+ bacpy(&cr->bdaddr, &bdaddr);
+ cr->type = ACL_LINK;
+ if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+ perror("Get connection info failed");
+ exit(1);
+ }
+
+ if (hci_read_transmit_power_level(dd, htobs(cr->conn_info->handle), type, &level, 1000) < 0) {
+ perror("HCI read transmit power level request failed");
+ exit(1);
+ }
+
+ printf("%s transmit power level: %d\n",
+ (type == 0) ? "Current" : "Maximum", level);
+
+ free(cr);
+
+ hci_close_dev(dd);
+}
+
+/* Get AFH channel map */
+
+static struct option afh_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *afh_help =
+ "Usage:\n"
+ "\tafh <bdaddr>\n";
+
+static void cmd_afh(int dev_id, int argc, char **argv)
+{
+ struct hci_conn_info_req *cr;
+ bdaddr_t bdaddr;
+ uint16_t handle;
+ uint8_t mode, map[10];
+ int opt, dd;
+
+ for_each_opt(opt, afh_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", afh_help);
+ return;
+ }
+ }
+ helper_arg(1, 1, &argc, &argv, afh_help);
+
+ str2ba(argv[0], &bdaddr);
+
+ if (dev_id < 0) {
+ dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+ if (dev_id < 0) {
+ fprintf(stderr, "Not connected.\n");
+ exit(1);
+ }
+ }
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ exit(1);
+ }
+
+ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+ if (!cr) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+
+ bacpy(&cr->bdaddr, &bdaddr);
+ cr->type = ACL_LINK;
+ if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+ perror("Get connection info failed");
+ exit(1);
+ }
+
+ handle = htobs(cr->conn_info->handle);
+
+ if (hci_read_afh_map(dd, handle, &mode, map, 1000) < 0) {
+ perror("HCI read AFH map request failed");
+ exit(1);
+ }
+
+ if (mode == 0x01) {
+ int i;
+ printf("AFH map: 0x");
+ for (i = 0; i < 10; i++)
+ printf("%02x", map[i]);
+ printf("\n");
+ } else
+ printf("AFH disabled\n");
+
+ free(cr);
+
+ hci_close_dev(dd);
+}
+
+/* Set connection packet type */
+
+static struct option cpt_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *cpt_help =
+ "Usage:\n"
+ "\tcpt <bdaddr> <packet_types>\n";
+
+static void cmd_cpt(int dev_id, int argc, char **argv)
+{
+ struct hci_conn_info_req *cr;
+ struct hci_request rq;
+ set_conn_ptype_cp cp;
+ evt_conn_ptype_changed rp;
+ bdaddr_t bdaddr;
+ unsigned int ptype;
+ int dd, opt;
+
+ for_each_opt(opt, cpt_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", cpt_help);
+ return;
+ }
+ }
+ helper_arg(2, 2, &argc, &argv, cpt_help);
+
+ str2ba(argv[0], &bdaddr);
+ hci_strtoptype(argv[1], &ptype);
+
+ if (dev_id < 0) {
+ dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+ if (dev_id < 0) {
+ fprintf(stderr, "Not connected.\n");
+ exit(1);
+ }
+ }
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ exit(1);
+ }
+
+ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+ if (!cr) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+
+ bacpy(&cr->bdaddr, &bdaddr);
+ cr->type = ACL_LINK;
+ if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+ perror("Get connection info failed");
+ exit(1);
+ }
+
+ cp.handle = htobs(cr->conn_info->handle);
+ cp.pkt_type = ptype;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_LINK_CTL;
+ rq.ocf = OCF_SET_CONN_PTYPE;
+ rq.cparam = &cp;
+ rq.clen = SET_CONN_PTYPE_CP_SIZE;
+ rq.rparam = &rp;
+ rq.rlen = EVT_CONN_PTYPE_CHANGED_SIZE;
+ rq.event = EVT_CONN_PTYPE_CHANGED;
+
+ if (hci_send_req(dd, &rq, 100) < 0) {
+ perror("Packet type change failed");
+ exit(1);
+ }
+
+ free(cr);
+
+ hci_close_dev(dd);
+}
+
+/* Get/Set link policy settings */
+
+static struct option lp_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *lp_help =
+ "Usage:\n"
+ "\tlp <bdaddr> [link policy]\n";
+
+static void cmd_lp(int dev_id, int argc, char **argv)
+{
+ struct hci_conn_info_req *cr;
+ bdaddr_t bdaddr;
+ uint16_t policy;
+ int opt, dd;
+
+ for_each_opt(opt, lp_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", lp_help);
+ return;
+ }
+ }
+ helper_arg(1, 2, &argc, &argv, lp_help);
+
+ str2ba(argv[0], &bdaddr);
+
+ if (dev_id < 0) {
+ dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+ if (dev_id < 0) {
+ fprintf(stderr, "Not connected.\n");
+ exit(1);
+ }
+ }
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ exit(1);
+ }
+
+ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+ if (!cr) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+
+ bacpy(&cr->bdaddr, &bdaddr);
+ cr->type = ACL_LINK;
+ if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+ perror("Get connection info failed");
+ exit(1);
+ }
+
+ if (argc == 1) {
+ char *str;
+ if (hci_read_link_policy(dd, htobs(cr->conn_info->handle),
+ &policy, 1000) < 0) {
+ perror("HCI read_link_policy_settings request failed");
+ exit(1);
+ }
+
+ policy = btohs(policy);
+ str = hci_lptostr(policy);
+ if (str) {
+ printf("Link policy settings: %s\n", str);
+ bt_free(str);
+ } else {
+ fprintf(stderr, "Invalig settings\n");
+ exit(1);
+ }
+ } else {
+ unsigned int val;
+ if (hci_strtolp(argv[1], &val) < 0) {
+ fprintf(stderr, "Invalig arguments\n");
+ exit(1);
+ }
+ policy = val;
+
+ if (hci_write_link_policy(dd, htobs(cr->conn_info->handle),
+ htobs(policy), 1000) < 0) {
+ perror("HCI write_link_policy_settings request failed");
+ exit(1);
+ }
+ }
+
+ free(cr);
+
+ hci_close_dev(dd);
+}
+
+/* Get/Set link supervision timeout */
+
+static struct option lst_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *lst_help =
+ "Usage:\n"
+ "\tlst <bdaddr> [new value in slots]\n";
+
+static void cmd_lst(int dev_id, int argc, char **argv)
+{
+ struct hci_conn_info_req *cr;
+ bdaddr_t bdaddr;
+ uint16_t timeout;
+ int opt, dd;
+
+ for_each_opt(opt, lst_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", lst_help);
+ return;
+ }
+ }
+ helper_arg(1, 2, &argc, &argv, lst_help);
+
+ str2ba(argv[0], &bdaddr);
+
+ if (dev_id < 0) {
+ dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+ if (dev_id < 0) {
+ fprintf(stderr, "Not connected.\n");
+ exit(1);
+ }
+ }
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ exit(1);
+ }
+
+ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+ if (!cr) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+
+ bacpy(&cr->bdaddr, &bdaddr);
+ cr->type = ACL_LINK;
+ if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+ perror("Get connection info failed");
+ exit(1);
+ }
+
+ if (argc == 1) {
+ if (hci_read_link_supervision_timeout(dd, htobs(cr->conn_info->handle),
+ &timeout, 1000) < 0) {
+ perror("HCI read_link_supervision_timeout request failed");
+ exit(1);
+ }
+
+ timeout = btohs(timeout);
+
+ if (timeout)
+ printf("Link supervision timeout: %u slots (%.2f msec)\n",
+ timeout, (float) timeout * 0.625);
+ else
+ printf("Link supervision timeout never expires\n");
+ } else {
+ timeout = strtol(argv[1], NULL, 10);
+
+ if (hci_write_link_supervision_timeout(dd, htobs(cr->conn_info->handle),
+ htobs(timeout), 1000) < 0) {
+ perror("HCI write_link_supervision_timeout request failed");
+ exit(1);
+ }
+ }
+
+ free(cr);
+
+ hci_close_dev(dd);
+}
+
+/* Request authentication */
+
+static struct option auth_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *auth_help =
+ "Usage:\n"
+ "\tauth <bdaddr>\n";
+
+static void cmd_auth(int dev_id, int argc, char **argv)
+{
+ struct hci_conn_info_req *cr;
+ bdaddr_t bdaddr;
+ int opt, dd;
+
+ for_each_opt(opt, auth_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", auth_help);
+ return;
+ }
+ }
+ helper_arg(1, 1, &argc, &argv, auth_help);
+
+ str2ba(argv[0], &bdaddr);
+
+ if (dev_id < 0) {
+ dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+ if (dev_id < 0) {
+ fprintf(stderr, "Not connected.\n");
+ exit(1);
+ }
+ }
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ exit(1);
+ }
+
+ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+ if (!cr) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+
+ bacpy(&cr->bdaddr, &bdaddr);
+ cr->type = ACL_LINK;
+ if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+ perror("Get connection info failed");
+ exit(1);
+ }
+
+ if (hci_authenticate_link(dd, htobs(cr->conn_info->handle), 25000) < 0) {
+ perror("HCI authentication request failed");
+ exit(1);
+ }
+
+ free(cr);
+
+ hci_close_dev(dd);
+}
+
+/* Activate encryption */
+
+static struct option enc_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *enc_help =
+ "Usage:\n"
+ "\tenc <bdaddr> [encrypt enable]\n";
+
+static void cmd_enc(int dev_id, int argc, char **argv)
+{
+ struct hci_conn_info_req *cr;
+ bdaddr_t bdaddr;
+ uint8_t encrypt;
+ int opt, dd;
+
+ for_each_opt(opt, enc_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", enc_help);
+ return;
+ }
+ }
+ helper_arg(1, 2, &argc, &argv, enc_help);
+
+ str2ba(argv[0], &bdaddr);
+
+ if (dev_id < 0) {
+ dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+ if (dev_id < 0) {
+ fprintf(stderr, "Not connected.\n");
+ exit(1);
+ }
+ }
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ exit(1);
+ }
+
+ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+ if (!cr) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+
+ bacpy(&cr->bdaddr, &bdaddr);
+ cr->type = ACL_LINK;
+ if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+ perror("Get connection info failed");
+ exit(1);
+ }
+
+ encrypt = (argc > 1) ? atoi(argv[1]) : 1;
+
+ if (hci_encrypt_link(dd, htobs(cr->conn_info->handle), encrypt, 25000) < 0) {
+ perror("HCI set encryption request failed");
+ exit(1);
+ }
+
+ free(cr);
+
+ hci_close_dev(dd);
+}
+
+/* Change connection link key */
+
+static struct option key_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *key_help =
+ "Usage:\n"
+ "\tkey <bdaddr>\n";
+
+static void cmd_key(int dev_id, int argc, char **argv)
+{
+ struct hci_conn_info_req *cr;
+ bdaddr_t bdaddr;
+ int opt, dd;
+
+ for_each_opt(opt, key_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", key_help);
+ return;
+ }
+ }
+ helper_arg(1, 1, &argc, &argv, key_help);
+
+ str2ba(argv[0], &bdaddr);
+
+ if (dev_id < 0) {
+ dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+ if (dev_id < 0) {
+ fprintf(stderr, "Not connected.\n");
+ exit(1);
+ }
+ }
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ exit(1);
+ }
+
+ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+ if (!cr) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+
+ bacpy(&cr->bdaddr, &bdaddr);
+ cr->type = ACL_LINK;
+ if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+ perror("Get connection info failed");
+ exit(1);
+ }
+
+ if (hci_change_link_key(dd, htobs(cr->conn_info->handle), 25000) < 0) {
+ perror("Changing link key failed");
+ exit(1);
+ }
+
+ free(cr);
+
+ hci_close_dev(dd);
+}
+
+/* Read clock offset */
+
+static struct option clkoff_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *clkoff_help =
+ "Usage:\n"
+ "\tclkoff <bdaddr>\n";
+
+static void cmd_clkoff(int dev_id, int argc, char **argv)
+{
+ struct hci_conn_info_req *cr;
+ bdaddr_t bdaddr;
+ uint16_t offset;
+ int opt, dd;
+
+ for_each_opt(opt, clkoff_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", clkoff_help);
+ return;
+ }
+ }
+ helper_arg(1, 1, &argc, &argv, clkoff_help);
+
+ str2ba(argv[0], &bdaddr);
+
+ if (dev_id < 0) {
+ dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+ if (dev_id < 0) {
+ fprintf(stderr, "Not connected.\n");
+ exit(1);
+ }
+ }
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ exit(1);
+ }
+
+ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+ if (!cr) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+
+ bacpy(&cr->bdaddr, &bdaddr);
+ cr->type = ACL_LINK;
+ if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+ perror("Get connection info failed");
+ exit(1);
+ }
+
+ if (hci_read_clock_offset(dd, htobs(cr->conn_info->handle), &offset, 1000) < 0) {
+ perror("Reading clock offset failed");
+ exit(1);
+ }
+
+ printf("Clock offset: 0x%4.4x\n", btohs(offset));
+
+ free(cr);
+
+ hci_close_dev(dd);
+}
+
+/* Read clock */
+
+static struct option clock_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *clock_help =
+ "Usage:\n"
+ "\tclock [bdaddr] [which clock]\n";
+
+static void cmd_clock(int dev_id, int argc, char **argv)
+{
+ struct hci_conn_info_req *cr;
+ bdaddr_t bdaddr;
+ uint8_t which;
+ uint32_t handle, clock;
+ uint16_t accuracy;
+ int opt, dd;
+
+ for_each_opt(opt, clock_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", clock_help);
+ return;
+ }
+ }
+ helper_arg(0, 2, &argc, &argv, clock_help);
+
+ if (argc > 0)
+ str2ba(argv[0], &bdaddr);
+ else
+ bacpy(&bdaddr, BDADDR_ANY);
+
+ if (dev_id < 0 && !bacmp(&bdaddr, BDADDR_ANY))
+ dev_id = hci_get_route(NULL);
+
+ if (dev_id < 0) {
+ dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+ if (dev_id < 0) {
+ fprintf(stderr, "Not connected.\n");
+ exit(1);
+ }
+ }
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ exit(1);
+ }
+
+ if (bacmp(&bdaddr, BDADDR_ANY)) {
+ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+ if (!cr) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+
+ bacpy(&cr->bdaddr, &bdaddr);
+ cr->type = ACL_LINK;
+ if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+ perror("Get connection info failed");
+ free(cr);
+ exit(1);
+ }
+
+ handle = htobs(cr->conn_info->handle);
+ which = (argc > 1) ? atoi(argv[1]) : 0x01;
+
+ free(cr);
+ } else {
+ handle = 0x00;
+ which = 0x00;
+ }
+
+ if (hci_read_clock(dd, handle, which, &clock, &accuracy, 1000) < 0) {
+ perror("Reading clock failed");
+ exit(1);
+ }
+
+ accuracy = btohs(accuracy);
+
+ printf("Clock: 0x%4.4x\n", btohl(clock));
+ printf("Accuracy: %.2f msec\n", (float) accuracy * 0.3125);
+
+ hci_close_dev(dd);
+}
+
+static int read_flags(uint8_t *flags, const uint8_t *data, size_t size)
+{
+ unsigned int offset;
+
+ if (!flags || !data)
+ return -EINVAL;
+
+ offset = 0;
+ while (offset < size) {
+ uint8_t len = data[offset];
+ uint8_t type = data[offset + 1];
+
+ /* Check if it is the end of the significant part */
+ if (len == 0)
+ break;
+
+ if (type == FLAGS_AD_TYPE) {
+ *flags = data[offset + 2];
+ return 0;
+ }
+
+ offset += 1 + len;
+ }
+
+ return -ENOENT;
+}
+
+static int check_report_filter(uint8_t procedure, le_advertising_info *info)
+{
+ uint8_t flags;
+
+ /* If no discovery procedure is set, all reports are treat as valid */
+ if (procedure == 0)
+ return 1;
+
+ /* Read flags AD type value from the advertising report if it exists */
+ if (read_flags(&flags, info->data, info->length))
+ return 0;
+
+ switch (procedure) {
+ case 'l': /* Limited Discovery Procedure */
+ if (flags & FLAGS_LIMITED_MODE_BIT)
+ return 1;
+ break;
+ case 'g': /* General Discovery Procedure */
+ if (flags & (FLAGS_LIMITED_MODE_BIT | FLAGS_GENERAL_MODE_BIT))
+ return 1;
+ break;
+ default:
+ fprintf(stderr, "Unknown discovery procedure\n");
+ }
+
+ return 0;
+}
+
+static int print_advertising_devices(int dd, uint8_t filter_type)
+{
+ unsigned char buf[HCI_MAX_EVENT_SIZE], *ptr;
+ struct hci_filter nf, of;
+ socklen_t olen;
+ hci_event_hdr *hdr;
+ int num, len;
+
+ olen = sizeof(of);
+ if (getsockopt(dd, SOL_HCI, HCI_FILTER, &of, &olen) < 0) {
+ printf("Could not get socket options\n");
+ return -1;
+ }
+
+ hci_filter_clear(&nf);
+ hci_filter_set_ptype(HCI_EVENT_PKT, &nf);
+ hci_filter_set_event(EVT_LE_META_EVENT, &nf);
+
+ if (setsockopt(dd, SOL_HCI, HCI_FILTER, &nf, sizeof(nf)) < 0) {
+ printf("Could not set socket options\n");
+ return -1;
+ }
+
+ /* Wait for 10 report events */
+ num = 10;
+ while (num--) {
+ evt_le_meta_event *meta;
+ le_advertising_info *info;
+ char addr[18];
+
+ while ((len = read(dd, buf, sizeof(buf))) < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ goto done;
+ }
+
+ hdr = (void *) (buf + 1);
+ ptr = buf + (1 + HCI_EVENT_HDR_SIZE);
+ len -= (1 + HCI_EVENT_HDR_SIZE);
+
+ meta = (void *) ptr;
+
+ if (meta->subevent != 0x02)
+ goto done;
+
+ /* Ignoring multiple reports */
+ info = (le_advertising_info *) (meta->data + 1);
+ if (check_report_filter(filter_type, info)) {
+ ba2str(&info->bdaddr, addr);
+ printf("%s\n", addr);
+ }
+ }
+
+done:
+ setsockopt(dd, SOL_HCI, HCI_FILTER, &of, sizeof(of));
+
+ if (len < 0)
+ return -1;
+
+ return 0;
+}
+
+static struct option lescan_options[] = {
+ { "help", 0, 0, 'h' },
+ { "privacy", 0, 0, 'p' },
+ { "passive", 0, 0, 'P' },
+ { "discovery", 1, 0, 'd' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *lescan_help =
+ "Usage:\n"
+ "\tlescan [--privacy] enable privacy\n"
+ "\tlescan [--passive] set scan type passive (default active)\n"
+ "\tlescan [--discovery=g|l] enable general or limited discovery"
+ "procedure\n";
+
+static void cmd_lescan(int dev_id, int argc, char **argv)
+{
+ int err, opt, dd;
+ uint8_t own_type = 0x00;
+ uint8_t scan_type = 0x01;
+ uint8_t filter_type = 0;
+ uint16_t interval = htobs(0x0010);
+ uint16_t window = htobs(0x0010);
+
+ for_each_opt(opt, lescan_options, NULL) {
+ switch (opt) {
+ case 'p':
+ own_type = 0x01; /* Random */
+ break;
+ case 'P':
+ scan_type = 0x00; /* Passive */
+ break;
+ case 'd':
+ filter_type = optarg[0];
+ if (filter_type != 'g' && filter_type != 'l') {
+ fprintf(stderr, "Unknown discovery procedure\n");
+ exit(1);
+ }
+
+ interval = htobs(0x0012);
+ window = htobs(0x0012);
+ break;
+ default:
+ printf("%s", lescan_help);
+ return;
+ }
+ }
+ helper_arg(0, 1, &argc, &argv, lescan_help);
+
+ if (dev_id < 0)
+ dev_id = hci_get_route(NULL);
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("Could not open device");
+ exit(1);
+ }
+
+ err = hci_le_set_scan_parameters(dd, scan_type, interval, window,
+ own_type, 0x00, 1000);
+ if (err < 0) {
+ perror("Set scan parameters failed");
+ exit(1);
+ }
+
+ err = hci_le_set_scan_enable(dd, 0x01, 0x00, 1000);
+ if (err < 0) {
+ perror("Enable scan failed");
+ exit(1);
+ }
+
+ printf("LE Scan ...\n");
+
+ err = print_advertising_devices(dd, filter_type);
+ if (err < 0) {
+ perror("Could not receive advertising events");
+ exit(1);
+ }
+
+ err = hci_le_set_scan_enable(dd, 0x00, 0x00, 1000);
+ if (err < 0) {
+ perror("Disable scan failed");
+ exit(1);
+ }
+
+ hci_close_dev(dd);
+}
+
+static struct option lecc_options[] = {
+ { "help", 0, 0, 'h' },
+ { "random", 0, 0, 'r' },
+ { "whitelist", 0, 0, 'w' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *lecc_help =
+ "Usage:\n"
+ "\tlecc [--random] <bdaddr>\n"
+ "\tlecc --whitelist\n";
+
+static void cmd_lecc(int dev_id, int argc, char **argv)
+{
+ int err, opt, dd;
+ bdaddr_t bdaddr;
+ uint16_t interval, latency, max_ce_length, max_interval, min_ce_length;
+ uint16_t min_interval, supervision_timeout, window, handle;
+ uint8_t initiator_filter, own_bdaddr_type, peer_bdaddr_type;
+
+ peer_bdaddr_type = LE_PUBLIC_ADDRESS;
+ initiator_filter = 0; /* Use peer address */
+
+ for_each_opt(opt, lecc_options, NULL) {
+ switch (opt) {
+ case 'r':
+ peer_bdaddr_type = LE_RANDOM_ADDRESS;
+ break;
+ case 'w':
+ initiator_filter = 0x01; /* Use white list */
+ break;
+ default:
+ printf("%s", lecc_help);
+ return;
+ }
+ }
+ helper_arg(0, 1, &argc, &argv, lecc_help);
+
+ if (dev_id < 0)
+ dev_id = hci_get_route(NULL);
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("Could not open device");
+ exit(1);
+ }
+
+ memset(&bdaddr, 0, sizeof(bdaddr_t));
+ if (argv[0])
+ str2ba(argv[0], &bdaddr);
+
+ interval = htobs(0x0004);
+ window = htobs(0x0004);
+ own_bdaddr_type = 0x00;
+ min_interval = htobs(0x000F);
+ max_interval = htobs(0x000F);
+ latency = htobs(0x0000);
+ supervision_timeout = htobs(0x0C80);
+ min_ce_length = htobs(0x0001);
+ max_ce_length = htobs(0x0001);
+
+ err = hci_le_create_conn(dd, interval, window, initiator_filter,
+ peer_bdaddr_type, bdaddr, own_bdaddr_type, min_interval,
+ max_interval, latency, supervision_timeout,
+ min_ce_length, max_ce_length, &handle, 25000);
+ if (err < 0) {
+ perror("Could not create connection");
+ exit(1);
+ }
+
+ printf("Connection handle %d\n", handle);
+
+ hci_close_dev(dd);
+}
+
+static struct option lewladd_options[] = {
+ { "help", 0, 0, 'h' },
+ { "random", 0, 0, 'r' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *lewladd_help =
+ "Usage:\n"
+ "\tlewladd [--random] <bdaddr>\n";
+
+static void cmd_lewladd(int dev_id, int argc, char **argv)
+{
+ int err, opt, dd;
+ bdaddr_t bdaddr;
+ uint8_t bdaddr_type = LE_PUBLIC_ADDRESS;
+
+ for_each_opt(opt, lewladd_options, NULL) {
+ switch (opt) {
+ case 'r':
+ bdaddr_type = LE_RANDOM_ADDRESS;
+ break;
+ default:
+ printf("%s", lewladd_help);
+ return;
+ }
+ }
+
+ helper_arg(1, 1, &argc, &argv, lewladd_help);
+
+ if (dev_id < 0)
+ dev_id = hci_get_route(NULL);
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("Could not open device");
+ exit(1);
+ }
+
+ str2ba(argv[0], &bdaddr);
+
+ err = hci_le_add_white_list(dd, &bdaddr, bdaddr_type, 1000);
+ hci_close_dev(dd);
+
+ if (err < 0) {
+ err = errno;
+ fprintf(stderr, "Can't add to white list: %s(%d)\n",
+ strerror(err), err);
+ exit(1);
+ }
+}
+
+static struct option lewlrm_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *lewlrm_help =
+ "Usage:\n"
+ "\tlewlrm <bdaddr>\n";
+
+static void cmd_lewlrm(int dev_id, int argc, char **argv)
+{
+ int err, opt, dd;
+ bdaddr_t bdaddr;
+
+ for_each_opt(opt, lewlrm_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", lewlrm_help);
+ return;
+ }
+ }
+
+ helper_arg(1, 1, &argc, &argv, lewlrm_help);
+
+ if (dev_id < 0)
+ dev_id = hci_get_route(NULL);
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("Could not open device");
+ exit(1);
+ }
+
+ str2ba(argv[0], &bdaddr);
+
+ err = hci_le_rm_white_list(dd, &bdaddr, LE_PUBLIC_ADDRESS, 1000);
+ hci_close_dev(dd);
+
+ if (err < 0) {
+ err = errno;
+ fprintf(stderr, "Can't remove from white list: %s(%d)\n",
+ strerror(err), err);
+ exit(1);
+ }
+}
+
+static struct option lewlsz_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *lewlsz_help =
+ "Usage:\n"
+ "\tlewlsz\n";
+
+static void cmd_lewlsz(int dev_id, int argc, char **argv)
+{
+ int err, dd, opt;
+ uint8_t size;
+
+ for_each_opt(opt, lewlsz_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", lewlsz_help);
+ return;
+ }
+ }
+
+ helper_arg(0, 0, &argc, &argv, lewlsz_help);
+
+ if (dev_id < 0)
+ dev_id = hci_get_route(NULL);
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("Could not open device");
+ exit(1);
+ }
+
+ err = hci_le_read_white_list_size(dd, &size, 1000);
+ hci_close_dev(dd);
+
+ if (err < 0) {
+ err = errno;
+ fprintf(stderr, "Can't read white list size: %s(%d)\n",
+ strerror(err), err);
+ exit(1);
+ }
+
+ printf("White list size: %d\n", size);
+}
+
+static struct option lewlclr_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *lewlclr_help =
+ "Usage:\n"
+ "\tlewlclr\n";
+
+static void cmd_lewlclr(int dev_id, int argc, char **argv)
+{
+ int err, dd, opt;
+
+ for_each_opt(opt, lewlclr_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", lewlclr_help);
+ return;
+ }
+ }
+
+ helper_arg(0, 0, &argc, &argv, lewlclr_help);
+
+ if (dev_id < 0)
+ dev_id = hci_get_route(NULL);
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("Could not open device");
+ exit(1);
+ }
+
+ err = hci_le_clear_white_list(dd, 1000);
+ hci_close_dev(dd);
+
+ if (err < 0) {
+ err = errno;
+ fprintf(stderr, "Can't clear white list: %s(%d)\n",
+ strerror(err), err);
+ exit(1);
+ }
+}
+
+static struct option ledc_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *ledc_help =
+ "Usage:\n"
+ "\tledc <handle> [reason]\n";
+
+static void cmd_ledc(int dev_id, int argc, char **argv)
+{
+ int err, opt, dd;
+ uint16_t handle;
+ uint8_t reason;
+
+ for_each_opt(opt, ledc_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", ledc_help);
+ return;
+ }
+ }
+ helper_arg(1, 2, &argc, &argv, ledc_help);
+
+ if (dev_id < 0)
+ dev_id = hci_get_route(NULL);
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("Could not open device");
+ exit(1);
+ }
+
+ handle = atoi(argv[0]);
+
+ reason = (argc > 1) ? atoi(argv[1]) : HCI_OE_USER_ENDED_CONNECTION;
+
+ err = hci_disconnect(dd, handle, reason, 10000);
+ if (err < 0) {
+ perror("Could not disconnect");
+ exit(1);
+ }
+
+ hci_close_dev(dd);
+}
+
+static struct option lecup_options[] = {
+ { "help", 0, 0, 'h' },
+ { "handle", 1, 0, 'H' },
+ { "min", 1, 0, 'm' },
+ { "max", 1, 0, 'M' },
+ { "latency", 1, 0, 'l' },
+ { "timeout", 1, 0, 't' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *lecup_help =
+ "Usage:\n"
+ "\tlecup <handle> <min> <max> <latency> <timeout>\n"
+ "\tOptions:\n"
+ "\t -H, --handle <0xXXXX> LE connection handle\n"
+ "\t -m, --min <interval> Range: 0x0006 to 0x0C80\n"
+ "\t -M, --max <interval> Range: 0x0006 to 0x0C80\n"
+ "\t -l, --latency <range> Slave latency. Range: 0x0000 to 0x03E8\n"
+ "\t -t, --timeout <time> N * 10ms. Range: 0x000A to 0x0C80\n"
+ "\n\t min/max range: 7.5ms to 4s. Multiply factor: 1.25ms"
+ "\n\t timeout range: 100ms to 32.0s. Larger than max interval\n";
+
+static void cmd_lecup(int dev_id, int argc, char **argv)
+{
+ uint16_t handle = 0, min, max, latency, timeout;
+ int opt, dd, base;
+
+ /* Aleatory valid values */
+ min = 0x0C8;
+ max = 0x0960;
+ latency = 0x0007;
+ timeout = 0x0C80;
+
+ for_each_opt(opt, lecup_options, NULL) {
+ if (optarg && strncasecmp("0x", optarg, 2) == 0)
+ base = 16;
+ else
+ base = 10;
+
+ switch (opt) {
+ case 'H':
+ handle = strtoul(optarg, NULL, base);
+ break;
+ case 'm':
+ min = strtoul(optarg, NULL, base);
+ break;
+ case 'M':
+ max = strtoul(optarg, NULL, base);
+ break;
+ case 'l':
+ latency = strtoul(optarg, NULL, base);
+ break;
+ case 't':
+ timeout = strtoul(optarg, NULL, base);
+ break;
+ default:
+ printf("%s", lecup_help);
+ return;
+ }
+ }
+
+ if (handle == 0) {
+ printf("%s", lecup_help);
+ return;
+ }
+
+ if (dev_id < 0)
+ dev_id = hci_get_route(NULL);
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ fprintf(stderr, "HCI device open failed\n");
+ exit(1);
+ }
+
+ if (hci_le_conn_update(dd, htobs(handle), htobs(min), htobs(max),
+ htobs(latency), htobs(timeout), 5000) < 0) {
+ int err = errno;
+ fprintf(stderr, "Could not change connection params: %s(%d)\n",
+ strerror(err), err);
+ }
+
+ hci_close_dev(dd);
+}
+
+static struct {
+ char *cmd;
+ void (*func)(int dev_id, int argc, char **argv);
+ char *doc;
+} command[] = {
+ { "dev", cmd_dev, "Display local devices" },
+ { "inq", cmd_inq, "Inquire remote devices" },
+ { "scan", cmd_scan, "Scan for remote devices" },
+ { "name", cmd_name, "Get name from remote device" },
+ { "info", cmd_info, "Get information from remote device" },
+ { "spinq", cmd_spinq, "Start periodic inquiry" },
+ { "epinq", cmd_epinq, "Exit periodic inquiry" },
+ { "cmd", cmd_cmd, "Submit arbitrary HCI commands" },
+ { "con", cmd_con, "Display active connections" },
+ { "cc", cmd_cc, "Create connection to remote device" },
+ { "dc", cmd_dc, "Disconnect from remote device" },
+ { "sr", cmd_sr, "Switch master/slave role" },
+ { "cpt", cmd_cpt, "Change connection packet type" },
+ { "rssi", cmd_rssi, "Display connection RSSI" },
+ { "lq", cmd_lq, "Display link quality" },
+ { "tpl", cmd_tpl, "Display transmit power level" },
+ { "afh", cmd_afh, "Display AFH channel map" },
+ { "lp", cmd_lp, "Set/display link policy settings" },
+ { "lst", cmd_lst, "Set/display link supervision timeout" },
+ { "auth", cmd_auth, "Request authentication" },
+ { "enc", cmd_enc, "Set connection encryption" },
+ { "key", cmd_key, "Change connection link key" },
+ { "clkoff", cmd_clkoff, "Read clock offset" },
+ { "clock", cmd_clock, "Read local or remote clock" },
+ { "lescan", cmd_lescan, "Start LE scan" },
+ { "lewladd", cmd_lewladd, "Add device to LE White List" },
+ { "lewlrm", cmd_lewlrm, "Remove device from LE White List" },
+ { "lewlsz", cmd_lewlsz, "Read size of LE White List" },
+ { "lewlclr", cmd_lewlclr, "Clear LE White list" },
+ { "lecc", cmd_lecc, "Create a LE Connection" },
+ { "ledc", cmd_ledc, "Disconnect a LE Connection" },
+ { "lecup", cmd_lecup, "LE Connection Update" },
+ { NULL, NULL, 0 }
+};
+
+static void usage(void)
+{
+ int i;
+
+ printf("hcitool - HCI Tool ver %s\n", VERSION);
+ printf("Usage:\n"
+ "\thcitool [options] <command> [command parameters]\n");
+ printf("Options:\n"
+ "\t--help\tDisplay help\n"
+ "\t-i dev\tHCI device\n");
+ printf("Commands:\n");
+ for (i = 0; command[i].cmd; i++)
+ printf("\t%-4s\t%s\n", command[i].cmd,
+ command[i].doc);
+ printf("\n"
+ "For more information on the usage of each command use:\n"
+ "\thcitool <command> --help\n" );
+}
+
+static struct option main_options[] = {
+ { "help", 0, 0, 'h' },
+ { "device", 1, 0, 'i' },
+ { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ int opt, i, dev_id = -1;
+ bdaddr_t ba;
+
+ while ((opt=getopt_long(argc, argv, "+i:h", main_options, NULL)) != -1) {
+ switch (opt) {
+ case 'i':
+ dev_id = hci_devid(optarg);
+ if (dev_id < 0) {
+ perror("Invalid device");
+ exit(1);
+ }
+ break;
+
+ case 'h':
+ default:
+ usage();
+ exit(0);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ if (argc < 1) {
+ usage();
+ exit(0);
+ }
+
+ if (dev_id != -1 && hci_devba(dev_id, &ba) < 0) {
+ perror("Device is not available");
+ exit(1);
+ }
+
+ for (i = 0; command[i].cmd; i++) {
+ if (strncmp(command[i].cmd,
+ argv[0], strlen(command[i].cmd)))
+ continue;
+
+ command[i].func(dev_id, argc, argv);
+ break;
+ }
+
+ if (command[i].cmd == 0) {
+ fprintf(stderr, "Unknown command - \"%s\"\n", *argv);
+ exit(1);
+ }
+
+ return 0;
+}
diff --git a/tools/hid2hci.8 b/tools/hid2hci.8
new file mode 100644
index 0000000..5d35274
--- /dev/null
+++ b/tools/hid2hci.8
@@ -0,0 +1,51 @@
+.\"
+.\" 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.TH HID2HCI 8 "MAY 15, 2009" "" ""
+
+.SH NAME
+hid2hci \- Bluetooth HID to HCI mode switching utility
+.SH SYNOPSIS
+.BR "hid2hci
+[
+.I options
+]
+.SH DESCRIPTION
+.B hid2hci
+is used to set up switch supported Bluetooth devices into the HCI
+mode and back.
+.SH OPTIONS
+.TP
+.BI -h
+Gives a list of possible options.
+.TP
+.BI -q
+Don't display any messages.
+.TP
+.BI -r [hid,hci]
+Sets the mode to switch the device into
+.TP
+.BI -v
+Specifies the 4 digit vendor ID assigned to the device being switched
+.TP
+.BI -p
+Specifies the 4 digit product ID assigned to the device being switched
+.TP
+.BI -m [csr, logitech, dell]
+Which vendor method to use for switching the device.
+.SH AUTHOR
+Written by Marcel Holtmann <marcel@holtmann.org>.
+.br
diff --git a/tools/hid2hci.c b/tools/hid2hci.c
new file mode 100644
index 0000000..a640772
--- /dev/null
+++ b/tools/hid2hci.c
@@ -0,0 +1,375 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2003-2010 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 <stdint.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/ioctl.h>
+
+#include <usb.h>
+
+#ifdef NEED_USB_GET_BUSSES
+static inline struct usb_bus *usb_get_busses(void)
+{
+ return usb_busses;
+}
+#endif
+
+#ifndef USB_DIR_OUT
+#define USB_DIR_OUT 0x00
+#endif
+
+static char devpath[PATH_MAX + 1] = "/dev";
+
+struct hiddev_devinfo {
+ unsigned int bustype;
+ unsigned int busnum;
+ unsigned int devnum;
+ unsigned int ifnum;
+ short vendor;
+ short product;
+ short version;
+ unsigned num_applications;
+};
+
+struct hiddev_report_info {
+ unsigned report_type;
+ unsigned report_id;
+ unsigned num_fields;
+};
+
+typedef __signed__ int __s32;
+
+struct hiddev_usage_ref {
+ unsigned report_type;
+ unsigned report_id;
+ unsigned field_index;
+ unsigned usage_index;
+ unsigned usage_code;
+ __s32 value;
+};
+
+#define HIDIOCGDEVINFO _IOR('H', 0x03, struct hiddev_devinfo)
+#define HIDIOCINITREPORT _IO('H', 0x05)
+#define HIDIOCSREPORT _IOW('H', 0x08, struct hiddev_report_info)
+#define HIDIOCSUSAGE _IOW('H', 0x0C, struct hiddev_usage_ref)
+
+#define HID_REPORT_TYPE_OUTPUT 2
+
+#define HCI 0
+#define HID 1
+
+struct device_info {
+ struct usb_device *dev;
+ int mode;
+ uint16_t vendor;
+ uint16_t product;
+};
+
+static int switch_csr(struct device_info *devinfo)
+{
+ struct usb_dev_handle *udev;
+ int err;
+
+ udev = usb_open(devinfo->dev);
+ if (!udev)
+ return -errno;
+
+ err = usb_control_msg(udev, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, devinfo->mode, 0, NULL, 0, 10000);
+
+ if (err == 0) {
+ err = -1;
+ errno = EALREADY;
+ } else {
+ if (errno == ETIMEDOUT)
+ err = 0;
+ }
+
+ usb_close(udev);
+
+ return err;
+}
+
+static int send_report(int fd, const char *buf, size_t size)
+{
+ struct hiddev_report_info rinfo;
+ struct hiddev_usage_ref uref;
+ unsigned int i;
+ int err;
+
+ for (i = 0; i < size; i++) {
+ memset(&uref, 0, sizeof(uref));
+ uref.report_type = HID_REPORT_TYPE_OUTPUT;
+ uref.report_id = 0x10;
+ uref.field_index = 0;
+ uref.usage_index = i;
+ uref.usage_code = 0xff000001;
+ uref.value = buf[i] & 0x000000ff;
+ err = ioctl(fd, HIDIOCSUSAGE, &uref);
+ if (err < 0)
+ return err;
+ }
+
+ memset(&rinfo, 0, sizeof(rinfo));
+ rinfo.report_type = HID_REPORT_TYPE_OUTPUT;
+ rinfo.report_id = 0x10;
+ rinfo.num_fields = 1;
+ err = ioctl(fd, HIDIOCSREPORT, &rinfo);
+
+ return err;
+}
+
+static int switch_logitech(struct device_info *devinfo)
+{
+ char devname[PATH_MAX + 1];
+ int i, fd, err = -1;
+
+ for (i = 0; i < 16; i++) {
+ struct hiddev_devinfo dinfo;
+ char rep1[] = { 0xff, 0x80, 0x80, 0x01, 0x00, 0x00 };
+ char rep2[] = { 0xff, 0x80, 0x00, 0x00, 0x30, 0x00 };
+ char rep3[] = { 0xff, 0x81, 0x80, 0x00, 0x00, 0x00 };
+
+ sprintf(devname, "%s/hiddev%d", devpath, i);
+ fd = open(devname, O_RDWR);
+ if (fd < 0) {
+ sprintf(devname, "%s/usb/hiddev%d", devpath, i);
+ fd = open(devname, O_RDWR);
+ if (fd < 0) {
+ sprintf(devname, "%s/usb/hid/hiddev%d", devpath, i);
+ fd = open(devname, O_RDWR);
+ if (fd < 0)
+ continue;
+ }
+ }
+
+ memset(&dinfo, 0, sizeof(dinfo));
+ err = ioctl(fd, HIDIOCGDEVINFO, &dinfo);
+ if (err < 0 || (int) dinfo.busnum != atoi(devinfo->dev->bus->dirname) ||
+ (int) dinfo.devnum != atoi(devinfo->dev->filename)) {
+ close(fd);
+ continue;
+ }
+
+ err = ioctl(fd, HIDIOCINITREPORT, 0);
+ if (err < 0) {
+ close(fd);
+ break;
+ }
+
+ err = send_report(fd, rep1, sizeof(rep1));
+ if (err < 0) {
+ close(fd);
+ break;
+ }
+
+ err = send_report(fd, rep2, sizeof(rep2));
+ if (err < 0) {
+ close(fd);
+ break;
+ }
+
+ err = send_report(fd, rep3, sizeof(rep3));
+ close(fd);
+ break;
+ }
+
+ return err;
+}
+
+static int switch_dell(struct device_info *devinfo)
+{
+ char report[] = { 0x7f, 0x00, 0x00, 0x00 };
+
+ struct usb_dev_handle *handle;
+ int err;
+
+ switch (devinfo->mode) {
+ case HCI:
+ report[1] = 0x13;
+ break;
+ case HID:
+ report[1] = 0x14;
+ break;
+ }
+
+ handle = usb_open(devinfo->dev);
+ if (!handle)
+ return -EIO;
+
+ /* Don't need to check return, as might not be in use */
+ usb_detach_kernel_driver_np(handle, 0);
+
+ if (usb_claim_interface(handle, 0) < 0) {
+ usb_close(handle);
+ return -EIO;
+ }
+
+ err = usb_control_msg(handle,
+ USB_ENDPOINT_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ USB_REQ_SET_CONFIGURATION, 0x7f | (0x03 << 8), 0,
+ report, sizeof(report), 5000);
+
+ if (err == 0) {
+ err = -1;
+ errno = EALREADY;
+ } else {
+ if (errno == ETIMEDOUT)
+ err = 0;
+ }
+
+ usb_close(handle);
+
+ return err;
+}
+
+static int find_device(struct device_info* devinfo)
+{
+ struct usb_bus *bus;
+ struct usb_device *dev;
+
+ usb_find_busses();
+ usb_find_devices();
+
+ for (bus = usb_get_busses(); bus; bus = bus->next)
+ for (dev = bus->devices; dev; dev = dev->next) {
+ if (dev->descriptor.idVendor == devinfo->vendor &&
+ dev->descriptor.idProduct == devinfo->product) {
+ devinfo->dev=dev;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void usage(char* error)
+{
+ if (error)
+ fprintf(stderr,"\n%s\n", error);
+ else
+ printf("hid2hci - Bluetooth HID to HCI mode switching utility\n\n");
+
+ printf("Usage:\n"
+ "\thid2hci [options]\n"
+ "\n");
+
+ printf("Options:\n"
+ "\t-h, --help Display help\n"
+ "\t-q, --quiet Don't display any messages\n"
+ "\t-r, --mode= Mode to switch to [hid, hci]\n"
+ "\t-v, --vendor= Vendor ID to act upon\n"
+ "\t-p, --product= Product ID to act upon\n"
+ "\t-m, --method= Method to use to switch [csr, logitech, dell]\n"
+ "\n");
+ if (error)
+ exit(1);
+}
+
+static struct option main_options[] = {
+ { "help", no_argument, 0, 'h' },
+ { "quiet", no_argument, 0, 'q' },
+ { "mode", required_argument, 0, 'r' },
+ { "vendor", required_argument, 0, 'v' },
+ { "product", required_argument, 0, 'p' },
+ { "method", required_argument, 0, 'm' },
+ { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ struct device_info dev = { NULL, HCI, 0, 0 };
+ int opt, quiet = 0;
+ int (*method)(struct device_info *dev) = NULL;
+
+ while ((opt = getopt_long(argc, argv, "+r:v:p:m:qh", main_options, NULL)) != -1) {
+ switch (opt) {
+ case 'r':
+ if (optarg && !strcmp(optarg, "hid"))
+ dev.mode = HID;
+ else if (optarg && !strcmp(optarg, "hci"))
+ dev.mode = HCI;
+ else
+ usage("ERROR: Undefined radio mode\n");
+ break;
+ case 'v':
+ sscanf(optarg, "%4hx", &dev.vendor);
+ break;
+ case 'p':
+ sscanf(optarg, "%4hx", &dev.product);
+ break;
+ case 'm':
+ if (optarg && !strcmp(optarg, "csr"))
+ method = switch_csr;
+ else if (optarg && !strcmp(optarg, "logitech"))
+ method = switch_logitech;
+ else if (optarg && !strcmp(optarg, "dell"))
+ method = switch_dell;
+ else
+ usage("ERROR: Undefined switching method\n");
+ break;
+ case 'q':
+ quiet = 1;
+ break;
+ case 'h':
+ usage(NULL);
+ default:
+ exit(0);
+ }
+ }
+
+ if (!quiet && (!dev.vendor || !dev.product || !method))
+ usage("ERROR: Vendor ID, Product ID, and Switching Method must all be defined.\n");
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ usb_init();
+
+ if (!find_device(&dev)) {
+ if (!quiet)
+ fprintf(stderr, "Device %04x:%04x not found on USB bus.\n",
+ dev.vendor, dev.product);
+ exit(1);
+ }
+
+ if (!quiet)
+ printf("Attempting to switch device %04x:%04x to %s mode ",
+ dev.vendor, dev.product, dev.mode ? "HID" : "HCI");
+ fflush(stdout);
+
+ if (method(&dev) < 0 && !quiet)
+ printf("failed (%s)\n", strerror(errno));
+ else if (!quiet)
+ printf("was successful\n");
+
+ return errno;
+}
diff --git a/tools/kword.c b/tools/kword.c
new file mode 100644
index 0000000..62e24fe
--- /dev/null
+++ b/tools/kword.c
@@ -0,0 +1,65 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2010 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 <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+
+#include "kword.h"
+#include "parser.h"
+
+int lineno;
+
+struct keyword_t rfcomm_keyword[] = {
+ { "bind", K_BIND },
+ { "device", K_DEVICE },
+ { "channel", K_CHANNEL },
+ { "comment", K_COMMENT },
+
+ { "yes", K_YES },
+ { "no", K_NO },
+ { "enable", K_YES },
+ { "disable", K_NO },
+
+ { NULL , 0 }
+};
+
+int rfcomm_find_keyword(struct keyword_t *keyword, char *string)
+{
+ while (keyword->string) {
+ if (!strcmp(string, keyword->string))
+ return keyword->type;
+ keyword++;
+ }
+
+ return -1;
+}
+
+struct rfcomm_opts rfcomm_opts[RFCOMM_MAX_DEV];
diff --git a/tools/kword.h b/tools/kword.h
new file mode 100644
index 0000000..81a2a88
--- /dev/null
+++ b/tools/kword.h
@@ -0,0 +1,46 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2010 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
+ *
+ */
+
+extern int lineno;
+
+struct keyword_t {
+ char *string;
+ int type;
+};
+
+extern struct keyword_t rfcomm_keyword[];
+
+int rfcomm_find_keyword(struct keyword_t *keyword, char *string);
+
+#define MAXCOMMENTLEN 100
+
+struct rfcomm_opts {
+ int bind;
+ bdaddr_t bdaddr;
+ int channel;
+ char comment[MAXCOMMENTLEN + 1];
+};
+
+extern struct rfcomm_opts rfcomm_opts[RFCOMM_MAX_DEV];
+
+int rfcomm_read_config(char *filename);
diff --git a/tools/l2ping.8 b/tools/l2ping.8
new file mode 100644
index 0000000..8b77ee2
--- /dev/null
+++ b/tools/l2ping.8
@@ -0,0 +1,76 @@
+.TH L2PING 8 "Jan 22 2002" BlueZ "Linux System Administration"
+.SH NAME
+l2ping \- Send L2CAP echo request and receive answer
+.SH SYNOPSIS
+.B l2ping
+.RB [\| \-i
+.IR <hciX> \|]
+.RB [\| \-s
+.IR size \|]
+.RB [\| \-c
+.IR count \|]
+.RB [\| \-t
+.IR timeout \|]
+.RB [\| \-d
+.IR delay \|]
+.RB [\| \-f \|]
+.RB [\| \-r \|]
+.RB [\| \-v \|]
+.I bd_addr
+
+.SH DESCRIPTION
+.LP
+L2ping sends a L2CAP echo request to the Bluetooth MAC address
+.I bd_addr
+given in dotted hex notation.
+.SH OPTIONS
+.TP
+.BI \-i " <hciX>"
+The command is applied to device
+.BI
+hciX
+, which must be the name of an installed Bluetooth device (X = 0, 1, 2, ...)
+If not specified, the command will be sent to the first available Bluetooth
+device.
+.TP
+.BI \-s " size"
+The
+.I size
+of the data packets to be sent.
+.TP
+.BI \-c " count"
+Send
+.I count
+number of packets then exit.
+.TP
+.BI \-t " timeout"
+Wait
+.I timeout
+seconds for the response.
+.TP
+.BI \-d " delay"
+Wait
+.I delay
+seconds between pings.
+.TP
+.B \-f
+Kind of flood ping. Use with care! It reduces the delay time between packets
+to 0.
+.TP
+.B \-r
+Reverse ping (gnip?). Send echo response instead of echo request.
+.TP
+.B \-v
+Verify response payload is identical to request payload. It is not required for
+remote stacks to return the request payload, but most stacks do (including
+Bluez).
+.TP
+.I bd_addr
+The Bluetooth MAC address to be pinged in dotted hex notation like
+.B 01:02:03:ab:cd:ef
+or
+.B 01:EF:cd:aB:02:03
+.SH AUTHORS
+Written by Maxim Krasnyansky <maxk@qualcomm.com> and Marcel Holtmann <marcel@holtmann.org>
+.PP
+man page by Nils Faerber <nils@kernelconcepts.de>, Adam Laurie <adam@algroup.co.uk>.
diff --git a/tools/l2ping.c b/tools/l2ping.c
new file mode 100644
index 0000000..29fb3d0
--- /dev/null
+++ b/tools/l2ping.c
@@ -0,0 +1,324 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2000-2001 Qualcomm Incorporated
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 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 <string.h>
+#include <getopt.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/l2cap.h>
+
+/* Defaults */
+static bdaddr_t bdaddr;
+static int size = 44;
+static int ident = 200;
+static int delay = 1;
+static int count = -1;
+static int timeout = 10;
+static int reverse = 0;
+static int verify = 0;
+
+/* Stats */
+static int sent_pkt = 0;
+static int recv_pkt = 0;
+
+static float tv2fl(struct timeval tv)
+{
+ return (float)(tv.tv_sec*1000.0) + (float)(tv.tv_usec/1000.0);
+}
+
+static void stat(int sig)
+{
+ int loss = sent_pkt ? (float)((sent_pkt-recv_pkt)/(sent_pkt/100.0)) : 0;
+ printf("%d sent, %d received, %d%% loss\n", sent_pkt, recv_pkt, loss);
+ exit(0);
+}
+
+static void ping(char *svr)
+{
+ struct sigaction sa;
+ struct sockaddr_l2 addr;
+ socklen_t optlen;
+ unsigned char *send_buf;
+ unsigned char *recv_buf;
+ char str[18];
+ int i, sk, lost;
+ uint8_t id;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = stat;
+ sigaction(SIGINT, &sa, NULL);
+
+ send_buf = malloc(L2CAP_CMD_HDR_SIZE + size);
+ recv_buf = malloc(L2CAP_CMD_HDR_SIZE + size);
+ if (!send_buf || !recv_buf) {
+ perror("Can't allocate buffer");
+ exit(1);
+ }
+
+ /* Create socket */
+ sk = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP);
+ if (sk < 0) {
+ perror("Can't create socket");
+ goto error;
+ }
+
+ /* 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) {
+ perror("Can't bind socket");
+ goto error;
+ }
+
+ /* Connect to remote device */
+ 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");
+ goto error;
+ }
+
+ /* Get local address */
+ memset(&addr, 0, sizeof(addr));
+ optlen = sizeof(addr);
+
+ if (getsockname(sk, (struct sockaddr *) &addr, &optlen) < 0) {
+ perror("Can't get local address");
+ goto error;
+ }
+
+ ba2str(&addr.l2_bdaddr, str);
+ printf("Ping: %s from %s (data size %d) ...\n", svr, str, size);
+
+ /* Initialize send buffer */
+ for (i = 0; i < size; i++)
+ send_buf[L2CAP_CMD_HDR_SIZE + i] = (i % 40) + 'A';
+
+ id = ident;
+
+ while (count == -1 || count-- > 0) {
+ struct timeval tv_send, tv_recv, tv_diff;
+ l2cap_cmd_hdr *send_cmd = (l2cap_cmd_hdr *) send_buf;
+ l2cap_cmd_hdr *recv_cmd = (l2cap_cmd_hdr *) recv_buf;
+
+ /* Build command header */
+ send_cmd->ident = id;
+ send_cmd->len = htobs(size);
+
+ if (reverse)
+ send_cmd->code = L2CAP_ECHO_RSP;
+ else
+ send_cmd->code = L2CAP_ECHO_REQ;
+
+ gettimeofday(&tv_send, NULL);
+
+ /* Send Echo Command */
+ if (send(sk, send_buf, L2CAP_CMD_HDR_SIZE + size, 0) <= 0) {
+ perror("Send failed");
+ goto error;
+ }
+
+ /* Wait for Echo Response */
+ lost = 0;
+ while (1) {
+ struct pollfd pf[1];
+ int err;
+
+ pf[0].fd = sk;
+ pf[0].events = POLLIN;
+
+ if ((err = poll(pf, 1, timeout * 1000)) < 0) {
+ perror("Poll failed");
+ goto error;
+ }
+
+ if (!err) {
+ lost = 1;
+ break;
+ }
+
+ if ((err = recv(sk, recv_buf, L2CAP_CMD_HDR_SIZE + size, 0)) < 0) {
+ perror("Recv failed");
+ goto error;
+ }
+
+ if (!err){
+ printf("Disconnected\n");
+ goto error;
+ }
+
+ recv_cmd->len = btohs(recv_cmd->len);
+
+ /* Check for our id */
+ if (recv_cmd->ident != id)
+ continue;
+
+ /* Check type */
+ if (!reverse && recv_cmd->code == L2CAP_ECHO_RSP)
+ break;
+
+ if (recv_cmd->code == L2CAP_COMMAND_REJ) {
+ printf("Peer doesn't support Echo packets\n");
+ goto error;
+ }
+
+ }
+ sent_pkt++;
+
+ if (!lost) {
+ recv_pkt++;
+
+ gettimeofday(&tv_recv, NULL);
+ timersub(&tv_recv, &tv_send, &tv_diff);
+
+ if (verify) {
+ /* Check payload length */
+ if (recv_cmd->len != size) {
+ fprintf(stderr, "Received %d bytes, expected %d\n",
+ recv_cmd->len, size);
+ goto error;
+ }
+
+ /* Check payload */
+ if (memcmp(&send_buf[L2CAP_CMD_HDR_SIZE],
+ &recv_buf[L2CAP_CMD_HDR_SIZE], size)) {
+ fprintf(stderr, "Response payload different.\n");
+ goto error;
+ }
+ }
+
+ printf("%d bytes from %s id %d time %.2fms\n", recv_cmd->len, svr,
+ id - ident, tv2fl(tv_diff));
+
+ if (delay)
+ sleep(delay);
+ } else {
+ printf("no response from %s: id %d\n", svr, id - ident);
+ }
+
+ if (++id > 254)
+ id = ident;
+ }
+ stat(0);
+ free(send_buf);
+ free(recv_buf);
+ return;
+
+error:
+ close(sk);
+ free(send_buf);
+ free(recv_buf);
+ exit(1);
+}
+
+static void usage(void)
+{
+ printf("l2ping - L2CAP ping\n");
+ printf("Usage:\n");
+ printf("\tl2ping [-i device] [-s size] [-c count] [-t timeout] [-d delay] [-f] [-r] [-v] <bdaddr>\n");
+ printf("\t-f Flood ping (delay = 0)\n");
+ printf("\t-r Reverse ping\n");
+ printf("\t-v Verify request and response payload\n");
+}
+
+int main(int argc, char *argv[])
+{
+ int opt;
+
+ /* Default options */
+ bacpy(&bdaddr, BDADDR_ANY);
+
+ while ((opt=getopt(argc,argv,"i:d:s:c:t:frv")) != EOF) {
+ switch(opt) {
+ case 'i':
+ if (!strncasecmp(optarg, "hci", 3))
+ hci_devba(atoi(optarg + 3), &bdaddr);
+ else
+ str2ba(optarg, &bdaddr);
+ break;
+
+ case 'd':
+ delay = atoi(optarg);
+ break;
+
+ case 'f':
+ /* Kinda flood ping */
+ delay = 0;
+ break;
+
+ case 'r':
+ /* Use responses instead of requests */
+ reverse = 1;
+ break;
+
+ case 'v':
+ verify = 1;
+ break;
+
+ case 'c':
+ count = atoi(optarg);
+ break;
+
+ case 't':
+ timeout = atoi(optarg);
+ break;
+
+ case 's':
+ size = atoi(optarg);
+ break;
+
+ default:
+ usage();
+ exit(1);
+ }
+ }
+
+ if (!(argc - optind)) {
+ usage();
+ exit(1);
+ }
+
+ ping(argv[optind]);
+
+ return 0;
+}
diff --git a/tools/lexer.c b/tools/lexer.c
new file mode 100644
index 0000000..4c7dcd4
--- /dev/null
+++ b/tools/lexer.c
@@ -0,0 +1,1834 @@
+
+#line 3 "tools/lexer.c"
+
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 35
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif /* defined (__STDC__) */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index. If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN (yy_start) = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START (((yy_start) - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart(yyin )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+extern int yyleng;
+
+extern FILE *yyin, *yyout;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+ #define YY_LESS_LINENO(n)
+
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ *yy_cp = (yy_hold_char); \
+ YY_RESTORE_YY_MORE_OFFSET \
+ (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+ } \
+ while ( 0 )
+
+#define unput(c) yyunput( c, (yytext_ptr) )
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via yyrestart()), so that the user can continue scanning by
+ * just pointing yyin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* Stack of input buffers. */
+static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */
+static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */
+static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
+ ? (yy_buffer_stack)[(yy_buffer_stack_top)] \
+ : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
+
+/* yy_hold_char holds the character lost when yytext is formed. */
+static char yy_hold_char;
+static int yy_n_chars; /* number of characters read into yy_ch_buf */
+int yyleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 0; /* whether we need to initialize */
+static int yy_start = 0; /* start state number */
+
+/* Flag which is used to allow yywrap()'s to do buffer switches
+ * instead of setting up a fresh yyin. A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void yyrestart (FILE *input_file );
+void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer );
+YY_BUFFER_STATE yy_create_buffer (FILE *file,int size );
+void yy_delete_buffer (YY_BUFFER_STATE b );
+void yy_flush_buffer (YY_BUFFER_STATE b );
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer );
+void yypop_buffer_state (void );
+
+static void yyensure_buffer_stack (void );
+static void yy_load_buffer_state (void );
+static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file );
+
+#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER )
+
+YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size );
+YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str );
+YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len );
+
+void *yyalloc (yy_size_t );
+void *yyrealloc (void *,yy_size_t );
+void yyfree (void * );
+
+#define yy_new_buffer yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){ \
+ yyensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ yy_create_buffer(yyin,YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+ }
+
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){\
+ yyensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ yy_create_buffer(yyin,YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+ }
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+typedef unsigned char YY_CHAR;
+
+FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0;
+
+typedef int yy_state_type;
+
+extern int yylineno;
+
+int yylineno = 1;
+
+extern char *yytext;
+#define yytext_ptr yytext
+
+static yy_state_type yy_get_previous_state (void );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state );
+static int yy_get_next_buffer (void );
+static void yy_fatal_error (yyconst char msg[] );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ (yytext_ptr) = yy_bp; \
+ yyleng = (size_t) (yy_cp - yy_bp); \
+ (yy_hold_char) = *yy_cp; \
+ *yy_cp = '\0'; \
+ (yy_c_buf_p) = yy_cp;
+
+#define YY_NUM_RULES 9
+#define YY_END_OF_BUFFER 10
+/* This struct is not used in this scanner,
+ but its presence is necessary. */
+struct yy_trans_info
+ {
+ flex_int32_t yy_verify;
+ flex_int32_t yy_nxt;
+ };
+static yyconst flex_int16_t yy_accept[36] =
+ { 0,
+ 0, 0, 10, 8, 1, 7, 8, 8, 6, 3,
+ 6, 0, 4, 0, 2, 6, 3, 6, 3, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 5, 0
+ } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 1, 4, 5, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 6, 1, 1, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 8, 1, 1,
+ 1, 1, 1, 1, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 1, 1, 1, 1, 6, 1, 9, 9, 9, 9,
+
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1
+ } ;
+
+static yyconst flex_int32_t yy_meta[10] =
+ { 0,
+ 1, 1, 2, 1, 1, 3, 4, 1, 4
+ } ;
+
+static yyconst flex_int16_t yy_base[49] =
+ { 0,
+ 0, 0, 46, 47, 47, 47, 41, 41, 0, 4,
+ 36, 38, 37, 37, 47, 0, 7, 31, 31, 0,
+ 0, 29, 0, 0, 28, 0, 0, 27, 0, 0,
+ 26, 0, 0, 47, 47, 15, 19, 21, 29, 28,
+ 27, 26, 25, 24, 23, 22, 13, 8
+ } ;
+
+static yyconst flex_int16_t yy_def[49] =
+ { 0,
+ 35, 1, 35, 35, 35, 35, 36, 37, 38, 35,
+ 10, 36, 36, 37, 35, 38, 38, 38, 38, 39,
+ 40, 35, 41, 42, 35, 43, 44, 35, 45, 46,
+ 35, 47, 48, 35, 0, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35
+ } ;
+
+static yyconst flex_int16_t yy_nxt[57] =
+ { 0,
+ 4, 5, 6, 7, 8, 9, 10, 4, 11, 16,
+ 17, 34, 18, 19, 20, 12, 33, 12, 12, 14,
+ 14, 14, 14, 16, 16, 31, 30, 28, 27, 25,
+ 24, 22, 21, 32, 29, 26, 23, 19, 20, 15,
+ 13, 13, 18, 15, 13, 35, 3, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35
+ } ;
+
+static yyconst flex_int16_t yy_chk[57] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 10,
+ 10, 48, 10, 17, 17, 36, 47, 36, 36, 37,
+ 37, 37, 37, 38, 38, 46, 45, 44, 43, 42,
+ 41, 40, 39, 31, 28, 25, 22, 19, 18, 14,
+ 13, 12, 11, 8, 7, 3, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35
+ } ;
+
+static yy_state_type yy_last_accepting_state;
+static char *yy_last_accepting_cpos;
+
+extern int yy_flex_debug;
+int yy_flex_debug = 0;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *yytext;
+#line 1 "lexer.l"
+#line 2 "lexer.l"
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2010 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
+
+/* Nasty workaround, but flex defines isatty() twice */
+#define _UNISTD_H
+
+#include <stdio.h>
+#include <errno.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+
+#include "kword.h"
+#include "parser.h"
+
+int yylex(void);
+
+#define YY_NO_INPUT
+
+#define ECHO {;}
+#define YY_DECL int yylex(void)
+
+int yyerror(char *str);
+
+#line 520 "tools/lexer.c"
+
+#define INITIAL 0
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+static int yy_init_globals (void );
+
+/* Accessor methods to globals.
+ These are made visible to non-reentrant scanners for convenience. */
+
+int yylex_destroy (void );
+
+int yyget_debug (void );
+
+void yyset_debug (int debug_flag );
+
+YY_EXTRA_TYPE yyget_extra (void );
+
+void yyset_extra (YY_EXTRA_TYPE user_defined );
+
+FILE *yyget_in (void );
+
+void yyset_in (FILE * in_str );
+
+FILE *yyget_out (void );
+
+void yyset_out (FILE * out_str );
+
+int yyget_leng (void );
+
+char *yyget_text (void );
+
+int yyget_lineno (void );
+
+void yyset_lineno (int line_number );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap (void );
+#else
+extern int yywrap (void );
+#endif
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int );
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * );
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (void );
+#else
+static int input (void );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO do { if (fwrite( yytext, yyleng, 1, yyout )) {} } while (0)
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+ { \
+ int c = '*'; \
+ unsigned n; \
+ for ( n = 0; n < max_size && \
+ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ if ( c == EOF && ferror( yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ result = n; \
+ } \
+ else \
+ { \
+ errno=0; \
+ while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \
+ { \
+ if( errno != EINTR) \
+ { \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ break; \
+ } \
+ errno=0; \
+ clearerr(yyin); \
+ } \
+ }\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int yylex (void);
+
+#define YY_DECL int yylex (void)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+ YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp, *yy_bp;
+ register int yy_act;
+
+#line 64 "lexer.l"
+
+
+#line 703 "tools/lexer.c"
+
+ if ( !(yy_init) )
+ {
+ (yy_init) = 1;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ if ( ! (yy_start) )
+ (yy_start) = 1; /* first start state */
+
+ if ( ! yyin )
+ yyin = stdin;
+
+ if ( ! yyout )
+ yyout = stdout;
+
+ if ( ! YY_CURRENT_BUFFER ) {
+ yyensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ yy_create_buffer(yyin,YY_BUF_SIZE );
+ }
+
+ yy_load_buffer_state( );
+ }
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+ yy_cp = (yy_c_buf_p);
+
+ /* Support of yytext. */
+ *yy_cp = (yy_hold_char);
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = (yy_start);
+yy_match:
+ do
+ {
+ register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 36 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ ++yy_cp;
+ }
+ while ( yy_base[yy_current_state] != 47 );
+
+yy_find_action:
+ yy_act = yy_accept[yy_current_state];
+ if ( yy_act == 0 )
+ { /* have to back up */
+ yy_cp = (yy_last_accepting_cpos);
+ yy_current_state = (yy_last_accepting_state);
+ yy_act = yy_accept[yy_current_state];
+ }
+
+ YY_DO_BEFORE_ACTION;
+
+do_action: /* This label is used only to access EOF actions. */
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+ case 0: /* must back up */
+ /* undo the effects of YY_DO_BEFORE_ACTION */
+ *yy_cp = (yy_hold_char);
+ yy_cp = (yy_last_accepting_cpos);
+ yy_current_state = (yy_last_accepting_state);
+ goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+#line 66 "lexer.l"
+{
+ /* Skip spaces and tabs */
+ ;
+ }
+ YY_BREAK
+case 2:
+/* rule 2 can match eol */
+YY_RULE_SETUP
+#line 71 "lexer.l"
+{
+ /* Skip comments */
+ lineno++;
+ }
+ YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 76 "lexer.l"
+{
+ yylval.number = atoi(yytext);
+ return NUMBER;
+ }
+ YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 81 "lexer.l"
+{
+ yylval.string = yytext;
+ return STRING;
+ }
+ YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 86 "lexer.l"
+{
+ bdaddr_t *ba = malloc(sizeof(bdaddr_t));
+ str2ba(yytext, ba);
+ yylval.bdaddr = ba;
+ return BDADDR;
+ }
+ YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 93 "lexer.l"
+{
+ int keyword = rfcomm_find_keyword(rfcomm_keyword, yytext);
+ if (keyword != -1)
+ return keyword;
+
+ if (strncmp(yytext, "rfcomm", 6) == 0) {
+ yylval.number = atoi(yytext + 6);
+ return RFCOMM;
+ }
+
+ yylval.string = yytext;
+ return WORD;
+ }
+ YY_BREAK
+case 7:
+/* rule 7 can match eol */
+YY_RULE_SETUP
+#line 107 "lexer.l"
+{
+ lineno++;
+ }
+ YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 111 "lexer.l"
+{
+ return *yytext;
+ }
+ YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 115 "lexer.l"
+ECHO;
+ YY_BREAK
+#line 866 "tools/lexer.c"
+case YY_STATE_EOF(INITIAL):
+ yyterminate();
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = (yy_hold_char);
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed yyin at a new source and called
+ * yylex(). If so, then we have to assure
+ * consistency between YY_CURRENT_BUFFER and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++(yy_c_buf_p);
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ yy_cp = (yy_c_buf_p);
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ (yy_did_buffer_switch_on_eof) = 0;
+
+ if ( yywrap( ) )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * yytext, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) =
+ (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ (yy_c_buf_p) =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)];
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+} /* end of yylex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (void)
+{
+ register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+ register char *source = (yytext_ptr);
+ register int number_to_move, i;
+ int ret_val;
+
+ if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0;
+
+ else
+ {
+ int num_to_read =
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+
+ /* just a shorter name for the current buffer */
+ YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+ int yy_c_buf_p_offset =
+ (int) ((yy_c_buf_p) - b->yy_ch_buf);
+
+ if ( b->yy_is_our_buffer )
+ {
+ int new_size = b->yy_buf_size * 2;
+
+ if ( new_size <= 0 )
+ b->yy_buf_size += b->yy_buf_size / 8;
+ else
+ b->yy_buf_size *= 2;
+
+ b->yy_ch_buf = (char *)
+ /* Include room in for 2 EOB chars. */
+ yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 );
+ }
+ else
+ /* Can't grow it, we don't own it. */
+ b->yy_ch_buf = 0;
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR(
+ "fatal error - scanner input buffer overflow" );
+
+ (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+ number_to_move - 1;
+
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+ (yy_n_chars), (size_t) num_to_read );
+
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ if ( (yy_n_chars) == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ yyrestart(yyin );
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+ /* Extend the array by 50%, plus the number we really need. */
+ yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1);
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size );
+ if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+ }
+
+ (yy_n_chars) += number_to_move;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR;
+
+ (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+ return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+ static yy_state_type yy_get_previous_state (void)
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp;
+
+ yy_current_state = (yy_start);
+
+ for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
+ {
+ register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 36 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ }
+
+ return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state )
+{
+ register int yy_is_jam;
+ register char *yy_cp = (yy_c_buf_p);
+
+ register YY_CHAR yy_c = 1;
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 36 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ yy_is_jam = (yy_current_state == 35);
+
+ return yy_is_jam ? 0 : yy_current_state;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+ static int yyinput (void)
+#else
+ static int input (void)
+#endif
+
+{
+ int c;
+
+ *(yy_c_buf_p) = (yy_hold_char);
+
+ if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ /* This was really a NUL. */
+ *(yy_c_buf_p) = '\0';
+
+ else
+ { /* need more input */
+ int offset = (yy_c_buf_p) - (yytext_ptr);
+ ++(yy_c_buf_p);
+
+ switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ yyrestart(yyin );
+
+ /*FALLTHROUGH*/
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( yywrap( ) )
+ return EOF;
+
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput();
+#else
+ return input();
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) = (yytext_ptr) + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */
+ *(yy_c_buf_p) = '\0'; /* preserve yytext */
+ (yy_hold_char) = *++(yy_c_buf_p);
+
+ return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ *
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+ void yyrestart (FILE * input_file )
+{
+
+ if ( ! YY_CURRENT_BUFFER ){
+ yyensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ yy_create_buffer(yyin,YY_BUF_SIZE );
+ }
+
+ yy_init_buffer(YY_CURRENT_BUFFER,input_file );
+ yy_load_buffer_state( );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ *
+ */
+ void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer )
+{
+
+ /* TODO. We should be able to replace this entire function body
+ * with
+ * yypop_buffer_state();
+ * yypush_buffer_state(new_buffer);
+ */
+ yyensure_buffer_stack ();
+ if ( YY_CURRENT_BUFFER == new_buffer )
+ return;
+
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+ yy_load_buffer_state( );
+
+ /* We don't actually know whether we did this switch during
+ * EOF (yywrap()) processing, but the only time this flag
+ * is looked at is after yywrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+
+static void yy_load_buffer_state (void)
+{
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+ yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+ (yy_hold_char) = *(yy_c_buf_p);
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ *
+ * @return the allocated buffer state.
+ */
+ YY_BUFFER_STATE yy_create_buffer (FILE * file, int size )
+{
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2 );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ yy_init_buffer(b,file );
+
+ return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with yy_create_buffer()
+ *
+ */
+ void yy_delete_buffer (YY_BUFFER_STATE b )
+{
+
+ if ( ! b )
+ return;
+
+ if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ yyfree((void *) b->yy_ch_buf );
+
+ yyfree((void *) b );
+}
+
+#ifndef __cplusplus
+extern int isatty (int );
+#endif /* __cplusplus */
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a yyrestart() or at EOF.
+ */
+ static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file )
+
+{
+ int oerrno = errno;
+
+ yy_flush_buffer(b );
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+ /* If b is the current buffer, then yy_init_buffer was _probably_
+ * called from yyrestart() or through yy_get_next_buffer.
+ * In that case, we don't want to reset the lineno or column.
+ */
+ if (b != YY_CURRENT_BUFFER){
+ b->yy_bs_lineno = 1;
+ b->yy_bs_column = 0;
+ }
+
+ b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+
+ errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ *
+ */
+ void yy_flush_buffer (YY_BUFFER_STATE b )
+{
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == YY_CURRENT_BUFFER )
+ yy_load_buffer_state( );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ * the current state. This function will allocate the stack
+ * if necessary.
+ * @param new_buffer The new state.
+ *
+ */
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer )
+{
+ if (new_buffer == NULL)
+ return;
+
+ yyensure_buffer_stack();
+
+ /* This block is copied from yy_switch_to_buffer. */
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ /* Only push if top exists. Otherwise, replace top. */
+ if (YY_CURRENT_BUFFER)
+ (yy_buffer_stack_top)++;
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+ /* copied from yy_switch_to_buffer. */
+ yy_load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ * The next element becomes the new top.
+ *
+ */
+void yypop_buffer_state (void)
+{
+ if (!YY_CURRENT_BUFFER)
+ return;
+
+ yy_delete_buffer(YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ if ((yy_buffer_stack_top) > 0)
+ --(yy_buffer_stack_top);
+
+ if (YY_CURRENT_BUFFER) {
+ yy_load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+ }
+}
+
+/* Allocates the stack if it does not exist.
+ * Guarantees space for at least one push.
+ */
+static void yyensure_buffer_stack (void)
+{
+ int num_to_alloc;
+
+ if (!(yy_buffer_stack)) {
+
+ /* First allocation is just for 2 elements, since we don't know if this
+ * scanner will even need a stack. We use 2 instead of 1 to avoid an
+ * immediate realloc on the next call.
+ */
+ num_to_alloc = 1;
+ (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc
+ (num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+ if ( ! (yy_buffer_stack) )
+ YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+ memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+ (yy_buffer_stack_max) = num_to_alloc;
+ (yy_buffer_stack_top) = 0;
+ return;
+ }
+
+ if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){
+
+ /* Increase the buffer to prepare for a possible push. */
+ int grow_size = 8 /* arbitrary grow size */;
+
+ num_to_alloc = (yy_buffer_stack_max) + grow_size;
+ (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc
+ ((yy_buffer_stack),
+ num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+ if ( ! (yy_buffer_stack) )
+ YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+ /* zero only the new slots.*/
+ memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
+ (yy_buffer_stack_max) = num_to_alloc;
+ }
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size )
+{
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return 0;
+
+ b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+ b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = 0;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ yy_switch_to_buffer(b );
+
+ return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to yylex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ *
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ * yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE yy_scan_string (yyconst char * yystr )
+{
+
+ return yy_scan_bytes(yystr,strlen(yystr) );
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param bytes the byte buffer to scan
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, int _yybytes_len )
+{
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ int i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = _yybytes_len + 2;
+ buf = (char *) yyalloc(n );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+ for ( i = 0; i < _yybytes_len; ++i )
+ buf[i] = yybytes[i];
+
+ buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = yy_scan_buffer(buf,n );
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg )
+{
+ (void) fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ yytext[yyleng] = (yy_hold_char); \
+ (yy_c_buf_p) = yytext + yyless_macro_arg; \
+ (yy_hold_char) = *(yy_c_buf_p); \
+ *(yy_c_buf_p) = '\0'; \
+ yyleng = yyless_macro_arg; \
+ } \
+ while ( 0 )
+
+/* Accessor methods (get/set functions) to struct members. */
+
+/** Get the current line number.
+ *
+ */
+int yyget_lineno (void)
+{
+
+ return yylineno;
+}
+
+/** Get the input stream.
+ *
+ */
+FILE *yyget_in (void)
+{
+ return yyin;
+}
+
+/** Get the output stream.
+ *
+ */
+FILE *yyget_out (void)
+{
+ return yyout;
+}
+
+/** Get the length of the current token.
+ *
+ */
+int yyget_leng (void)
+{
+ return yyleng;
+}
+
+/** Get the current token.
+ *
+ */
+
+char *yyget_text (void)
+{
+ return yytext;
+}
+
+/** Set the current line number.
+ * @param line_number
+ *
+ */
+void yyset_lineno (int line_number )
+{
+
+ yylineno = line_number;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ *
+ * @see yy_switch_to_buffer
+ */
+void yyset_in (FILE * in_str )
+{
+ yyin = in_str ;
+}
+
+void yyset_out (FILE * out_str )
+{
+ yyout = out_str ;
+}
+
+int yyget_debug (void)
+{
+ return yy_flex_debug;
+}
+
+void yyset_debug (int bdebug )
+{
+ yy_flex_debug = bdebug ;
+}
+
+static int yy_init_globals (void)
+{
+ /* Initialization is the same as for the non-reentrant scanner.
+ * This function is called from yylex_destroy(), so don't allocate here.
+ */
+
+ (yy_buffer_stack) = 0;
+ (yy_buffer_stack_top) = 0;
+ (yy_buffer_stack_max) = 0;
+ (yy_c_buf_p) = (char *) 0;
+ (yy_init) = 0;
+ (yy_start) = 0;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+ yyin = stdin;
+ yyout = stdout;
+#else
+ yyin = (FILE *) 0;
+ yyout = (FILE *) 0;
+#endif
+
+ /* For future reference: Set errno on error, since we are called by
+ * yylex_init()
+ */
+ return 0;
+}
+
+/* yylex_destroy is for both reentrant and non-reentrant scanners. */
+int yylex_destroy (void)
+{
+
+ /* Pop the buffer stack, destroying each element. */
+ while(YY_CURRENT_BUFFER){
+ yy_delete_buffer(YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ yypop_buffer_state();
+ }
+
+ /* Destroy the stack itself. */
+ yyfree((yy_buffer_stack) );
+ (yy_buffer_stack) = NULL;
+
+ /* Reset the globals. This is important in a non-reentrant scanner so the next time
+ * yylex() is called, initialization will occur. */
+ yy_init_globals( );
+
+ return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n )
+{
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s )
+{
+ register int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+}
+#endif
+
+void *yyalloc (yy_size_t size )
+{
+ return (void *) malloc( size );
+}
+
+void *yyrealloc (void * ptr, yy_size_t size )
+{
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return (void *) realloc( (char *) ptr, size );
+}
+
+void yyfree (void * ptr )
+{
+ free( (char *) ptr ); /* see yyrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 115 "lexer.l"
+
+
+
+int yywrap(void)
+{
+ return 1;
+}
+
diff --git a/tools/lexer.l b/tools/lexer.l
new file mode 100644
index 0000000..ff9ce81
--- /dev/null
+++ b/tools/lexer.l
@@ -0,0 +1,120 @@
+%{
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2010 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
+
+/* Nasty workaround, but flex defines isatty() twice */
+#define _UNISTD_H
+
+#include <stdio.h>
+#include <errno.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+
+#include "kword.h"
+#include "parser.h"
+
+int yylex(void);
+
+#define YY_NO_INPUT
+
+#define ECHO {;}
+#define YY_DECL int yylex(void)
+
+int yyerror(char *str);
+
+%}
+
+%option nounput
+
+space [ \t]
+linebreak \n
+comment \#.*\n
+keyword [A-Za-z0-9\_\-]+
+
+number [0-9]+
+string \".*\"
+bdaddr [A-Za-z0-9]{2}:[A-Za-z0-9]{2}:[A-Za-z0-9]{2}:[A-Za-z0-9]{2}:[A-Za-z0-9]{2}:[A-Za-z0-9]{2}
+
+%%
+
+{space} {
+ /* Skip spaces and tabs */
+ ;
+ }
+
+{comment} {
+ /* Skip comments */
+ lineno++;
+ }
+
+{number} {
+ yylval.number = atoi(yytext);
+ return NUMBER;
+ }
+
+{string} {
+ yylval.string = yytext;
+ return STRING;
+ }
+
+{bdaddr} {
+ bdaddr_t *ba = malloc(sizeof(bdaddr_t));
+ str2ba(yytext, ba);
+ yylval.bdaddr = ba;
+ return BDADDR;
+ }
+
+{keyword} {
+ int keyword = rfcomm_find_keyword(rfcomm_keyword, yytext);
+ if (keyword != -1)
+ return keyword;
+
+ if (strncmp(yytext, "rfcomm", 6) == 0) {
+ yylval.number = atoi(yytext + 6);
+ return RFCOMM;
+ }
+
+ yylval.string = yytext;
+ return WORD;
+ }
+
+{linebreak} {
+ lineno++;
+ }
+
+. {
+ return *yytext;
+ }
+
+%%
+
+int yywrap(void)
+{
+ return 1;
+}
diff --git a/tools/parser.c b/tools/parser.c
new file mode 100644
index 0000000..e9e1b6c
--- /dev/null
+++ b/tools/parser.c
@@ -0,0 +1,1768 @@
+
+/* A Bison parser, made by GNU Bison 2.4.1. */
+
+/* Skeleton implementation for Bison's Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+ Free Software Foundation, Inc.
+
+ 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 3 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, see <http://www.gnu.org/licenses/>. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+ simplifying the original so-called "semantic" parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "2.4.1"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 0
+
+/* Push parsers. */
+#define YYPUSH 0
+
+/* Pull parsers. */
+#define YYPULL 1
+
+/* Using locations. */
+#define YYLSP_NEEDED 0
+
+
+
+/* Copy the first part of user declarations. */
+
+/* Line 189 of yacc.c */
+#line 1 "parser.y"
+
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2010 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 <string.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+
+#include "kword.h"
+
+int yylex(void);
+int yyerror(char *s);
+
+struct rfcomm_opts *opts;
+
+
+
+/* Line 189 of yacc.c */
+#line 122 "tools/parser.c"
+
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* Enabling the token table. */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ K_BIND = 258,
+ K_DEVICE = 259,
+ K_CHANNEL = 260,
+ K_COMMENT = 261,
+ K_YES = 262,
+ K_NO = 263,
+ NUMBER = 264,
+ RFCOMM = 265,
+ STRING = 266,
+ WORD = 267,
+ BDADDR = 268
+ };
+#endif
+/* Tokens. */
+#define K_BIND 258
+#define K_DEVICE 259
+#define K_CHANNEL 260
+#define K_COMMENT 261
+#define K_YES 262
+#define K_NO 263
+#define NUMBER 264
+#define RFCOMM 265
+#define STRING 266
+#define WORD 267
+#define BDADDR 268
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+{
+
+/* Line 214 of yacc.c */
+#line 49 "parser.y"
+
+ int number;
+ char *string;
+ bdaddr_t *bdaddr;
+
+
+
+/* Line 214 of yacc.c */
+#line 192 "tools/parser.c"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+
+/* Copy the second part of user declarations. */
+
+
+/* Line 264 of yacc.c */
+#line 204 "tools/parser.c"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+# define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+# define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# else
+# define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(msgid) dgettext ("bison-runtime", msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions. */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int yyi)
+#else
+static int
+YYID (yyi)
+ int yyi;
+#endif
+{
+ return yyi;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# elif defined __BUILTIN_VA_ARG_INCR
+# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+# elif defined _AIX
+# define YYSTACK_ALLOC __alloca
+# elif defined _MSC_VER
+# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+# define alloca _alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef _STDLIB_H
+# define _STDLIB_H 1
+# endif
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+# endif
+# if (defined __cplusplus && ! defined _STDLIB_H \
+ && ! ((defined YYMALLOC || defined malloc) \
+ && (defined YYFREE || defined free)))
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef _STDLIB_H
+# define _STDLIB_H 1
+# endif
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifndef YYFREE
+# define YYFREE free
+# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+ && (! defined __cplusplus \
+ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ yytype_int16 yyss_alloc;
+ YYSTYPE yyvs_alloc;
+};
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+ + YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined __GNUC__ && 1 < __GNUC__
+# define YYCOPY(To, From, Count) \
+ __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+# else
+# define YYCOPY(To, From, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (To)[yyi] = (From)[yyi]; \
+ } \
+ while (YYID (0))
+# endif
+# endif
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack_alloc, Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \
+ Stack = &yyptr->Stack_alloc; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (YYID (0))
+
+#endif
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 8
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 40
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 17
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 8
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 20
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 33
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 268
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+static const yytype_uint8 yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 16,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 14, 2, 15, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const yytype_uint8 yyprhs[] =
+{
+ 0, 0, 3, 4, 6, 9, 14, 19, 21, 23,
+ 25, 27, 30, 33, 37, 40, 43, 46, 49, 51,
+ 53
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yytype_int8 yyrhs[] =
+{
+ 18, 0, -1, -1, 19, -1, 18, 19, -1, 20,
+ 14, 22, 15, -1, 21, 14, 22, 15, -1, 12,
+ -1, 1, -1, 12, -1, 10, -1, 23, 16, -1,
+ 1, 16, -1, 22, 23, 16, -1, 3, 24, -1,
+ 4, 13, -1, 5, 9, -1, 6, 11, -1, 12,
+ -1, 7, -1, 8, -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const yytype_uint8 yyrline[] =
+{
+ 0, 66, 66, 67, 68, 71, 72, 73, 76, 83,
+ 89, 98, 99, 100, 103, 108, 113, 118, 123, 129,
+ 130
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "K_BIND", "K_DEVICE", "K_CHANNEL",
+ "K_COMMENT", "K_YES", "K_NO", "NUMBER", "RFCOMM", "STRING", "WORD",
+ "BDADDR", "'{'", "'}'", "';'", "$accept", "config", "statement",
+ "section", "rfcomm", "rfcomm_options", "rfcomm_option", "bool", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const yytype_uint16 yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 123, 125, 59
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const yytype_uint8 yyr1[] =
+{
+ 0, 17, 18, 18, 18, 19, 19, 19, 19, 20,
+ 21, 22, 22, 22, 23, 23, 23, 23, 23, 24,
+ 24
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const yytype_uint8 yyr2[] =
+{
+ 0, 2, 0, 1, 2, 4, 4, 1, 1, 1,
+ 1, 2, 2, 3, 2, 2, 2, 2, 1, 1,
+ 1
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+ STATE-NUM when YYTABLE doesn't specify something else to do. Zero
+ means the default is an error. */
+static const yytype_uint8 yydefact[] =
+{
+ 0, 8, 10, 7, 0, 3, 0, 0, 1, 4,
+ 0, 0, 0, 0, 0, 0, 0, 18, 0, 0,
+ 0, 12, 19, 20, 14, 15, 16, 17, 5, 0,
+ 11, 6, 13
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yytype_int8 yydefgoto[] =
+{
+ -1, 4, 5, 6, 7, 18, 19, 24
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -7
+static const yytype_int8 yypact[] =
+{
+ 10, -7, -7, -6, 14, -7, 4, 7, -7, -7,
+ 24, 24, 15, 25, 21, 26, 12, -7, -3, 22,
+ 1, -7, -7, -7, -7, -7, -7, -7, -7, 23,
+ -7, -7, -7
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yytype_int8 yypgoto[] =
+{
+ -7, -7, 33, -7, -7, 29, -1, -7
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule which
+ number is the opposite. If zero, do what YYDEFACT says.
+ If YYTABLE_NINF, syntax error. */
+#define YYTABLE_NINF -10
+static const yytype_int8 yytable[] =
+{
+ 13, 14, 15, 16, 13, 14, 15, 16, -9, 17,
+ -2, 1, 28, 17, 8, 1, 31, 29, 10, 29,
+ 2, 11, 3, 27, 2, 12, 3, 13, 14, 15,
+ 16, 21, 22, 23, 25, 26, 17, 9, 30, 32,
+ 20
+};
+
+static const yytype_uint8 yycheck[] =
+{
+ 3, 4, 5, 6, 3, 4, 5, 6, 14, 12,
+ 0, 1, 15, 12, 0, 1, 15, 18, 14, 20,
+ 10, 14, 12, 11, 10, 1, 12, 3, 4, 5,
+ 6, 16, 7, 8, 13, 9, 12, 4, 16, 16,
+ 11
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const yytype_uint8 yystos[] =
+{
+ 0, 1, 10, 12, 18, 19, 20, 21, 0, 19,
+ 14, 14, 1, 3, 4, 5, 6, 12, 22, 23,
+ 22, 16, 7, 8, 24, 13, 9, 11, 15, 23,
+ 16, 15, 16
+};
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror. This remains here temporarily
+ to ease the transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+
+#define YYFAIL goto yyerrlab
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ yytoken = YYTRANSLATE (yychar); \
+ YYPOPSTACK (1); \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror (YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (YYID (0))
+
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+ If N is 0, then set CURRENT to the empty location which ends
+ the previous symbol: RHS[0] (always defined). */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do \
+ if (YYID (N)) \
+ { \
+ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
+ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
+ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \
+ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \
+ } \
+ else \
+ { \
+ (Current).first_line = (Current).last_line = \
+ YYRHSLOC (Rhs, 0).last_line; \
+ (Current).first_column = (Current).last_column = \
+ YYRHSLOC (Rhs, 0).last_column; \
+ } \
+ while (YYID (0))
+#endif
+
+
+/* YY_LOCATION_PRINT -- Print the location on the stream.
+ This macro was not mandated originally: define only if we know
+ we won't break user code: when these are the locations we know. */
+
+#ifndef YY_LOCATION_PRINT
+# if YYLTYPE_IS_TRIVIAL
+# define YY_LOCATION_PRINT(File, Loc) \
+ fprintf (File, "%d.%d-%d.%d", \
+ (Loc).first_line, (Loc).first_column, \
+ (Loc).last_line, (Loc).last_column)
+# else
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yy_symbol_print (stderr, \
+ Type, Value); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ if (!yyvaluep)
+ return;
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+ YYUSE (yyoutput);
+# endif
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ if (yytype < YYNTOKENS)
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+ yy_symbol_value_print (yyoutput, yytype, yyvaluep);
+ YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop)
+#else
+static void
+yy_stack_print (yybottom, yytop)
+ yytype_int16 *yybottom;
+ yytype_int16 *yytop;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (; yybottom <= yytop; yybottom++)
+ {
+ int yybot = *yybottom;
+ YYFPRINTF (stderr, " %d", yybot);
+ }
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, int yyrule)
+#else
+static void
+yy_reduce_print (yyvsp, yyrule)
+ YYSTYPE *yyvsp;
+ int yyrule;
+#endif
+{
+ int yynrhs = yyr2[yyrule];
+ int yyi;
+ unsigned long int yylno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+ yyrule - 1, yylno);
+ /* The symbols being reduced. */
+ for (yyi = 0; yyi < yynrhs; yyi++)
+ {
+ YYFPRINTF (stderr, " $%d = ", yyi + 1);
+ yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+ &(yyvsp[(yyi + 1) - (yynrhs)])
+ );
+ YYFPRINTF (stderr, "\n");
+ }
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (yyvsp, Rule); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined __GLIBC__ && defined _STRING_H
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+ const char *yystr;
+#endif
+{
+ YYSIZE_T yylen;
+ for (yylen = 0; yystr[yylen]; yylen++)
+ continue;
+ return yylen;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+#endif
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ YYSIZE_T yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into YYRESULT an error message about the unexpected token
+ YYCHAR while in state YYSTATE. Return the number of bytes copied,
+ including the terminating null byte. If YYRESULT is null, do not
+ copy anything; just return the number of bytes that would be
+ copied. As a special case, return 0 if an ordinary "syntax error"
+ message will do. Return YYSIZE_MAXIMUM if overflow occurs during
+ size calculation. */
+static YYSIZE_T
+yysyntax_error (char *yyresult, int yystate, int yychar)
+{
+ int yyn = yypact[yystate];
+
+ if (! (YYPACT_NINF < yyn && yyn <= YYLAST))
+ return 0;
+ else
+ {
+ int yytype = YYTRANSLATE (yychar);
+ YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
+ YYSIZE_T yysize = yysize0;
+ YYSIZE_T yysize1;
+ int yysize_overflow = 0;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ int yyx;
+
+# if 0
+ /* This is so xgettext sees the translatable formats that are
+ constructed on the fly. */
+ YY_("syntax error, unexpected %s");
+ YY_("syntax error, unexpected %s, expecting %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
+# endif
+ char *yyfmt;
+ char const *yyf;
+ static char const yyunexpected[] = "syntax error, unexpected %s";
+ static char const yyexpecting[] = ", expecting %s";
+ static char const yyor[] = " or %s";
+ char yyformat[sizeof yyunexpected
+ + sizeof yyexpecting - 1
+ + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
+ * (sizeof yyor - 1))];
+ char const *yyprefix = yyexpecting;
+
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn + 1;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yycount = 1;
+
+ yyarg[0] = yytname[yytype];
+ yyfmt = yystpcpy (yyformat, yyunexpected);
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ yyformat[sizeof yyunexpected - 1] = '\0';
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+ yyfmt = yystpcpy (yyfmt, yyprefix);
+ yyprefix = yyor;
+ }
+
+ yyf = YY_(yyformat);
+ yysize1 = yysize + yystrlen (yyf);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+
+ if (yysize_overflow)
+ return YYSIZE_MAXIMUM;
+
+ if (yyresult)
+ {
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ char *yyp = yyresult;
+ int yyi = 0;
+ while ((*yyp = *yyf) != '\0')
+ {
+ if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyf += 2;
+ }
+ else
+ {
+ yyp++;
+ yyf++;
+ }
+ }
+ }
+ return yysize;
+ }
+}
+#endif /* YYERROR_VERBOSE */
+
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep)
+ const char *yymsg;
+ int yytype;
+ YYSTYPE *yyvaluep;
+#endif
+{
+ YYUSE (yyvaluep);
+
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ switch (yytype)
+ {
+
+ default:
+ break;
+ }
+}
+
+/* Prevent warnings from -Wmissing-prototypes. */
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+/* The lookahead symbol. */
+int yychar;
+
+/* The semantic value of the lookahead symbol. */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far. */
+int yynerrs;
+
+
+
+/*-------------------------.
+| yyparse or yypush_parse. |
+`-------------------------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+
+
+ int yystate;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+
+ /* The stacks and their tools:
+ `yyss': related to states.
+ `yyvs': related to semantic values.
+
+ Refer to the stacks thru separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ yytype_int16 yyssa[YYINITDEPTH];
+ yytype_int16 *yyss;
+ yytype_int16 *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs;
+ YYSTYPE *yyvsp;
+
+ YYSIZE_T yystacksize;
+
+ int yyn;
+ int yyresult;
+ /* Lookahead token as an internal (translated) token number. */
+ int yytoken;
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
+#if YYERROR_VERBOSE
+ /* Buffer for error messages, and its allocated size. */
+ char yymsgbuf[128];
+ char *yymsg = yymsgbuf;
+ YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N))
+
+ /* The number of symbols on the RHS of the reduced rule.
+ Keep to zero when no symbol should be popped. */
+ int yylen = 0;
+
+ yytoken = 0;
+ yyss = yyssa;
+ yyvs = yyvsa;
+ yystacksize = YYINITDEPTH;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+ yyssp = yyss;
+ yyvsp = yyvs;
+
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. So pushing a state here evens the stacks. */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ yytype_int16 *yyss1 = yyss;
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ yytype_int16 *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss_alloc, yyss);
+ YYSTACK_RELOCATE (yyvs_alloc, yyvs);
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ if (yystate == YYFINAL)
+ YYACCEPT;
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+ /* Do appropriate processing given the current state. Read a
+ lookahead token if we need one and don't already have one. */
+
+ /* First try to decide what to do without reference to lookahead token. */
+ yyn = yypact[yystate];
+ if (yyn == YYPACT_NINF)
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yyn == 0 || yyn == YYTABLE_NINF)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ /* Shift the lookahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the shifted token. */
+ yychar = YYEMPTY;
+
+ yystate = yyn;
+ *++yyvsp = yylval;
+
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 7:
+
+/* Line 1455 of yacc.c */
+#line 74 "parser.y"
+ {
+ }
+ break;
+
+ case 8:
+
+/* Line 1455 of yacc.c */
+#line 77 "parser.y"
+ {
+ yyclearin;
+ yyerrok;
+ }
+ break;
+
+ case 9:
+
+/* Line 1455 of yacc.c */
+#line 84 "parser.y"
+ {
+ opts = NULL;
+ }
+ break;
+
+ case 10:
+
+/* Line 1455 of yacc.c */
+#line 90 "parser.y"
+ {
+ if (((yyvsp[(1) - (1)].number) >= 0) && ((yyvsp[(1) - (1)].number) < RFCOMM_MAX_DEV))
+ opts = &rfcomm_opts[(yyvsp[(1) - (1)].number)];
+ else
+ opts = NULL;
+ }
+ break;
+
+ case 14:
+
+/* Line 1455 of yacc.c */
+#line 104 "parser.y"
+ {
+ if (opts)
+ opts->bind = (yyvsp[(2) - (2)].number);
+ }
+ break;
+
+ case 15:
+
+/* Line 1455 of yacc.c */
+#line 109 "parser.y"
+ {
+ if (opts)
+ bacpy(&opts->bdaddr, (yyvsp[(2) - (2)].bdaddr));
+ }
+ break;
+
+ case 16:
+
+/* Line 1455 of yacc.c */
+#line 114 "parser.y"
+ {
+ if (opts)
+ opts->channel = (yyvsp[(2) - (2)].number);
+ }
+ break;
+
+ case 17:
+
+/* Line 1455 of yacc.c */
+#line 119 "parser.y"
+ {
+ if (opts)
+ snprintf(opts->comment, MAXCOMMENTLEN, "%s", (yyvsp[(2) - (2)].string));
+ }
+ break;
+
+ case 18:
+
+/* Line 1455 of yacc.c */
+#line 124 "parser.y"
+ {
+ // Unknown option
+ }
+ break;
+
+ case 19:
+
+/* Line 1455 of yacc.c */
+#line 129 "parser.y"
+ { (yyval.number) = 1; }
+ break;
+
+ case 20:
+
+/* Line 1455 of yacc.c */
+#line 130 "parser.y"
+ { (yyval.number) = 0; }
+ break;
+
+
+
+/* Line 1455 of yacc.c */
+#line 1517 "tools/parser.c"
+ default: break;
+ }
+ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+
+ /* Now `shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if ! YYERROR_VERBOSE
+ yyerror (YY_("syntax error"));
+#else
+ {
+ YYSIZE_T yysize = yysyntax_error (0, yystate, yychar);
+ if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM)
+ {
+ YYSIZE_T yyalloc = 2 * yysize;
+ if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM))
+ yyalloc = YYSTACK_ALLOC_MAXIMUM;
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = (char *) YYSTACK_ALLOC (yyalloc);
+ if (yymsg)
+ yymsg_alloc = yyalloc;
+ else
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ }
+ }
+
+ if (0 < yysize && yysize <= yymsg_alloc)
+ {
+ (void) yysyntax_error (yymsg, yystate, yychar);
+ yyerror (yymsg);
+ }
+ else
+ {
+ yyerror (YY_("syntax error"));
+ if (yysize != 0)
+ goto yyexhaustedlab;
+ }
+ }
+#endif
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse lookahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding",
+ yytoken, &yylval);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse lookahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (/*CONSTCOND*/ 0)
+ goto yyerrorlab;
+
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYERROR. */
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (yyn != YYPACT_NINF)
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+
+ yydestruct ("Error: popping",
+ yystos[yystate], yyvsp);
+ YYPOPSTACK (1);
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ *++yyvsp = yylval;
+
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#if !defined(yyoverflow) || YYERROR_VERBOSE
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ if (yychar != YYEMPTY)
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval);
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYABORT or YYACCEPT. */
+ YYPOPSTACK (yylen);
+ YY_STACK_PRINT (yyss, yyssp);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp);
+ YYPOPSTACK (1);
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+#endif
+ /* Make sure YYID is used. */
+ return YYID (yyresult);
+}
+
+
+
+/* Line 1675 of yacc.c */
+#line 133 "parser.y"
+
+
+int yyerror(char *s)
+{
+ fprintf(stderr, "%s line %d\n", s, lineno);
+ return 0;
+}
+
+int rfcomm_read_config(char *filename)
+{
+ extern FILE *yyin;
+ char file[MAXPATHLEN + 1];
+ int i;
+
+ for (i = 0; i < RFCOMM_MAX_DEV; i++) {
+ rfcomm_opts[i].bind = 0;
+ bacpy(&rfcomm_opts[i].bdaddr, BDADDR_ANY);
+ rfcomm_opts[i].channel = 1;
+ }
+
+ if (filename) {
+ snprintf(file, MAXPATHLEN, "%s", filename);
+ } else {
+ snprintf(file, MAXPATHLEN, "%s/.bluetooth/rfcomm.conf", getenv("HOME"));
+
+ if ((getuid() == 0) || (access(file, R_OK) < 0))
+ snprintf(file, MAXPATHLEN, "%s/rfcomm.conf", CONFIGDIR);
+ }
+
+ if (!(yyin = fopen(file, "r")))
+ return -1;
+
+ lineno = 1;
+ yyparse();
+
+ fclose(yyin);
+
+ return 0;
+}
+
diff --git a/tools/parser.h b/tools/parser.h
new file mode 100644
index 0000000..2c444d6
--- /dev/null
+++ b/tools/parser.h
@@ -0,0 +1,94 @@
+
+/* A Bison parser, made by GNU Bison 2.4.1. */
+
+/* Skeleton interface for Bison's Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+ Free Software Foundation, Inc.
+
+ 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 3 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, see <http://www.gnu.org/licenses/>. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ K_BIND = 258,
+ K_DEVICE = 259,
+ K_CHANNEL = 260,
+ K_COMMENT = 261,
+ K_YES = 262,
+ K_NO = 263,
+ NUMBER = 264,
+ RFCOMM = 265,
+ STRING = 266,
+ WORD = 267,
+ BDADDR = 268
+ };
+#endif
+/* Tokens. */
+#define K_BIND 258
+#define K_DEVICE 259
+#define K_CHANNEL 260
+#define K_COMMENT 261
+#define K_YES 262
+#define K_NO 263
+#define NUMBER 264
+#define RFCOMM 265
+#define STRING 266
+#define WORD 267
+#define BDADDR 268
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+{
+
+/* Line 1676 of yacc.c */
+#line 49 "parser.y"
+
+ int number;
+ char *string;
+ bdaddr_t *bdaddr;
+
+
+
+/* Line 1676 of yacc.c */
+#line 86 "tools/parser.h"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+extern YYSTYPE yylval;
+
+
diff --git a/tools/parser.y b/tools/parser.y
new file mode 100644
index 0000000..96e6a56
--- /dev/null
+++ b/tools/parser.y
@@ -0,0 +1,171 @@
+%{
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2010 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 <string.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+
+#include "kword.h"
+
+int yylex(void);
+int yyerror(char *s);
+
+struct rfcomm_opts *opts;
+
+%}
+
+%union {
+ int number;
+ char *string;
+ bdaddr_t *bdaddr;
+}
+
+%token K_BIND K_DEVICE K_CHANNEL K_COMMENT
+%token K_YES K_NO
+
+%token <number> NUMBER RFCOMM
+%token <string> STRING WORD
+%token <bdaddr> BDADDR
+
+%type <number> bool
+
+%%
+
+config :
+ | statement
+ | config statement
+ ;
+
+statement : section '{' rfcomm_options '}'
+ | rfcomm '{' rfcomm_options '}'
+ | WORD
+ {
+ }
+ | error
+ {
+ yyclearin;
+ yyerrok;
+ }
+ ;
+
+section : WORD
+ {
+ opts = NULL;
+ }
+ ;
+
+rfcomm : RFCOMM
+ {
+ if (($1 >= 0) && ($1 < RFCOMM_MAX_DEV))
+ opts = &rfcomm_opts[$1];
+ else
+ opts = NULL;
+ }
+ ;
+
+rfcomm_options : rfcomm_option ';'
+ | error ';'
+ | rfcomm_options rfcomm_option ';'
+ ;
+
+rfcomm_option : K_BIND bool
+ {
+ if (opts)
+ opts->bind = $2;
+ }
+ | K_DEVICE BDADDR
+ {
+ if (opts)
+ bacpy(&opts->bdaddr, $2);
+ }
+ | K_CHANNEL NUMBER
+ {
+ if (opts)
+ opts->channel = $2;
+ }
+ | K_COMMENT STRING
+ {
+ if (opts)
+ snprintf(opts->comment, MAXCOMMENTLEN, "%s", $2);
+ }
+ | WORD
+ {
+ // Unknown option
+ }
+ ;
+
+bool : K_YES { $$ = 1; }
+ | K_NO { $$ = 0; }
+ ;
+
+%%
+
+int yyerror(char *s)
+{
+ fprintf(stderr, "%s line %d\n", s, lineno);
+ return 0;
+}
+
+int rfcomm_read_config(char *filename)
+{
+ extern FILE *yyin;
+ char file[MAXPATHLEN + 1];
+ int i;
+
+ for (i = 0; i < RFCOMM_MAX_DEV; i++) {
+ rfcomm_opts[i].bind = 0;
+ bacpy(&rfcomm_opts[i].bdaddr, BDADDR_ANY);
+ rfcomm_opts[i].channel = 1;
+ }
+
+ if (filename) {
+ snprintf(file, MAXPATHLEN, "%s", filename);
+ } else {
+ snprintf(file, MAXPATHLEN, "%s/.bluetooth/rfcomm.conf", getenv("HOME"));
+
+ if ((getuid() == 0) || (access(file, R_OK) < 0))
+ snprintf(file, MAXPATHLEN, "%s/rfcomm.conf", CONFIGDIR);
+ }
+
+ if (!(yyin = fopen(file, "r")))
+ return -1;
+
+ lineno = 1;
+ yyparse();
+
+ fclose(yyin);
+
+ return 0;
+}
diff --git a/tools/ppporc.c b/tools/ppporc.c
new file mode 100644
index 0000000..ca44b40
--- /dev/null
+++ b/tools/ppporc.c
@@ -0,0 +1,271 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2010 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 <syslog.h>
+#include <getopt.h>
+#include <sys/poll.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+
+/* IO cancelation */
+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;
+}
+
+/* Signal functions */
+static void sig_hup(int sig)
+{
+ return;
+}
+
+static void sig_term(int sig)
+{
+ syslog(LOG_INFO, "Closing RFCOMM channel");
+ io_cancel();
+}
+
+/* Read exactly len bytes (Signal safe)*/
+static inline int read_n(int fd, char *buf, int len)
+{
+ register int t = 0, w;
+
+ 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, char *buf, int len)
+{
+ register int t = 0, w;
+
+ 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;
+}
+
+/* Create the RFCOMM connection */
+static int create_connection(bdaddr_t *bdaddr, uint8_t channel)
+{
+ struct sockaddr_rc remote_addr, local_addr;
+ int fd, err;
+
+ if ((fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0)
+ return fd;
+
+ memset(&local_addr, 0, sizeof(local_addr));
+ local_addr.rc_family = AF_BLUETOOTH;
+ bacpy(&local_addr.rc_bdaddr, BDADDR_ANY);
+ if ((err = bind(fd, (struct sockaddr *)&local_addr, sizeof(local_addr))) < 0) {
+ close(fd);
+ return err;
+ }
+
+ memset(&remote_addr, 0, sizeof(remote_addr));
+ remote_addr.rc_family = AF_BLUETOOTH;
+ bacpy(&remote_addr.rc_bdaddr, bdaddr);
+ remote_addr.rc_channel = channel;
+ if ((err = connect(fd, (struct sockaddr *)&remote_addr, sizeof(remote_addr))) < 0) {
+ close(fd);
+ return err;
+ }
+
+ syslog(LOG_INFO, "RFCOMM channel %d connected", channel);
+
+ return fd;
+}
+
+/* Process the data from socket and pseudo tty */
+static int process_data(int fd)
+{
+ struct pollfd p[2];
+ char buf[1024];
+ int err, r;
+
+ p[0].fd = 0;
+ p[0].events = POLLIN | POLLERR | POLLHUP | POLLNVAL;
+
+ p[1].fd = fd;
+ p[1].events = POLLIN | POLLERR | POLLHUP | POLLNVAL;
+
+ err = 0;
+
+ while (!__io_canceled) {
+ p[0].revents = 0;
+ p[1].revents = 0;
+
+ err = poll(p, 2, -1);
+ if (err < 0)
+ break;
+
+ err = 0;
+
+ if (p[0].revents) {
+ if (p[0].revents & (POLLERR | POLLHUP | POLLNVAL))
+ break;
+ r = read(0, buf, sizeof(buf));
+ if (r < 0) {
+ if (errno != EINTR && errno != EAGAIN) {
+ err = r;
+ break;
+ }
+ }
+
+ err = write_n(fd, buf, r);
+ if (err < 0)
+ break;
+ }
+
+ if (p[1].revents) {
+ if (p[1].revents & (POLLERR | POLLHUP | POLLNVAL))
+ break;
+ r = read(fd, buf, sizeof(buf));
+ if (r < 0) {
+ if (errno != EINTR && errno != EAGAIN) {
+ err = r;
+ break;
+ }
+ }
+
+ err = write_n(1, buf, r);
+ if (err < 0)
+ break;
+ }
+ }
+
+ return err;
+}
+
+static void usage(void)
+{
+ printf("Usage:\tppporc <bdaddr> [channel]\n");
+}
+
+int main(int argc, char** argv)
+{
+ struct sigaction sa;
+ int fd, err, opt;
+
+ bdaddr_t bdaddr;
+ uint8_t channel;
+
+ /* Parse command line options */
+ while ((opt = getopt(argc, argv, "h")) != EOF) {
+ switch(opt) {
+ case 'h':
+ usage();
+ exit(0);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ switch (argc) {
+ case 1:
+ str2ba(argv[0], &bdaddr);
+ channel = 1;
+ break;
+ case 2:
+ str2ba(argv[0], &bdaddr);
+ channel = atoi(argv[1]);
+ break;
+ default:
+ usage();
+ exit(0);
+ }
+
+ /* Initialize syslog */
+ openlog("ppporc", LOG_PID | LOG_NDELAY | LOG_PERROR, LOG_DAEMON);
+ syslog(LOG_INFO, "PPP over RFCOMM");
+
+ /* Initialize signals */
+ 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);
+
+ sa.sa_handler = sig_hup;
+ sigaction(SIGHUP, &sa, NULL);
+
+ syslog(LOG_INFO, "Connecting to %s", argv[0]);
+
+ if ((fd = create_connection(&bdaddr, channel)) < 0) {
+ syslog(LOG_ERR, "Can't connect to remote device (%s)", strerror(errno));
+ return fd;
+ }
+
+ err = process_data(fd);
+
+ close(fd);
+
+ return err;
+}
diff --git a/tools/rfcomm.1 b/tools/rfcomm.1
new file mode 100644
index 0000000..06252e5
--- /dev/null
+++ b/tools/rfcomm.1
@@ -0,0 +1,137 @@
+.\"
+.\" 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.TH RFCOMM 1 "APRIL 28, 2002" "" ""
+
+.SH NAME
+rfcomm \- RFCOMM configuration utility
+.SH SYNOPSIS
+.BR "rfcomm
+[
+.I options
+] <
+.I command
+> <
+.I dev
+>
+.SH DESCRIPTION
+.B rfcomm
+is used to set up, maintain, and inspect the RFCOMM configuration
+of the Bluetooth subsystem in the Linux kernel. If no
+.B command
+is given, or if the option
+.B -a
+is used,
+.B rfcomm
+prints information about the configured RFCOMM devices.
+.SH OPTIONS
+.TP
+.BI -h
+Gives a list of possible commands.
+.TP
+.BI -a
+Prints information about all configured RFCOMM devices.
+.TP
+.BI -r
+Switch TTY into raw mode (doesn't work with "bind").
+.TP
+.BI -f " <file>"
+Specify alternate config file.
+.TP
+.BI -i " <hciX> | <bdaddr>"
+The command is applied to device
+.BI -A
+Enable authentication.
+.BI -E
+Enable encryption.
+.BI -S
+Secure connection.
+.BI -M
+Become the master of a piconet.
+.I
+hciX
+, which must be the name or the address of an installed Bluetooth
+device. If not specified, the command will be use the first
+available Bluetooth device.
+.TP
+.BI -A
+Enable authentification
+.TP
+.BI -E
+Enable encryption
+.TP
+.BI -S
+Secure connection
+.TP
+.BI -M
+Become the master of a piconet
+.TP
+.BI -L " <seconds>"
+Set linger timeout
+.SH COMMANDS
+.TP
+.BI show " <dev>"
+Display the information about the specified device.
+.TP
+.BI connect " <dev> [bdaddr] [channel]"
+Connect the RFCOMM device to the remote Bluetooth device on the
+specified channel. If no channel is specified, it will use the
+channel number 1. If also the Bluetooth address is left out, it
+tries to read the data from the config file. This command can
+be terminated with the key sequence CTRL-C.
+.TP
+.BI listen " <dev> [channel] [cmd]"
+Listen on a specified RFCOMM channel for incoming connections.
+If no channel is specified, it will use the channel number 1, but
+a channel must be specified before cmd. If cmd is given, it will be
+executed as soon as a client connects. When the child process
+terminates or the client disconnect, the command will terminate.
+Occurences of {} in cmd will be replaced by the name of the device
+used by the connection. This command can be terminated with the key
+sequence CTRL-C.
+.TP
+.BI watch " <dev> [channel] [cmd]"
+Watch is identical to
+.B listen
+except that when the child process terminates or the client
+disconnect, the command will restart listening with the same
+parameters.
+.TP
+.BI bind " <dev> [bdaddr] [channel]"
+This binds the RFCOMM device to a remote Bluetooth device. The
+command did not establish a connection to the remote device, it
+only creates the binding. The connection will be established right
+after an application tries to open the RFCOMM device. If no channel
+number is specified, it uses the channel number 1. If the Bluetooth
+address is also left out, it tries to read the data from the config
+file.
+
+If
+.B all
+is specified for the RFCOMM device, then all devices that have
+.B "bind yes"
+set in the config will be bound.
+.TP
+.BI release " <dev>"
+This command releases a defined RFCOMM binding.
+
+If
+.B all
+is specified for the RFCOMM device, then all bindings will be removed.
+This command didn't care about the settings in the config file.
+.SH AUTHOR
+Written by Marcel Holtmann <marcel@holtmann.org>.
+.br
diff --git a/tools/rfcomm.c b/tools/rfcomm.c
new file mode 100644
index 0000000..6800445
--- /dev/null
+++ b/tools/rfcomm.c
@@ -0,0 +1,849 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2010 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
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <signal.h>
+#include <termios.h>
+#include <sys/poll.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/rfcomm.h>
+
+#include "kword.h"
+
+#ifdef NEED_PPOLL
+#include "ppoll.h"
+#endif
+
+static char *rfcomm_config_file = NULL;
+static int rfcomm_raw_tty = 0;
+static int auth = 0;
+static int encryption = 0;
+static int secure = 0;
+static int master = 0;
+static int linger = 0;
+
+static char *rfcomm_state[] = {
+ "unknown",
+ "connected",
+ "clean",
+ "bound",
+ "listening",
+ "connecting",
+ "connecting",
+ "config",
+ "disconnecting",
+ "closed"
+};
+
+static volatile sig_atomic_t __io_canceled = 0;
+
+static void sig_hup(int sig)
+{
+ return;
+}
+
+static void sig_term(int sig)
+{
+ __io_canceled = 1;
+}
+
+static char *rfcomm_flagstostr(uint32_t flags)
+{
+ static char str[100];
+ str[0] = 0;
+
+ strcat(str, "[");
+
+ if (flags & (1 << RFCOMM_REUSE_DLC))
+ strcat(str, "reuse-dlc ");
+
+ if (flags & (1 << RFCOMM_RELEASE_ONHUP))
+ strcat(str, "release-on-hup ");
+
+ if (flags & (1 << RFCOMM_TTY_ATTACHED))
+ strcat(str, "tty-attached");
+
+ strcat(str, "]");
+ return str;
+}
+
+static void print_dev_info(struct rfcomm_dev_info *di)
+{
+ char src[18], dst[18], addr[40];
+
+ ba2str(&di->src, src); ba2str(&di->dst, dst);
+
+ if (bacmp(&di->src, BDADDR_ANY) == 0)
+ sprintf(addr, "%s", dst);
+ else
+ sprintf(addr, "%s -> %s", src, dst);
+
+ printf("rfcomm%d: %s channel %d %s %s\n",
+ di->id, addr, di->channel,
+ rfcomm_state[di->state],
+ di->flags ? rfcomm_flagstostr(di->flags) : "");
+}
+
+static void print_dev_list(int ctl, int flags)
+{
+ struct rfcomm_dev_list_req *dl;
+ struct rfcomm_dev_info *di;
+ int i;
+
+ dl = malloc(sizeof(*dl) + RFCOMM_MAX_DEV * sizeof(*di));
+ if (!dl) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+
+ dl->dev_num = RFCOMM_MAX_DEV;
+ di = dl->dev_info;
+
+ if (ioctl(ctl, RFCOMMGETDEVLIST, (void *) dl) < 0) {
+ perror("Can't get device list");
+ free(dl);
+ exit(1);
+ }
+
+ for (i = 0; i < dl->dev_num; i++)
+ print_dev_info(di + i);
+ free(dl);
+}
+
+static int create_dev(int ctl, int dev, uint32_t flags, bdaddr_t *bdaddr, int argc, char **argv)
+{
+ struct rfcomm_dev_req req;
+ int err;
+
+ memset(&req, 0, sizeof(req));
+ req.dev_id = dev;
+ req.flags = flags;
+ bacpy(&req.src, bdaddr);
+
+ if (argc < 2) {
+ err = rfcomm_read_config(rfcomm_config_file);
+ if (err < 0) {
+ perror("Can't open RFCOMM config file");
+ return err;
+ }
+
+ bacpy(&req.dst, &rfcomm_opts[dev].bdaddr);
+ req.channel = rfcomm_opts[dev].channel;
+
+ if (bacmp(&req.dst, BDADDR_ANY) == 0) {
+ fprintf(stderr, "Can't find a config entry for rfcomm%d\n", dev);
+ return -EFAULT;
+ }
+ } else {
+ str2ba(argv[1], &req.dst);
+
+ if (argc > 2)
+ req.channel = atoi(argv[2]);
+ else
+ req.channel = 1;
+ }
+
+ err = ioctl(ctl, RFCOMMCREATEDEV, &req);
+ if (err == EOPNOTSUPP)
+ fprintf(stderr, "RFCOMM TTY support not available\n");
+ else if (err < 0)
+ perror("Can't create device");
+
+ return err;
+}
+
+static int create_all(int ctl)
+{
+ struct rfcomm_dev_req req;
+ int i, err;
+
+ err = rfcomm_read_config(rfcomm_config_file);
+ if (err < 0) {
+ perror("Can't open RFCOMM config file");
+ return err;
+ }
+
+ for (i = 0; i < RFCOMM_MAX_DEV; i++) {
+ if (!rfcomm_opts[i].bind)
+ continue;
+
+ memset(&req, 0, sizeof(req));
+ req.dev_id = i;
+ req.flags = 0;
+ bacpy(&req.src, BDADDR_ANY);
+ bacpy(&req.dst, &rfcomm_opts[i].bdaddr);
+ req.channel = rfcomm_opts[i].channel;
+
+ if (bacmp(&req.dst, BDADDR_ANY) != 0)
+ ioctl(ctl, RFCOMMCREATEDEV, &req);
+ }
+
+ return 0;
+}
+
+static int release_dev(int ctl, int dev, uint32_t flags)
+{
+ struct rfcomm_dev_req req;
+ int err;
+
+ memset(&req, 0, sizeof(req));
+ req.dev_id = dev;
+
+ err = ioctl(ctl, RFCOMMRELEASEDEV, &req);
+ if (err < 0)
+ perror("Can't release device");
+
+ return err;
+}
+
+static int release_all(int ctl)
+{
+ struct rfcomm_dev_list_req *dl;
+ struct rfcomm_dev_info *di;
+ int i;
+
+ dl = malloc(sizeof(*dl) + RFCOMM_MAX_DEV * sizeof(*di));
+ if (!dl) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+
+ dl->dev_num = RFCOMM_MAX_DEV;
+ di = dl->dev_info;
+
+ if (ioctl(ctl, RFCOMMGETDEVLIST, (void *) dl) < 0) {
+ perror("Can't get device list");
+ free(dl);
+ exit(1);
+ }
+
+ for (i = 0; i < dl->dev_num; i++)
+ release_dev(ctl, (di + i)->id, 0);
+
+ free(dl);
+ return 0;
+}
+
+static void run_cmdline(struct pollfd *p, sigset_t* sigs, char *devname,
+ int argc, char **argv)
+{
+ int i;
+ pid_t pid;
+ char **cmdargv;
+
+ cmdargv = malloc((argc + 1) * sizeof(char*));
+ if (!cmdargv)
+ return;
+
+ for (i = 0; i < argc; i++)
+ cmdargv[i] = (strcmp(argv[i], "{}") == 0) ? devname : argv[i];
+ cmdargv[i] = NULL;
+
+ pid = fork();
+
+ switch (pid) {
+ case 0:
+ i = execvp(cmdargv[0], cmdargv);
+ fprintf(stderr, "Couldn't execute command %s (errno=%d:%s)\n",
+ cmdargv[0], errno, strerror(errno));
+ break;
+ case -1:
+ fprintf(stderr, "Couldn't fork to execute command %s\n",
+ cmdargv[0]);
+ break;
+ default:
+ while (1) {
+ int status;
+ pid_t child;
+ struct timespec ts;
+
+ child = waitpid(-1, &status, WNOHANG);
+ if (child == pid || (child < 0 && errno != EAGAIN))
+ break;
+
+ p->revents = 0;
+ ts.tv_sec = 0;
+ ts.tv_nsec = 200;
+ if (ppoll(p, 1, &ts, sigs) || __io_canceled) {
+ kill(pid, SIGTERM);
+ waitpid(pid, &status, 0);
+ break;
+ }
+ }
+ break;
+ }
+
+ free(cmdargv);
+}
+
+static void cmd_connect(int ctl, int dev, bdaddr_t *bdaddr, int argc, char **argv)
+{
+ struct sockaddr_rc laddr, raddr;
+ struct rfcomm_dev_req req;
+ struct termios ti;
+ struct sigaction sa;
+ struct pollfd p;
+ sigset_t sigs;
+ socklen_t alen;
+ char dst[18], devname[MAXPATHLEN];
+ int sk, fd, try = 30;
+
+ laddr.rc_family = AF_BLUETOOTH;
+ bacpy(&laddr.rc_bdaddr, bdaddr);
+ laddr.rc_channel = 0;
+
+ if (argc < 2) {
+ if (rfcomm_read_config(rfcomm_config_file) < 0) {
+ perror("Can't open RFCOMM config file");
+ return;
+ }
+
+ raddr.rc_family = AF_BLUETOOTH;
+ bacpy(&raddr.rc_bdaddr, &rfcomm_opts[dev].bdaddr);
+ raddr.rc_channel = rfcomm_opts[dev].channel;
+
+ if (bacmp(&raddr.rc_bdaddr, BDADDR_ANY) == 0) {
+ fprintf(stderr, "Can't find a config entry for rfcomm%d\n", dev);
+ return;
+ }
+ } else {
+ raddr.rc_family = AF_BLUETOOTH;
+ str2ba(argv[1], &raddr.rc_bdaddr);
+
+ if (argc > 2)
+ raddr.rc_channel = atoi(argv[2]);
+ else
+ raddr.rc_channel = 1;
+ }
+
+ sk = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+ if (sk < 0) {
+ perror("Can't create RFCOMM socket");
+ return;
+ }
+
+ if (linger) {
+ struct linger l = { .l_onoff = 1, .l_linger = linger };
+
+ if (setsockopt(sk, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0) {
+ perror("Can't set linger option");
+ return;
+ }
+ }
+
+ if (bind(sk, (struct sockaddr *) &laddr, sizeof(laddr)) < 0) {
+ perror("Can't bind RFCOMM socket");
+ close(sk);
+ return;
+ }
+
+ if (connect(sk, (struct sockaddr *) &raddr, sizeof(raddr)) < 0) {
+ perror("Can't connect RFCOMM socket");
+ close(sk);
+ return;
+ }
+
+ alen = sizeof(laddr);
+ if (getsockname(sk, (struct sockaddr *)&laddr, &alen) < 0) {
+ perror("Can't get RFCOMM socket name");
+ close(sk);
+ return;
+ }
+
+ memset(&req, 0, sizeof(req));
+ req.dev_id = dev;
+ req.flags = (1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP);
+
+ bacpy(&req.src, &laddr.rc_bdaddr);
+ bacpy(&req.dst, &raddr.rc_bdaddr);
+ req.channel = raddr.rc_channel;
+
+ dev = ioctl(sk, RFCOMMCREATEDEV, &req);
+ if (dev < 0) {
+ perror("Can't create RFCOMM TTY");
+ close(sk);
+ return;
+ }
+
+ snprintf(devname, MAXPATHLEN - 1, "/dev/rfcomm%d", dev);
+ while ((fd = open(devname, O_RDONLY | O_NOCTTY)) < 0) {
+ if (errno == EACCES) {
+ perror("Can't open RFCOMM device");
+ goto release;
+ }
+
+ snprintf(devname, MAXPATHLEN - 1, "/dev/bluetooth/rfcomm/%d", dev);
+ if ((fd = open(devname, O_RDONLY | O_NOCTTY)) < 0) {
+ if (try--) {
+ snprintf(devname, MAXPATHLEN - 1, "/dev/rfcomm%d", dev);
+ usleep(100 * 1000);
+ continue;
+ }
+ perror("Can't open RFCOMM device");
+ goto release;
+ }
+ }
+
+ if (rfcomm_raw_tty) {
+ tcflush(fd, TCIOFLUSH);
+
+ cfmakeraw(&ti);
+ tcsetattr(fd, TCSANOW, &ti);
+ }
+
+ close(sk);
+
+ ba2str(&req.dst, dst);
+ printf("Connected %s to %s on channel %d\n", devname, dst, req.channel);
+ printf("Press CTRL-C for hangup\n");
+
+ 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);
+
+ sa.sa_handler = sig_hup;
+ sigaction(SIGHUP, &sa, NULL);
+
+ sigfillset(&sigs);
+ sigdelset(&sigs, SIGCHLD);
+ sigdelset(&sigs, SIGPIPE);
+ sigdelset(&sigs, SIGTERM);
+ sigdelset(&sigs, SIGINT);
+ sigdelset(&sigs, SIGHUP);
+
+ p.fd = fd;
+ p.events = POLLERR | POLLHUP;
+
+ while (!__io_canceled) {
+ p.revents = 0;
+ if (ppoll(&p, 1, NULL, &sigs) > 0)
+ break;
+ }
+
+ printf("Disconnected\n");
+
+ close(fd);
+ return;
+
+release:
+ memset(&req, 0, sizeof(req));
+ req.dev_id = dev;
+ req.flags = (1 << RFCOMM_HANGUP_NOW);
+ ioctl(ctl, RFCOMMRELEASEDEV, &req);
+
+ close(sk);
+}
+
+static void cmd_listen(int ctl, int dev, bdaddr_t *bdaddr, int argc, char **argv)
+{
+ struct sockaddr_rc laddr, raddr;
+ struct rfcomm_dev_req req;
+ struct termios ti;
+ struct sigaction sa;
+ struct pollfd p;
+ sigset_t sigs;
+ socklen_t alen;
+ char dst[18], devname[MAXPATHLEN];
+ int sk, nsk, fd, lm, try = 30;
+
+ laddr.rc_family = AF_BLUETOOTH;
+ bacpy(&laddr.rc_bdaddr, bdaddr);
+ laddr.rc_channel = (argc < 2) ? 1 : atoi(argv[1]);
+
+ sk = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+ if (sk < 0) {
+ perror("Can't create RFCOMM socket");
+ return;
+ }
+
+ lm = 0;
+ if (master)
+ lm |= RFCOMM_LM_MASTER;
+ if (auth)
+ lm |= RFCOMM_LM_AUTH;
+ if (encryption)
+ lm |= RFCOMM_LM_ENCRYPT;
+ if (secure)
+ lm |= RFCOMM_LM_SECURE;
+
+ if (lm && setsockopt(sk, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) {
+ perror("Can't set RFCOMM link mode");
+ close(sk);
+ return;
+ }
+
+ if (bind(sk, (struct sockaddr *)&laddr, sizeof(laddr)) < 0) {
+ perror("Can't bind RFCOMM socket");
+ close(sk);
+ return;
+ }
+
+ printf("Waiting for connection on channel %d\n", laddr.rc_channel);
+
+ listen(sk, 10);
+
+ alen = sizeof(raddr);
+ nsk = accept(sk, (struct sockaddr *) &raddr, &alen);
+
+ alen = sizeof(laddr);
+ if (getsockname(nsk, (struct sockaddr *)&laddr, &alen) < 0) {
+ perror("Can't get RFCOMM socket name");
+ close(nsk);
+ return;
+ }
+
+ if (linger) {
+ struct linger l = { .l_onoff = 1, .l_linger = linger };
+
+ if (setsockopt(nsk, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0) {
+ perror("Can't set linger option");
+ close(nsk);
+ return;
+ }
+ }
+
+ memset(&req, 0, sizeof(req));
+ req.dev_id = dev;
+ req.flags = (1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP);
+
+ bacpy(&req.src, &laddr.rc_bdaddr);
+ bacpy(&req.dst, &raddr.rc_bdaddr);
+ req.channel = raddr.rc_channel;
+
+ dev = ioctl(nsk, RFCOMMCREATEDEV, &req);
+ if (dev < 0) {
+ perror("Can't create RFCOMM TTY");
+ close(sk);
+ return;
+ }
+
+ snprintf(devname, MAXPATHLEN - 1, "/dev/rfcomm%d", dev);
+ while ((fd = open(devname, O_RDONLY | O_NOCTTY)) < 0) {
+ if (errno == EACCES) {
+ perror("Can't open RFCOMM device");
+ goto release;
+ }
+
+ snprintf(devname, MAXPATHLEN - 1, "/dev/bluetooth/rfcomm/%d", dev);
+ if ((fd = open(devname, O_RDONLY | O_NOCTTY)) < 0) {
+ if (try--) {
+ snprintf(devname, MAXPATHLEN - 1, "/dev/rfcomm%d", dev);
+ usleep(100 * 1000);
+ continue;
+ }
+ perror("Can't open RFCOMM device");
+ goto release;
+ }
+ }
+
+ if (rfcomm_raw_tty) {
+ tcflush(fd, TCIOFLUSH);
+
+ cfmakeraw(&ti);
+ tcsetattr(fd, TCSANOW, &ti);
+ }
+
+ close(sk);
+ close(nsk);
+
+ ba2str(&req.dst, dst);
+ printf("Connection from %s to %s\n", dst, devname);
+ printf("Press CTRL-C for hangup\n");
+
+ 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);
+
+ sa.sa_handler = sig_hup;
+ sigaction(SIGHUP, &sa, NULL);
+
+ sigfillset(&sigs);
+ sigdelset(&sigs, SIGCHLD);
+ sigdelset(&sigs, SIGPIPE);
+ sigdelset(&sigs, SIGTERM);
+ sigdelset(&sigs, SIGINT);
+ sigdelset(&sigs, SIGHUP);
+
+ p.fd = fd;
+ p.events = POLLERR | POLLHUP;
+
+ if (argc <= 2) {
+ while (!__io_canceled) {
+ p.revents = 0;
+ if (ppoll(&p, 1, NULL, &sigs) > 0)
+ break;
+ }
+ } else
+ run_cmdline(&p, &sigs, devname, argc - 2, argv + 2);
+
+ sa.sa_handler = NULL;
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+
+ printf("Disconnected\n");
+
+ close(fd);
+ return;
+
+release:
+ memset(&req, 0, sizeof(req));
+ req.dev_id = dev;
+ req.flags = (1 << RFCOMM_HANGUP_NOW);
+ ioctl(ctl, RFCOMMRELEASEDEV, &req);
+
+ close(sk);
+}
+
+static void cmd_watch(int ctl, int dev, bdaddr_t *bdaddr, int argc, char **argv)
+{
+ while (!__io_canceled) {
+ cmd_listen(ctl, dev, bdaddr, argc, argv);
+ usleep(10000);
+ }
+}
+
+static void cmd_create(int ctl, int dev, bdaddr_t *bdaddr, int argc, char **argv)
+{
+ if (strcmp(argv[0], "all") == 0)
+ create_all(ctl);
+ else
+ create_dev(ctl, dev, 0, bdaddr, argc, argv);
+}
+
+static void cmd_release(int ctl, int dev, bdaddr_t *bdaddr, int argc, char **argv)
+{
+ if (strcmp(argv[0], "all") == 0)
+ release_all(ctl);
+ else
+ release_dev(ctl, dev, 0);
+}
+
+static void cmd_show(int ctl, int dev, bdaddr_t *bdaddr, int argc, char **argv)
+{
+ if (strcmp(argv[0], "all") == 0)
+ print_dev_list(ctl, 0);
+ else {
+ struct rfcomm_dev_info di = { id: atoi(argv[0]) };
+ if (ioctl(ctl, RFCOMMGETDEVINFO, &di) < 0) {
+ perror("Get info failed");
+ exit(1);
+ }
+
+ print_dev_info(&di);
+ }
+}
+
+struct {
+ char *cmd;
+ char *alt;
+ void (*func)(int ctl, int dev, bdaddr_t *bdaddr, int argc, char **argv);
+ char *opt;
+ char *doc;
+} command[] = {
+ { "bind", "create", cmd_create, "<dev> <bdaddr> [channel]", "Bind device" },
+ { "release", "unbind", cmd_release, "<dev>", "Release device" },
+ { "show", "info", cmd_show, "<dev>", "Show device" },
+ { "connect", "conn", cmd_connect, "<dev> <bdaddr> [channel]", "Connect device" },
+ { "listen", "server", cmd_listen, "<dev> [channel [cmd]]", "Listen" },
+ { "watch", "watch", cmd_watch, "<dev> [channel [cmd]]", "Watch" },
+ { NULL, NULL, NULL, 0, 0 }
+};
+
+static void usage(void)
+{
+ int i;
+
+ printf("RFCOMM configuration utility ver %s\n", VERSION);
+
+ printf("Usage:\n"
+ "\trfcomm [options] <command> <dev>\n"
+ "\n");
+
+ printf("Options:\n"
+ "\t-i [hciX|bdaddr] Local HCI device or BD Address\n"
+ "\t-h, --help Display help\n"
+ "\t-r, --raw Switch TTY into raw mode\n"
+ "\t-A, --auth Enable authentication\n"
+ "\t-E, --encrypt Enable encryption\n"
+ "\t-S, --secure Secure connection\n"
+ "\t-M, --master Become the master of a piconet\n"
+ "\t-f, --config [file] Specify alternate config file\n"
+ "\t-a Show all devices (default)\n"
+ "\n");
+
+ printf("Commands:\n");
+ for (i = 0; command[i].cmd; i++)
+ printf("\t%-8s %-24s\t%s\n",
+ command[i].cmd,
+ command[i].opt ? command[i].opt : " ",
+ command[i].doc);
+ printf("\n");
+}
+
+static struct option main_options[] = {
+ { "help", 0, 0, 'h' },
+ { "device", 1, 0, 'i' },
+ { "config", 1, 0, 'f' },
+ { "raw", 0, 0, 'r' },
+ { "auth", 0, 0, 'A' },
+ { "encrypt", 0, 0, 'E' },
+ { "secure", 0, 0, 'S' },
+ { "master", 0, 0, 'M' },
+ { "linger", 1, 0, 'L' },
+ { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ bdaddr_t bdaddr;
+ int i, opt, ctl, dev_id, show_all = 0;
+
+ bacpy(&bdaddr, BDADDR_ANY);
+
+ while ((opt = getopt_long(argc, argv, "+i:f:rahAESML:", main_options, NULL)) != -1) {
+ switch(opt) {
+ case 'i':
+ if (strncmp(optarg, "hci", 3) == 0)
+ hci_devba(atoi(optarg + 3), &bdaddr);
+ else
+ str2ba(optarg, &bdaddr);
+ break;
+
+ case 'f':
+ rfcomm_config_file = strdup(optarg);
+ break;
+
+ case 'r':
+ rfcomm_raw_tty = 1;
+ break;
+
+ case 'a':
+ show_all = 1;
+ break;
+
+ case 'h':
+ usage();
+ exit(0);
+
+ case 'A':
+ auth = 1;
+ break;
+
+ case 'E':
+ encryption = 1;
+ break;
+
+ case 'S':
+ secure = 1;
+ break;
+
+ case 'M':
+ master = 1;
+ break;
+
+ case 'L':
+ linger = atoi(optarg);
+ break;
+
+ default:
+ exit(0);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ if (argc < 2) {
+ if (argc != 0) {
+ usage();
+ exit(1);
+ } else
+ show_all = 1;
+ }
+
+ ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_RFCOMM);
+ if (ctl < 0) {
+ perror("Can't open RFCOMM control socket");
+ exit(1);
+ }
+
+ if (show_all) {
+ print_dev_list(ctl, 0);
+ close(ctl);
+ exit(0);
+ }
+
+ if (strncmp(argv[1], "/dev/rfcomm", 11) == 0)
+ dev_id = atoi(argv[1] + 11);
+ else if (strncmp(argv[1], "rfcomm", 6) == 0)
+ dev_id = atoi(argv[1] + 6);
+ else
+ dev_id = atoi(argv[1]);
+
+ for (i = 0; command[i].cmd; i++) {
+ if (strncmp(command[i].cmd, argv[0], 4) && strncmp(command[i].alt, argv[0], 4))
+ continue;
+ argc--;
+ argv++;
+ command[i].func(ctl, dev_id, &bdaddr, argc, argv);
+ close(ctl);
+ exit(0);
+ }
+
+ usage();
+
+ close(ctl);
+
+ return 0;
+}
diff --git a/tools/rfcomm.conf b/tools/rfcomm.conf
new file mode 100644
index 0000000..6179ef7
--- /dev/null
+++ b/tools/rfcomm.conf
@@ -0,0 +1,17 @@
+#
+# RFCOMM configuration file.
+#
+
+#rfcomm0 {
+# # Automatically bind the device at startup
+# bind no;
+#
+# # Bluetooth address of the device
+# device 11:22:33:44:55:66;
+#
+# # RFCOMM channel for the connection
+# channel 1;
+#
+# # Description of the connection
+# comment "Example Bluetooth device";
+#}
diff --git a/tools/sdptool.1 b/tools/sdptool.1
new file mode 100644
index 0000000..0f100e2
--- /dev/null
+++ b/tools/sdptool.1
@@ -0,0 +1,130 @@
+.\" $Header$
+.\"
+.\" transcript compatibility for postscript use.
+.\"
+.\" synopsis: .P! <file.ps>
+.\"
+.de P!
+.fl
+\!!1 setgray
+.fl
+\\&.\"
+.fl
+\!!0 setgray
+.fl \" force out current output buffer
+\!!save /psv exch def currentpoint translate 0 0 moveto
+\!!/showpage{}def
+.fl \" prolog
+.sy sed -e 's/^/!/' \\$1\" bring in postscript file
+\!!psv restore
+.
+.de pF
+.ie \\*(f1 .ds f1 \\n(.f
+.el .ie \\*(f2 .ds f2 \\n(.f
+.el .ie \\*(f3 .ds f3 \\n(.f
+.el .ie \\*(f4 .ds f4 \\n(.f
+.el .tm ? font overflow
+.ft \\$1
+..
+.de fP
+.ie !\\*(f4 \{\
+. ft \\*(f4
+. ds f4\"
+' br \}
+.el .ie !\\*(f3 \{\
+. ft \\*(f3
+. ds f3\"
+' br \}
+.el .ie !\\*(f2 \{\
+. ft \\*(f2
+. ds f2\"
+' br \}
+.el .ie !\\*(f1 \{\
+. ft \\*(f1
+. ds f1\"
+' br \}
+.el .tm ? font underflow
+..
+.ds f1\"
+.ds f2\"
+.ds f3\"
+.ds f4\"
+'\" t
+.ta 8n 16n 24n 32n 40n 48n 56n 64n 72n
+.TH "sdptool" "1"
+.SH "NAME"
+sdptool \(em control and interrogate SDP servers
+.SH "SYNOPSIS"
+.PP
+\fBsdptool\fR [\fIoptions\fR] {\fIcommand\fR} [\fIcommand parameters\fR \&...]
+.SH "DESCRIPTION"
+.PP
+\fBsdptool\fR provides the interface for
+performing SDP queries on Bluetooth devices, and administering a
+local \fBsdpd\fR.
+.SH "COMMANDS"
+.PP
+The following commands are available. In all cases \fBbdaddr\fR
+specifies the device to search or browse. If \fIlocal\fP is used
+for \fBbdaddr\fP, then the local \fBsdpd\fR is searched.
+.PP
+Services are identified and manipulated with a 4-byte \fBrecord_handle\fP
+(NOT the service name). To find a service's \fBrecord_handle\fP, look for the
+"Service RecHandle" line in the \fBsearch\fP or \fBbrowse\fP results
+.IP "\fBsearch [--bdaddr bdaddr] [--tree] [--raw] [--xml] service_name\fP" 10
+Search for services..
+.IP "" 10
+Known service names are DID, SP, DUN, LAN, FAX, OPUSH,
+FTP, HS, HF, HFAG, SAP, NAP, GN, PANU, HCRP, HID, CIP,
+A2SRC, A2SNK, AVRCT, AVRTG, UDIUE, UDITE and SYNCML.
+.IP "\fBbrowse [--tree] [--raw] [--xml] [bdaddr]\fP" 10
+Browse all available services on the device
+specified by a Bluetooth address as a parameter.
+.IP "\fBrecords [--tree] [--raw] [--xml] bdaddr\fP" 10
+Retrieve all possible service records.
+.IP "\fBadd [ --handle=N --channel=N ]\fP" 10
+Add a service to the local
+\fBsdpd\fR.
+.IP "" 10
+You can specify a handle for this record using
+the \fB--handle\fP option.
+.IP "" 10
+You can specify a channel to add the service on
+using the \fB--channel\fP option.
+.IP "\fBdel record_handle\fP" 10
+Remove a service from the local
+\fBsdpd\fR.
+.IP "\fBget [--tree] [--raw] [--xml] [--bdaddr bdaddr] record_handle\fP" 10
+Retrieve a service from the local
+\fBsdpd\fR.
+.IP "\fBsetattr record_handle attrib_id attrib_value\fP" 10
+Set or add an attribute to an SDP record.
+
+.IP "\fBsetseq record_handle attrib_id attrib_values\fP" 10
+Set or add an attribute sequence to an
+SDP record.
+.SH "OPTIONS"
+.IP "\fB--help\fP" 10
+Displays help on using sdptool.
+
+.SH "EXAMPLES"
+.PP
+sdptool browse 00:80:98:24:15:6D
+.PP
+sdptool browse local
+.PP
+sdptool add DUN
+.PP
+sdptool del 0x10000
+.SH "BUGS"
+.PP
+Documentation needs improving.
+.SH "AUTHOR"
+.PP
+Maxim Krasnyansky <maxk@qualcomm.com>. Man page written
+by Edd Dumbill <ejad@debian.org>.
+
+.SH "SEE ALSO"
+.PP
+sdpd(8)
+.\" created by instant / docbook-to-man, Thu 15 Jan 2004, 21:01
diff --git a/tools/sdptool.c b/tools/sdptool.c
new file mode 100644
index 0000000..140a46a
--- /dev/null
+++ b/tools/sdptool.c
@@ -0,0 +1,4274 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2001-2002 Nokia Corporation
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2002-2003 Stephen Crane <steve.crane@rococosoft.com>
+ * Copyright (C) 2002-2003 Jean Tourrilhes <jt@hpl.hp.com>
+ *
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+#include <getopt.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>
+
+#include <netinet/in.h>
+
+#include "sdp-xml.h"
+
+#ifndef APPLE_AGENT_SVCLASS_ID
+#define APPLE_AGENT_SVCLASS_ID 0x2112
+#endif
+
+#define for_each_opt(opt, long, short) while ((opt=getopt_long(argc, argv, short ? short:"+", long, 0)) != -1)
+
+/*
+ * Convert a string to a BDADDR, with a few "enhancements" - Jean II
+ */
+static int estr2ba(char *str, bdaddr_t *ba)
+{
+ /* Only trap "local", "any" is already dealt with */
+ if(!strcmp(str, "local")) {
+ bacpy(ba, BDADDR_LOCAL);
+ return 0;
+ }
+ return str2ba(str, ba);
+}
+
+#define DEFAULT_VIEW 0 /* Display only known attribute */
+#define TREE_VIEW 1 /* Display full attribute tree */
+#define RAW_VIEW 2 /* Display raw tree */
+#define XML_VIEW 3 /* Display xml tree */
+
+/* Pass args to the inquiry/search handler */
+struct search_context {
+ char *svc; /* Service */
+ uuid_t group; /* Browse group */
+ int view; /* View mode */
+ uint32_t handle; /* Service record handle */
+};
+
+typedef int (*handler_t)(bdaddr_t *bdaddr, struct search_context *arg);
+
+static char UUID_str[MAX_LEN_UUID_STR];
+static bdaddr_t interface;
+
+/* Definition of attribute members */
+struct member_def {
+ char *name;
+};
+
+/* Definition of an attribute */
+struct attrib_def {
+ int num; /* Numeric ID - 16 bits */
+ char *name; /* User readable name */
+ struct member_def *members; /* Definition of attribute args */
+ int member_max; /* Max of attribute arg definitions */
+};
+
+/* Definition of a service or protocol */
+struct uuid_def {
+ int num; /* Numeric ID - 16 bits */
+ char *name; /* User readable name */
+ struct attrib_def *attribs; /* Specific attribute definitions */
+ int attrib_max; /* Max of attribute definitions */
+};
+
+/* Context information about current attribute */
+struct attrib_context {
+ struct uuid_def *service; /* Service UUID, if known */
+ struct attrib_def *attrib; /* Description of the attribute */
+ int member_index; /* Index of current attribute member */
+};
+
+/* Context information about the whole service */
+struct service_context {
+ struct uuid_def *service; /* Service UUID, if known */
+};
+
+/* Allow us to do nice formatting of the lists */
+static char *indent_spaces = " ";
+
+/* ID of the service attribute.
+ * Most attributes after 0x200 are defined based on the service, so
+ * we need to find what is the service (which is messy) - Jean II */
+#define SERVICE_ATTR 0x1
+
+/* Definition of the optional arguments in protocol list */
+static struct member_def protocol_members[] = {
+ { "Protocol" },
+ { "Channel/Port" },
+ { "Version" },
+};
+
+/* Definition of the optional arguments in profile list */
+static struct member_def profile_members[] = {
+ { "Profile" },
+ { "Version" },
+};
+
+/* Definition of the optional arguments in Language list */
+static struct member_def language_members[] = {
+ { "Code ISO639" },
+ { "Encoding" },
+ { "Base Offset" },
+};
+
+/* Name of the various common attributes. See BT assigned numbers */
+static struct attrib_def attrib_names[] = {
+ { 0x0, "ServiceRecordHandle", NULL, 0 },
+ { 0x1, "ServiceClassIDList", NULL, 0 },
+ { 0x2, "ServiceRecordState", NULL, 0 },
+ { 0x3, "ServiceID", NULL, 0 },
+ { 0x4, "ProtocolDescriptorList",
+ protocol_members, sizeof(protocol_members)/sizeof(struct member_def) },
+ { 0x5, "BrowseGroupList", NULL, 0 },
+ { 0x6, "LanguageBaseAttributeIDList",
+ language_members, sizeof(language_members)/sizeof(struct member_def) },
+ { 0x7, "ServiceInfoTimeToLive", NULL, 0 },
+ { 0x8, "ServiceAvailability", NULL, 0 },
+ { 0x9, "BluetoothProfileDescriptorList",
+ profile_members, sizeof(profile_members)/sizeof(struct member_def) },
+ { 0xA, "DocumentationURL", NULL, 0 },
+ { 0xB, "ClientExecutableURL", NULL, 0 },
+ { 0xC, "IconURL", NULL, 0 },
+ { 0xD, "AdditionalProtocolDescriptorLists", NULL, 0 },
+ /* Definitions after that are tricky (per profile or offset) */
+};
+
+const int attrib_max = sizeof(attrib_names)/sizeof(struct attrib_def);
+
+/* Name of the various SPD attributes. See BT assigned numbers */
+static struct attrib_def sdp_attrib_names[] = {
+ { 0x200, "VersionNumberList", NULL, 0 },
+ { 0x201, "ServiceDatabaseState", NULL, 0 },
+};
+
+/* Name of the various SPD attributes. See BT assigned numbers */
+static struct attrib_def browse_attrib_names[] = {
+ { 0x200, "GroupID", NULL, 0 },
+};
+
+/* Name of the various Device ID attributes. See Device Id spec. */
+static struct attrib_def did_attrib_names[] = {
+ { 0x200, "SpecificationID", NULL, 0 },
+ { 0x201, "VendorID", NULL, 0 },
+ { 0x202, "ProductID", NULL, 0 },
+ { 0x203, "Version", NULL, 0 },
+ { 0x204, "PrimaryRecord", NULL, 0 },
+ { 0x205, "VendorIDSource", NULL, 0 },
+};
+
+/* Name of the various HID attributes. See HID spec. */
+static struct attrib_def hid_attrib_names[] = {
+ { 0x200, "DeviceReleaseNum", NULL, 0 },
+ { 0x201, "ParserVersion", NULL, 0 },
+ { 0x202, "DeviceSubclass", NULL, 0 },
+ { 0x203, "CountryCode", NULL, 0 },
+ { 0x204, "VirtualCable", NULL, 0 },
+ { 0x205, "ReconnectInitiate", NULL, 0 },
+ { 0x206, "DescriptorList", NULL, 0 },
+ { 0x207, "LangIDBaseList", NULL, 0 },
+ { 0x208, "SDPDisable", NULL, 0 },
+ { 0x209, "BatteryPower", NULL, 0 },
+ { 0x20a, "RemoteWakeup", NULL, 0 },
+ { 0x20b, "ProfileVersion", NULL, 0 },
+ { 0x20c, "SupervisionTimeout", NULL, 0 },
+ { 0x20d, "NormallyConnectable", NULL, 0 },
+ { 0x20e, "BootDevice", NULL, 0 },
+};
+
+/* Name of the various PAN attributes. See BT assigned numbers */
+/* Note : those need to be double checked - Jean II */
+static struct attrib_def pan_attrib_names[] = {
+ { 0x200, "IpSubnet", NULL, 0 }, /* Obsolete ??? */
+ { 0x30A, "SecurityDescription", NULL, 0 },
+ { 0x30B, "NetAccessType", NULL, 0 },
+ { 0x30C, "MaxNetAccessrate", NULL, 0 },
+ { 0x30D, "IPv4Subnet", NULL, 0 },
+ { 0x30E, "IPv6Subnet", NULL, 0 },
+};
+
+/* Name of the various Generic-Audio attributes. See BT assigned numbers */
+/* Note : totally untested - Jean II */
+static struct attrib_def audio_attrib_names[] = {
+ { 0x302, "Remote audio volume control", NULL, 0 },
+};
+
+/* Same for the UUIDs. See BT assigned numbers */
+static struct uuid_def uuid16_names[] = {
+ /* -- Protocols -- */
+ { 0x0001, "SDP", NULL, 0 },
+ { 0x0002, "UDP", NULL, 0 },
+ { 0x0003, "RFCOMM", NULL, 0 },
+ { 0x0004, "TCP", NULL, 0 },
+ { 0x0005, "TCS-BIN", NULL, 0 },
+ { 0x0006, "TCS-AT", NULL, 0 },
+ { 0x0008, "OBEX", NULL, 0 },
+ { 0x0009, "IP", NULL, 0 },
+ { 0x000a, "FTP", NULL, 0 },
+ { 0x000c, "HTTP", NULL, 0 },
+ { 0x000e, "WSP", NULL, 0 },
+ { 0x000f, "BNEP", NULL, 0 },
+ { 0x0010, "UPnP/ESDP", NULL, 0 },
+ { 0x0011, "HIDP", NULL, 0 },
+ { 0x0012, "HardcopyControlChannel", NULL, 0 },
+ { 0x0014, "HardcopyDataChannel", NULL, 0 },
+ { 0x0016, "HardcopyNotification", NULL, 0 },
+ { 0x0017, "AVCTP", NULL, 0 },
+ { 0x0019, "AVDTP", NULL, 0 },
+ { 0x001b, "CMTP", NULL, 0 },
+ { 0x001d, "UDI_C-Plane", NULL, 0 },
+ { 0x0100, "L2CAP", NULL, 0 },
+ /* -- Services -- */
+ { 0x1000, "ServiceDiscoveryServerServiceClassID",
+ sdp_attrib_names, sizeof(sdp_attrib_names)/sizeof(struct attrib_def) },
+ { 0x1001, "BrowseGroupDescriptorServiceClassID",
+ browse_attrib_names, sizeof(browse_attrib_names)/sizeof(struct attrib_def) },
+ { 0x1002, "PublicBrowseGroup", NULL, 0 },
+ { 0x1101, "SerialPort", NULL, 0 },
+ { 0x1102, "LANAccessUsingPPP", NULL, 0 },
+ { 0x1103, "DialupNetworking (DUN)", NULL, 0 },
+ { 0x1104, "IrMCSync", NULL, 0 },
+ { 0x1105, "OBEXObjectPush", NULL, 0 },
+ { 0x1106, "OBEXFileTransfer", NULL, 0 },
+ { 0x1107, "IrMCSyncCommand", NULL, 0 },
+ { 0x1108, "Headset",
+ audio_attrib_names, sizeof(audio_attrib_names)/sizeof(struct attrib_def) },
+ { 0x1109, "CordlessTelephony", NULL, 0 },
+ { 0x110a, "AudioSource", NULL, 0 },
+ { 0x110b, "AudioSink", NULL, 0 },
+ { 0x110c, "RemoteControlTarget", NULL, 0 },
+ { 0x110d, "AdvancedAudio", NULL, 0 },
+ { 0x110e, "RemoteControl", NULL, 0 },
+ { 0x110f, "VideoConferencing", NULL, 0 },
+ { 0x1110, "Intercom", NULL, 0 },
+ { 0x1111, "Fax", NULL, 0 },
+ { 0x1112, "HeadsetAudioGateway", NULL, 0 },
+ { 0x1113, "WAP", NULL, 0 },
+ { 0x1114, "WAP Client", NULL, 0 },
+ { 0x1115, "PANU (PAN/BNEP)",
+ pan_attrib_names, sizeof(pan_attrib_names)/sizeof(struct attrib_def) },
+ { 0x1116, "NAP (PAN/BNEP)",
+ pan_attrib_names, sizeof(pan_attrib_names)/sizeof(struct attrib_def) },
+ { 0x1117, "GN (PAN/BNEP)",
+ pan_attrib_names, sizeof(pan_attrib_names)/sizeof(struct attrib_def) },
+ { 0x1118, "DirectPrinting (BPP)", NULL, 0 },
+ { 0x1119, "ReferencePrinting (BPP)", NULL, 0 },
+ { 0x111a, "Imaging (BIP)", NULL, 0 },
+ { 0x111b, "ImagingResponder (BIP)", NULL, 0 },
+ { 0x111c, "ImagingAutomaticArchive (BIP)", NULL, 0 },
+ { 0x111d, "ImagingReferencedObjects (BIP)", NULL, 0 },
+ { 0x111e, "Handsfree", NULL, 0 },
+ { 0x111f, "HandsfreeAudioGateway", NULL, 0 },
+ { 0x1120, "DirectPrintingReferenceObjectsService (BPP)", NULL, 0 },
+ { 0x1121, "ReflectedUI (BPP)", NULL, 0 },
+ { 0x1122, "BasicPrinting (BPP)", NULL, 0 },
+ { 0x1123, "PrintingStatus (BPP)", NULL, 0 },
+ { 0x1124, "HumanInterfaceDeviceService (HID)",
+ hid_attrib_names, sizeof(hid_attrib_names)/sizeof(struct attrib_def) },
+ { 0x1125, "HardcopyCableReplacement (HCR)", NULL, 0 },
+ { 0x1126, "HCR_Print (HCR)", NULL, 0 },
+ { 0x1127, "HCR_Scan (HCR)", NULL, 0 },
+ { 0x1128, "Common ISDN Access (CIP)", NULL, 0 },
+ { 0x1129, "VideoConferencingGW (VCP)", NULL, 0 },
+ { 0x112a, "UDI-MT", NULL, 0 },
+ { 0x112b, "UDI-TA", NULL, 0 },
+ { 0x112c, "Audio/Video", NULL, 0 },
+ { 0x112d, "SIM Access (SAP)", NULL, 0 },
+ { 0x112e, "Phonebook Access (PBAP) - PCE", NULL, 0 },
+ { 0x112f, "Phonebook Access (PBAP) - PSE", NULL, 0 },
+ { 0x1130, "Phonebook Access (PBAP)", NULL, 0 },
+ /* ... */
+ { 0x1200, "PnPInformation",
+ did_attrib_names, sizeof(did_attrib_names)/sizeof(struct attrib_def) },
+ { 0x1201, "GenericNetworking", NULL, 0 },
+ { 0x1202, "GenericFileTransfer", NULL, 0 },
+ { 0x1203, "GenericAudio",
+ audio_attrib_names, sizeof(audio_attrib_names)/sizeof(struct attrib_def) },
+ { 0x1204, "GenericTelephony", NULL, 0 },
+ /* ... */
+ { 0x1303, "VideoSource", NULL, 0 },
+ { 0x1304, "VideoSink", NULL, 0 },
+ { 0x1305, "VideoDistribution", NULL, 0 },
+ { 0x1400, "HDP", NULL, 0 },
+ { 0x1401, "HDPSource", NULL, 0 },
+ { 0x1402, "HDPSink", NULL, 0 },
+ { 0x2112, "AppleAgent", NULL, 0 },
+};
+
+static const int uuid16_max = sizeof(uuid16_names)/sizeof(struct uuid_def);
+
+static void sdp_data_printf(sdp_data_t *, struct attrib_context *, int);
+
+/*
+ * Parse a UUID.
+ * The BT assigned numbers only list UUID16, so I'm not sure the
+ * other types will ever get used...
+ */
+static void sdp_uuid_printf(uuid_t *uuid, struct attrib_context *context, int indent)
+{
+ if (uuid) {
+ if (uuid->type == SDP_UUID16) {
+ uint16_t uuidNum = uuid->value.uuid16;
+ struct uuid_def *uuidDef = NULL;
+ int i;
+
+ for (i = 0; i < uuid16_max; i++)
+ if (uuid16_names[i].num == uuidNum) {
+ uuidDef = &uuid16_names[i];
+ break;
+ }
+
+ /* Check if it's the service attribute */
+ if (context->attrib && context->attrib->num == SERVICE_ATTR) {
+ /* We got the service ID !!! */
+ context->service = uuidDef;
+ }
+
+ if (uuidDef)
+ printf("%.*sUUID16 : 0x%.4x - %s\n",
+ indent, indent_spaces, uuidNum, uuidDef->name);
+ else
+ printf("%.*sUUID16 : 0x%.4x\n",
+ indent, indent_spaces, uuidNum);
+ } else if (uuid->type == SDP_UUID32) {
+ struct uuid_def *uuidDef = NULL;
+ int i;
+
+ if (!(uuid->value.uuid32 & 0xffff0000)) {
+ uint16_t uuidNum = uuid->value.uuid32;
+ for (i = 0; i < uuid16_max; i++)
+ if (uuid16_names[i].num == uuidNum) {
+ uuidDef = &uuid16_names[i];
+ break;
+ }
+ }
+
+ if (uuidDef)
+ printf("%.*sUUID32 : 0x%.8x - %s\n",
+ indent, indent_spaces, uuid->value.uuid32, uuidDef->name);
+ else
+ printf("%.*sUUID32 : 0x%.8x\n",
+ indent, indent_spaces, uuid->value.uuid32);
+ } else if (uuid->type == SDP_UUID128) {
+ unsigned int data0;
+ unsigned short data1;
+ unsigned short data2;
+ unsigned short data3;
+ unsigned int data4;
+ unsigned short data5;
+
+ memcpy(&data0, &uuid->value.uuid128.data[0], 4);
+ memcpy(&data1, &uuid->value.uuid128.data[4], 2);
+ memcpy(&data2, &uuid->value.uuid128.data[6], 2);
+ memcpy(&data3, &uuid->value.uuid128.data[8], 2);
+ memcpy(&data4, &uuid->value.uuid128.data[10], 4);
+ memcpy(&data5, &uuid->value.uuid128.data[14], 2);
+
+ printf("%.*sUUID128 : 0x%.8x-%.4x-%.4x-%.4x-%.8x-%.4x\n",
+ indent, indent_spaces,
+ ntohl(data0), ntohs(data1), ntohs(data2),
+ ntohs(data3), ntohl(data4), ntohs(data5));
+ } else
+ printf("%.*sEnum type of UUID not set\n",
+ indent, indent_spaces);
+ } else
+ printf("%.*sNull passed to print UUID\n",
+ indent, indent_spaces);
+}
+
+/*
+ * Parse a sequence of data elements (i.e. a list)
+ */
+static void printf_dataseq(sdp_data_t * pData, struct attrib_context *context, int indent)
+{
+ sdp_data_t *sdpdata = NULL;
+
+ sdpdata = pData;
+ if (sdpdata) {
+ context->member_index = 0;
+ do {
+ sdp_data_printf(sdpdata, context, indent + 2);
+ sdpdata = sdpdata->next;
+ context->member_index++;
+ } while (sdpdata);
+ } else {
+ printf("%.*sBroken dataseq link\n", indent, indent_spaces);
+ }
+}
+
+/*
+ * Parse a single data element (either in the attribute or in a data
+ * sequence).
+ */
+static void sdp_data_printf(sdp_data_t *sdpdata, struct attrib_context *context, int indent)
+{
+ char *member_name = NULL;
+
+ /* Find member name. Almost black magic ;-) */
+ if (context && context->attrib && context->attrib->members &&
+ context->member_index < context->attrib->member_max) {
+ member_name = context->attrib->members[context->member_index].name;
+ }
+
+ switch (sdpdata->dtd) {
+ case SDP_DATA_NIL:
+ printf("%.*sNil\n", indent, indent_spaces);
+ break;
+ case SDP_BOOL:
+ case SDP_UINT8:
+ case SDP_UINT16:
+ case SDP_UINT32:
+ case SDP_UINT64:
+ case SDP_UINT128:
+ case SDP_INT8:
+ case SDP_INT16:
+ case SDP_INT32:
+ case SDP_INT64:
+ case SDP_INT128:
+ if (member_name) {
+ printf("%.*s%s (Integer) : 0x%x\n",
+ indent, indent_spaces, member_name, sdpdata->val.uint32);
+ } else {
+ printf("%.*sInteger : 0x%x\n", indent, indent_spaces,
+ sdpdata->val.uint32);
+ }
+ break;
+
+ case SDP_UUID16:
+ case SDP_UUID32:
+ case SDP_UUID128:
+ //printf("%.*sUUID\n", indent, indent_spaces);
+ sdp_uuid_printf(&sdpdata->val.uuid, context, indent);
+ break;
+
+ case SDP_TEXT_STR8:
+ case SDP_TEXT_STR16:
+ case SDP_TEXT_STR32:
+ if (sdpdata->unitSize > (int) strlen(sdpdata->val.str)) {
+ int i;
+ printf("%.*sData :", indent, indent_spaces);
+ for (i = 0; i < sdpdata->unitSize; i++)
+ printf(" %02x", (unsigned char) sdpdata->val.str[i]);
+ printf("\n");
+ } else
+ printf("%.*sText : \"%s\"\n", indent, indent_spaces, sdpdata->val.str);
+ break;
+ case SDP_URL_STR8:
+ case SDP_URL_STR16:
+ case SDP_URL_STR32:
+ printf("%.*sURL : %s\n", indent, indent_spaces, sdpdata->val.str);
+ break;
+
+ case SDP_SEQ8:
+ case SDP_SEQ16:
+ case SDP_SEQ32:
+ printf("%.*sData Sequence\n", indent, indent_spaces);
+ printf_dataseq(sdpdata->val.dataseq, context, indent);
+ break;
+
+ case SDP_ALT8:
+ case SDP_ALT16:
+ case SDP_ALT32:
+ printf("%.*sData Sequence Alternates\n", indent, indent_spaces);
+ printf_dataseq(sdpdata->val.dataseq, context, indent);
+ break;
+ }
+}
+
+/*
+ * Parse a single attribute.
+ */
+static void print_tree_attr_func(void *value, void *userData)
+{
+ sdp_data_t *sdpdata = value;
+ uint16_t attrId;
+ struct service_context *service = (struct service_context *) userData;
+ struct attrib_context context;
+ struct attrib_def *attrDef = NULL;
+ int i;
+
+ if (!sdpdata)
+ return;
+
+ attrId = sdpdata->attrId;
+ /* Search amongst the generic attributes */
+ for (i = 0; i < attrib_max; i++)
+ if (attrib_names[i].num == attrId) {
+ attrDef = &attrib_names[i];
+ break;
+ }
+ /* Search amongst the specific attributes of this service */
+ if ((attrDef == NULL) && (service->service != NULL) &&
+ (service->service->attribs != NULL)) {
+ struct attrib_def *svc_attribs = service->service->attribs;
+ int svc_attrib_max = service->service->attrib_max;
+ for (i = 0; i < svc_attrib_max; i++)
+ if (svc_attribs[i].num == attrId) {
+ attrDef = &svc_attribs[i];
+ break;
+ }
+ }
+
+ if (attrDef)
+ printf("Attribute Identifier : 0x%x - %s\n", attrId, attrDef->name);
+ else
+ printf("Attribute Identifier : 0x%x\n", attrId);
+ /* Build context */
+ context.service = service->service;
+ context.attrib = attrDef;
+ context.member_index = 0;
+ /* Parse attribute members */
+ sdp_data_printf(sdpdata, &context, 2);
+ /* Update service */
+ service->service = context.service;
+}
+
+/*
+ * Main entry point of this library. Parse a SDP record.
+ * We assume the record has already been read, parsed and cached
+ * locally. Jean II
+ */
+static void print_tree_attr(sdp_record_t *rec)
+{
+ if (rec && rec->attrlist) {
+ struct service_context service = { NULL };
+ sdp_list_foreach(rec->attrlist, print_tree_attr_func, &service);
+ }
+}
+
+static void print_raw_data(sdp_data_t *data, int indent)
+{
+ struct uuid_def *def;
+ int i, hex;
+
+ if (!data)
+ return;
+
+ for (i = 0; i < indent; i++)
+ printf("\t");
+
+ switch (data->dtd) {
+ case SDP_DATA_NIL:
+ printf("NIL\n");
+ break;
+ case SDP_BOOL:
+ printf("Bool %s\n", data->val.uint8 ? "True" : "False");
+ break;
+ case SDP_UINT8:
+ printf("UINT8 0x%02x\n", data->val.uint8);
+ break;
+ case SDP_UINT16:
+ printf("UINT16 0x%04x\n", data->val.uint16);
+ break;
+ case SDP_UINT32:
+ printf("UINT32 0x%08x\n", data->val.uint32);
+ break;
+ case SDP_UINT64:
+ printf("UINT64 0x%016jx\n", data->val.uint64);
+ break;
+ case SDP_UINT128:
+ printf("UINT128 ...\n");
+ break;
+ case SDP_INT8:
+ printf("INT8 %d\n", data->val.int8);
+ break;
+ case SDP_INT16:
+ printf("INT16 %d\n", data->val.int16);
+ break;
+ case SDP_INT32:
+ printf("INT32 %d\n", data->val.int32);
+ break;
+ case SDP_INT64:
+ printf("INT64 %jd\n", data->val.int64);
+ break;
+ case SDP_INT128:
+ printf("INT128 ...\n");
+ break;
+ case SDP_UUID16:
+ case SDP_UUID32:
+ case SDP_UUID128:
+ switch (data->val.uuid.type) {
+ case SDP_UUID16:
+ def = NULL;
+ for (i = 0; i < uuid16_max; i++)
+ if (uuid16_names[i].num == data->val.uuid.value.uuid16) {
+ def = &uuid16_names[i];
+ break;
+ }
+ if (def)
+ printf("UUID16 0x%04x - %s\n", data->val.uuid.value.uuid16, def->name);
+ else
+ printf("UUID16 0x%04x\n", data->val.uuid.value.uuid16);
+ break;
+ case SDP_UUID32:
+ def = NULL;
+ if (!(data->val.uuid.value.uuid32 & 0xffff0000)) {
+ uint16_t value = data->val.uuid.value.uuid32;
+ for (i = 0; i < uuid16_max; i++)
+ if (uuid16_names[i].num == value) {
+ def = &uuid16_names[i];
+ break;
+ }
+ }
+ if (def)
+ printf("UUID32 0x%08x - %s\n", data->val.uuid.value.uuid32, def->name);
+ else
+ printf("UUID32 0x%08x\n", data->val.uuid.value.uuid32);
+ break;
+ case SDP_UUID128:
+ printf("UUID128 ");
+ for (i = 0; i < 16; i++) {
+ switch (i) {
+ case 4:
+ case 6:
+ case 8:
+ case 10:
+ printf("-");
+ break;
+ }
+ printf("%02x", (unsigned char ) data->val.uuid.value.uuid128.data[i]);
+ }
+ printf("\n");
+ break;
+ default:
+ printf("UUID type 0x%02x\n", data->val.uuid.type);
+ break;
+ }
+ break;
+ case SDP_TEXT_STR8:
+ case SDP_TEXT_STR16:
+ case SDP_TEXT_STR32:
+ hex = 0;
+ for (i = 0; i < data->unitSize; i++) {
+ if (i == (data->unitSize - 1) && data->val.str[i] == '\0')
+ break;
+ if (!isprint(data->val.str[i])) {
+ hex = 1;
+ break;
+ }
+ }
+ if (hex) {
+ printf("Data");
+ for (i = 0; i < data->unitSize; i++)
+ printf(" %02x", (unsigned char) data->val.str[i]);
+ } else {
+ printf("String ");
+ for (i = 0; i < data->unitSize; i++)
+ printf("%c", data->val.str[i]);
+ }
+ printf("\n");
+ break;
+ case SDP_URL_STR8:
+ case SDP_URL_STR16:
+ case SDP_URL_STR32:
+ printf("URL %s\n", data->val.str);
+ break;
+ case SDP_SEQ8:
+ case SDP_SEQ16:
+ case SDP_SEQ32:
+ printf("Sequence\n");
+ print_raw_data(data->val.dataseq, indent + 1);
+ break;
+ case SDP_ALT8:
+ case SDP_ALT16:
+ case SDP_ALT32:
+ printf("Alternate\n");
+ print_raw_data(data->val.dataseq, indent + 1);
+ break;
+ default:
+ printf("Unknown type 0x%02x\n", data->dtd);
+ break;
+ }
+
+ print_raw_data(data->next, indent);
+}
+
+static void print_raw_attr_func(void *value, void *userData)
+{
+ sdp_data_t *data = (sdp_data_t *) value;
+ struct attrib_def *def = NULL;
+ int i;
+
+ if (!data)
+ return;
+
+ /* Search amongst the generic attributes */
+ for (i = 0; i < attrib_max; i++)
+ if (attrib_names[i].num == data->attrId) {
+ def = &attrib_names[i];
+ break;
+ }
+
+ if (def)
+ printf("\tAttribute 0x%04x - %s\n", data->attrId, def->name);
+ else
+ printf("\tAttribute 0x%04x\n", data->attrId);
+
+ print_raw_data(data, 2);
+}
+
+static void print_raw_attr(sdp_record_t *rec)
+{
+ if (rec && rec->attrlist) {
+ printf("Sequence\n");
+ sdp_list_foreach(rec->attrlist, print_raw_attr_func, 0);
+ }
+}
+
+/*
+ * Set attributes with single values in SDP record
+ * Jean II
+ */
+static int set_attrib(sdp_session_t *sess, uint32_t handle, uint16_t attrib, char *value)
+{
+ sdp_list_t *attrid_list;
+ uint32_t range = 0x0000ffff;
+ sdp_record_t *rec;
+ int ret;
+
+ /* Get the old SDP record */
+ attrid_list = sdp_list_append(NULL, &range);
+ rec = sdp_service_attr_req(sess, handle, SDP_ATTR_REQ_RANGE, attrid_list);
+ sdp_list_free(attrid_list, NULL);
+
+ if (!rec) {
+ printf("Service get request failed.\n");
+ return -1;
+ }
+
+ /* Check the type of attribute */
+ if (!strncasecmp(value, "u0x", 3)) {
+ /* UUID16 */
+ uint16_t value_int = 0;
+ uuid_t value_uuid;
+ value_int = strtoul(value + 3, NULL, 16);
+ sdp_uuid16_create(&value_uuid, value_int);
+ printf("Adding attrib 0x%X uuid16 0x%X to record 0x%X\n",
+ attrib, value_int, handle);
+
+ sdp_attr_add_new(rec, attrib, SDP_UUID16, &value_uuid.value.uuid16);
+ } else if (!strncasecmp(value, "0x", 2)) {
+ /* Int */
+ uint32_t value_int;
+ value_int = strtoul(value + 2, NULL, 16);
+ printf("Adding attrib 0x%X int 0x%X to record 0x%X\n",
+ attrib, value_int, handle);
+
+ sdp_attr_add_new(rec, attrib, SDP_UINT32, &value_int);
+ } else {
+ /* String */
+ printf("Adding attrib 0x%X string \"%s\" to record 0x%X\n",
+ attrib, value, handle);
+
+ /* Add/Update our attribute to the record */
+ sdp_attr_add_new(rec, attrib, SDP_TEXT_STR8, value);
+ }
+
+ /* Update on the server */
+ ret = sdp_device_record_update(sess, &interface, rec);
+ if (ret < 0)
+ printf("Service Record update failed (%d).\n", errno);
+ sdp_record_free(rec);
+ return ret;
+}
+
+static struct option set_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *set_help =
+ "Usage:\n"
+ "\tget record_handle attrib_id attrib_value\n";
+
+/*
+ * Add an attribute to an existing SDP record on the local SDP server
+ */
+static int cmd_setattr(int argc, char **argv)
+{
+ int opt, status;
+ uint32_t handle;
+ uint16_t attrib;
+ sdp_session_t *sess;
+
+ for_each_opt(opt, set_options, NULL) {
+ switch(opt) {
+ default:
+ printf("%s", set_help);
+ return -1;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 3) {
+ printf("%s", set_help);
+ return -1;
+ }
+
+ /* Convert command line args */
+ handle = strtoul(argv[0], NULL, 16);
+ attrib = strtoul(argv[1], NULL, 16);
+
+ /* Do it */
+ sess = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, 0);
+ if (!sess)
+ return -1;
+
+ status = set_attrib(sess, handle, attrib, argv[2]);
+ sdp_close(sess);
+
+ return status;
+}
+
+/*
+ * We do only simple data sequences. Sequence of sequences is a pain ;-)
+ * Jean II
+ */
+static int set_attribseq(sdp_session_t *session, uint32_t handle, uint16_t attrib, int argc, char **argv)
+{
+ sdp_list_t *attrid_list;
+ uint32_t range = 0x0000ffff;
+ sdp_record_t *rec;
+ sdp_data_t *pSequenceHolder = NULL;
+ void **dtdArray;
+ void **valueArray;
+ void **allocArray;
+ uint8_t uuid16 = SDP_UUID16;
+ uint8_t uint32 = SDP_UINT32;
+ uint8_t str8 = SDP_TEXT_STR8;
+ int i, ret = 0;
+
+ /* Get the old SDP record */
+ attrid_list = sdp_list_append(NULL, &range);
+ rec = sdp_service_attr_req(session, handle, SDP_ATTR_REQ_RANGE, attrid_list);
+ sdp_list_free(attrid_list, NULL);
+
+ if (!rec) {
+ printf("Service get request failed.\n");
+ return -1;
+ }
+
+ /* Create arrays */
+ dtdArray = (void **)malloc(argc * sizeof(void *));
+ valueArray = (void **)malloc(argc * sizeof(void *));
+ allocArray = (void **)malloc(argc * sizeof(void *));
+
+ /* Loop on all args, add them in arrays */
+ for (i = 0; i < argc; i++) {
+ /* Check the type of attribute */
+ if (!strncasecmp(argv[i], "u0x", 3)) {
+ /* UUID16 */
+ uint16_t value_int = strtoul((argv[i]) + 3, NULL, 16);
+ uuid_t *value_uuid = (uuid_t *) malloc(sizeof(uuid_t));
+ allocArray[i] = value_uuid;
+ sdp_uuid16_create(value_uuid, value_int);
+
+ printf("Adding uuid16 0x%X to record 0x%X\n", value_int, handle);
+ dtdArray[i] = &uuid16;
+ valueArray[i] = &value_uuid->value.uuid16;
+ } else if (!strncasecmp(argv[i], "0x", 2)) {
+ /* Int */
+ uint32_t *value_int = (uint32_t *) malloc(sizeof(int));
+ allocArray[i] = value_int;
+ *value_int = strtoul((argv[i]) + 2, NULL, 16);
+
+ printf("Adding int 0x%X to record 0x%X\n", *value_int, handle);
+ dtdArray[i] = &uint32;
+ valueArray[i] = value_int;
+ } else {
+ /* String */
+ printf("Adding string \"%s\" to record 0x%X\n", argv[i], handle);
+ dtdArray[i] = &str8;
+ valueArray[i] = argv[i];
+ }
+ }
+
+ /* Add this sequence to the attrib list */
+ pSequenceHolder = sdp_seq_alloc(dtdArray, valueArray, argc);
+ if (pSequenceHolder) {
+ sdp_attr_replace(rec, attrib, pSequenceHolder);
+
+ /* Update on the server */
+ ret = sdp_device_record_update(session, &interface, rec);
+ if (ret < 0)
+ printf("Service Record update failed (%d).\n", errno);
+ } else
+ printf("Failed to create pSequenceHolder\n");
+
+ /* Cleanup */
+ for (i = 0; i < argc; i++)
+ free(allocArray[i]);
+
+ free(dtdArray);
+ free(valueArray);
+ free(allocArray);
+
+ sdp_record_free(rec);
+
+ return ret;
+}
+
+static struct option seq_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *seq_help =
+ "Usage:\n"
+ "\tget record_handle attrib_id attrib_values\n";
+
+/*
+ * Add an attribute sequence to an existing SDP record
+ * on the local SDP server
+ */
+static int cmd_setseq(int argc, char **argv)
+{
+ int opt, status;
+ uint32_t handle;
+ uint16_t attrib;
+ sdp_session_t *sess;
+
+ for_each_opt(opt, seq_options, NULL) {
+ switch(opt) {
+ default:
+ printf("%s", seq_help);
+ return -1;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 3) {
+ printf("%s", seq_help);
+ return -1;
+ }
+
+ /* Convert command line args */
+ handle = strtoul(argv[0], NULL, 16);
+ attrib = strtoul(argv[1], NULL, 16);
+
+ argc -= 2;
+ argv += 2;
+
+ /* Do it */
+ sess = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, 0);
+ if (!sess)
+ return -1;
+
+ status = set_attribseq(sess, handle, attrib, argc, argv);
+ sdp_close(sess);
+
+ return status;
+}
+
+static void print_service_class(void *value, void *userData)
+{
+ char ServiceClassUUID_str[MAX_LEN_SERVICECLASS_UUID_STR];
+ uuid_t *uuid = (uuid_t *)value;
+
+ sdp_uuid2strn(uuid, UUID_str, MAX_LEN_UUID_STR);
+ sdp_svclass_uuid2strn(uuid, ServiceClassUUID_str, MAX_LEN_SERVICECLASS_UUID_STR);
+ if (uuid->type != SDP_UUID128)
+ printf(" \"%s\" (0x%s)\n", ServiceClassUUID_str, UUID_str);
+ else
+ printf(" UUID 128: %s\n", UUID_str);
+}
+
+static void print_service_desc(void *value, void *user)
+{
+ char str[MAX_LEN_PROTOCOL_UUID_STR];
+ sdp_data_t *p = (sdp_data_t *)value, *s;
+ int i = 0, proto = 0;
+
+ for (; p; p = p->next, i++) {
+ switch (p->dtd) {
+ case SDP_UUID16:
+ case SDP_UUID32:
+ case SDP_UUID128:
+ sdp_uuid2strn(&p->val.uuid, UUID_str, MAX_LEN_UUID_STR);
+ sdp_proto_uuid2strn(&p->val.uuid, str, sizeof(str));
+ proto = sdp_uuid_to_proto(&p->val.uuid);
+ printf(" \"%s\" (0x%s)\n", str, UUID_str);
+ break;
+ case SDP_UINT8:
+ if (proto == RFCOMM_UUID)
+ printf(" Channel: %d\n", p->val.uint8);
+ else
+ printf(" uint8: 0x%x\n", p->val.uint8);
+ break;
+ case SDP_UINT16:
+ if (proto == L2CAP_UUID) {
+ if (i == 1)
+ printf(" PSM: %d\n", p->val.uint16);
+ else
+ printf(" Version: 0x%04x\n", p->val.uint16);
+ } else if (proto == BNEP_UUID)
+ if (i == 1)
+ printf(" Version: 0x%04x\n", p->val.uint16);
+ else
+ printf(" uint16: 0x%x\n", p->val.uint16);
+ else
+ printf(" uint16: 0x%x\n", p->val.uint16);
+ break;
+ case SDP_SEQ16:
+ printf(" SEQ16:");
+ for (s = p->val.dataseq; s; s = s->next)
+ printf(" %x", s->val.uint16);
+ printf("\n");
+ break;
+ case SDP_SEQ8:
+ printf(" SEQ8:");
+ for (s = p->val.dataseq; s; s = s->next)
+ printf(" %x", s->val.uint8);
+ printf("\n");
+ break;
+ default:
+ printf(" FIXME: dtd=0%x\n", p->dtd);
+ break;
+ }
+ }
+}
+
+static void print_lang_attr(void *value, void *user)
+{
+ sdp_lang_attr_t *lang = (sdp_lang_attr_t *)value;
+ printf(" code_ISO639: 0x%02x\n", lang->code_ISO639);
+ printf(" encoding: 0x%02x\n", lang->encoding);
+ printf(" base_offset: 0x%02x\n", lang->base_offset);
+}
+
+static void print_access_protos(void *value, void *userData)
+{
+ sdp_list_t *protDescSeq = (sdp_list_t *)value;
+ sdp_list_foreach(protDescSeq, print_service_desc, 0);
+}
+
+static void print_profile_desc(void *value, void *userData)
+{
+ sdp_profile_desc_t *desc = (sdp_profile_desc_t *)value;
+ char str[MAX_LEN_PROFILEDESCRIPTOR_UUID_STR];
+
+ sdp_uuid2strn(&desc->uuid, UUID_str, MAX_LEN_UUID_STR);
+ sdp_profile_uuid2strn(&desc->uuid, str, MAX_LEN_PROFILEDESCRIPTOR_UUID_STR);
+
+ printf(" \"%s\" (0x%s)\n", str, UUID_str);
+ if (desc->version)
+ printf(" Version: 0x%04x\n", desc->version);
+}
+
+/*
+ * Parse a SDP record in user friendly form.
+ */
+static void print_service_attr(sdp_record_t *rec)
+{
+ sdp_list_t *list = 0, *proto = 0;
+
+ sdp_record_print(rec);
+
+ printf("Service RecHandle: 0x%x\n", rec->handle);
+
+ if (sdp_get_service_classes(rec, &list) == 0) {
+ printf("Service Class ID List:\n");
+ sdp_list_foreach(list, print_service_class, 0);
+ sdp_list_free(list, free);
+ }
+ if (sdp_get_access_protos(rec, &proto) == 0) {
+ printf("Protocol Descriptor List:\n");
+ sdp_list_foreach(proto, print_access_protos, 0);
+ sdp_list_foreach(proto, (sdp_list_func_t)sdp_list_free, 0);
+ sdp_list_free(proto, 0);
+ }
+ if (sdp_get_lang_attr(rec, &list) == 0) {
+ printf("Language Base Attr List:\n");
+ sdp_list_foreach(list, print_lang_attr, 0);
+ sdp_list_free(list, free);
+ }
+ if (sdp_get_profile_descs(rec, &list) == 0) {
+ printf("Profile Descriptor List:\n");
+ sdp_list_foreach(list, print_profile_desc, 0);
+ sdp_list_free(list, free);
+ }
+}
+
+/*
+ * Support for Service (de)registration
+ */
+typedef struct {
+ uint32_t handle;
+ char *name;
+ char *provider;
+ char *desc;
+ unsigned int class;
+ unsigned int profile;
+ uint16_t psm;
+ uint8_t channel;
+ uint8_t network;
+} svc_info_t;
+
+static void add_lang_attr(sdp_record_t *r)
+{
+ sdp_lang_attr_t base_lang;
+ sdp_list_t *langs = 0;
+
+ /* UTF-8 MIBenum (http://www.iana.org/assignments/character-sets) */
+ base_lang.code_ISO639 = (0x65 << 8) | 0x6e;
+ base_lang.encoding = 106;
+ base_lang.base_offset = SDP_PRIMARY_LANG_BASE;
+ langs = sdp_list_append(0, &base_lang);
+ sdp_set_lang_attr(r, langs);
+ sdp_list_free(langs, 0);
+}
+
+static int add_sp(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *apseq, *proto[2], *profiles, *root, *aproto;
+ uuid_t root_uuid, sp_uuid, l2cap, rfcomm;
+ sdp_profile_desc_t profile;
+ sdp_record_t record;
+ uint8_t u8 = si->channel ? si->channel : 1;
+ sdp_data_t *channel;
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+ sdp_list_free(root, 0);
+
+ sdp_uuid16_create(&sp_uuid, SERIAL_PORT_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &sp_uuid);
+ sdp_set_service_classes(&record, svclass_id);
+ sdp_list_free(svclass_id, 0);
+
+ sdp_uuid16_create(&profile.uuid, SERIAL_PORT_PROFILE_ID);
+ profile.version = 0x0100;
+ profiles = sdp_list_append(0, &profile);
+ sdp_set_profile_descs(&record, profiles);
+ sdp_list_free(profiles, 0);
+
+ sdp_uuid16_create(&l2cap, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&rfcomm, RFCOMM_UUID);
+ proto[1] = sdp_list_append(0, &rfcomm);
+ channel = sdp_data_alloc(SDP_UINT8, &u8);
+ proto[1] = sdp_list_append(proto[1], channel);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ add_lang_attr(&record);
+
+ sdp_set_info_attr(&record, "Serial Port", "BlueZ", "COM Port");
+
+ sdp_set_url_attr(&record, "http://www.bluez.org/",
+ "http://www.bluez.org/", "http://www.bluez.org/");
+
+ sdp_set_service_id(&record, sp_uuid);
+ sdp_set_service_ttl(&record, 0xffff);
+ sdp_set_service_avail(&record, 0xff);
+ sdp_set_record_state(&record, 0x00001234);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto end;
+ }
+
+ printf("Serial Port service registered\n");
+
+end:
+ sdp_data_free(channel);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_dun(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root, *aproto;
+ uuid_t rootu, dun, gn, l2cap, rfcomm;
+ sdp_profile_desc_t profile;
+ sdp_list_t *proto[2];
+ sdp_record_t record;
+ uint8_t u8 = si->channel ? si->channel : 2;
+ sdp_data_t *channel;
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&rootu, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &rootu);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&dun, DIALUP_NET_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &dun);
+ sdp_uuid16_create(&gn, GENERIC_NETWORKING_SVCLASS_ID);
+ svclass_id = sdp_list_append(svclass_id, &gn);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile.uuid, DIALUP_NET_PROFILE_ID);
+ profile.version = 0x0100;
+ pfseq = sdp_list_append(0, &profile);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&rfcomm, RFCOMM_UUID);
+ proto[1] = sdp_list_append(0, &rfcomm);
+ channel = sdp_data_alloc(SDP_UINT8, &u8);
+ proto[1] = sdp_list_append(proto[1], channel);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sdp_set_info_attr(&record, "Dial-Up Networking", 0, 0);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto end;
+ }
+
+ printf("Dial-Up Networking service registered\n");
+
+end:
+ sdp_data_free(channel);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_fax(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, fax_uuid, tel_uuid, l2cap_uuid, rfcomm_uuid;
+ sdp_profile_desc_t profile;
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t record;
+ uint8_t u8 = si->channel? si->channel : 3;
+ sdp_data_t *channel;
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&fax_uuid, FAX_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &fax_uuid);
+ sdp_uuid16_create(&tel_uuid, GENERIC_TELEPHONY_SVCLASS_ID);
+ svclass_id = sdp_list_append(svclass_id, &tel_uuid);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile.uuid, FAX_PROFILE_ID);
+ profile.version = 0x0100;
+ pfseq = sdp_list_append(0, &profile);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap_uuid);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto[1] = sdp_list_append(0, &rfcomm_uuid);
+ channel = sdp_data_alloc(SDP_UINT8, &u8);
+ proto[1] = sdp_list_append(proto[1], channel);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sdp_set_info_attr(&record, "Fax", 0, 0);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto end;
+ }
+ printf("Fax service registered\n");
+end:
+ sdp_data_free(channel);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+ return ret;
+}
+
+static int add_lan(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid;
+ sdp_profile_desc_t profile;
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t record;
+ uint8_t u8 = si->channel ? si->channel : 4;
+ sdp_data_t *channel;
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&svclass_uuid, LAN_ACCESS_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &svclass_uuid);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile.uuid, LAN_ACCESS_PROFILE_ID);
+ profile.version = 0x0100;
+ pfseq = sdp_list_append(0, &profile);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap_uuid);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto[1] = sdp_list_append(0, &rfcomm_uuid);
+ channel = sdp_data_alloc(SDP_UINT8, &u8);
+ proto[1] = sdp_list_append(proto[1], channel);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sdp_set_info_attr(&record, "LAN Access over PPP", 0, 0);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto end;
+ }
+
+ printf("LAN Access service registered\n");
+
+end:
+ sdp_data_free(channel);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_headset(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid;
+ sdp_profile_desc_t profile;
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t record;
+ uint8_t u8 = si->channel ? si->channel : 5;
+ sdp_data_t *channel;
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&svclass_uuid, HEADSET_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &svclass_uuid);
+ sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
+ svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile.uuid, HEADSET_PROFILE_ID);
+ profile.version = 0x0100;
+ pfseq = sdp_list_append(0, &profile);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap_uuid);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto[1] = sdp_list_append(0, &rfcomm_uuid);
+ channel = sdp_data_alloc(SDP_UINT8, &u8);
+ proto[1] = sdp_list_append(proto[1], channel);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sdp_set_info_attr(&record, "Headset", 0, 0);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto end;
+ }
+
+ printf("Headset service registered\n");
+
+end:
+ sdp_data_free(channel);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_headset_ag(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid;
+ sdp_profile_desc_t profile;
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t record;
+ uint8_t u8 = si->channel ? si->channel : 7;
+ sdp_data_t *channel;
+ uint8_t netid = si->network ? si->network : 0x01; // ???? profile document
+ sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid);
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&svclass_uuid, HEADSET_AGW_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &svclass_uuid);
+ sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
+ svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile.uuid, HEADSET_PROFILE_ID);
+ profile.version = 0x0100;
+ pfseq = sdp_list_append(0, &profile);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap_uuid);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto[1] = sdp_list_append(0, &rfcomm_uuid);
+ channel = sdp_data_alloc(SDP_UINT8, &u8);
+ proto[1] = sdp_list_append(proto[1], channel);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sdp_set_info_attr(&record, "Voice Gateway", 0, 0);
+
+ sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network);
+
+ if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto end;
+ }
+
+ printf("Headset AG service registered\n");
+
+end:
+ sdp_data_free(channel);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_handsfree(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid;
+ sdp_profile_desc_t profile;
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t record;
+ uint8_t u8 = si->channel ? si->channel : 6;
+ uint16_t u16 = 0x31;
+ sdp_data_t *channel, *features;
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&svclass_uuid, HANDSFREE_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &svclass_uuid);
+ sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
+ svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID);
+ profile.version = 0x0101;
+ pfseq = sdp_list_append(0, &profile);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap_uuid);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto[1] = sdp_list_append(0, &rfcomm_uuid);
+ channel = sdp_data_alloc(SDP_UINT8, &u8);
+ proto[1] = sdp_list_append(proto[1], channel);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ features = sdp_data_alloc(SDP_UINT16, &u16);
+ sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sdp_set_info_attr(&record, "Handsfree", 0, 0);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto end;
+ }
+
+ printf("Handsfree service registered\n");
+
+end:
+ sdp_data_free(channel);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_handsfree_ag(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid;
+ sdp_profile_desc_t profile;
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t record;
+ uint8_t u8 = si->channel ? si->channel : 7;
+ uint16_t u16 = 0x17;
+ sdp_data_t *channel, *features;
+ uint8_t netid = si->network ? si->network : 0x01; // ???? profile document
+ sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid);
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&svclass_uuid, HANDSFREE_AGW_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &svclass_uuid);
+ sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
+ svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID);
+ profile.version = 0x0105;
+ pfseq = sdp_list_append(0, &profile);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap_uuid);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto[1] = sdp_list_append(0, &rfcomm_uuid);
+ channel = sdp_data_alloc(SDP_UINT8, &u8);
+ proto[1] = sdp_list_append(proto[1], channel);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ features = sdp_data_alloc(SDP_UINT16, &u16);
+ sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sdp_set_info_attr(&record, "Voice Gateway", 0, 0);
+
+ sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network);
+
+ if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto end;
+ }
+
+ printf("Handsfree AG service registered\n");
+
+end:
+ sdp_data_free(channel);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_simaccess(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid;
+ sdp_profile_desc_t profile;
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t record;
+ uint8_t u8 = si->channel? si->channel : 8;
+ uint16_t u16 = 0x31;
+ sdp_data_t *channel, *features;
+ int ret = 0;
+
+ memset((void *)&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&svclass_uuid, SAP_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &svclass_uuid);
+ sdp_uuid16_create(&ga_svclass_uuid, GENERIC_TELEPHONY_SVCLASS_ID);
+ svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile.uuid, SAP_PROFILE_ID);
+ profile.version = 0x0101;
+ pfseq = sdp_list_append(0, &profile);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap_uuid);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto[1] = sdp_list_append(0, &rfcomm_uuid);
+ channel = sdp_data_alloc(SDP_UINT8, &u8);
+ proto[1] = sdp_list_append(proto[1], channel);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ features = sdp_data_alloc(SDP_UINT16, &u16);
+ sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sdp_set_info_attr(&record, "SIM Access", 0, 0);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto end;
+ }
+
+ printf("SIM Access service registered\n");
+
+end:
+ sdp_data_free(channel);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_opush(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, opush_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[3];
+ sdp_record_t record;
+ uint8_t chan = si->channel ? si->channel : 9;
+ sdp_data_t *channel;
+ uint8_t formats[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xff };
+ void *dtds[sizeof(formats)], *values[sizeof(formats)];
+ unsigned int i;
+ uint8_t dtd = SDP_UINT8;
+ sdp_data_t *sflist;
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&opush_uuid, OBEX_OBJPUSH_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &opush_uuid);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile[0].uuid, OBEX_OBJPUSH_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(0, profile);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap_uuid);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto[1] = sdp_list_append(0, &rfcomm_uuid);
+ channel = sdp_data_alloc(SDP_UINT8, &chan);
+ proto[1] = sdp_list_append(proto[1], channel);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ sdp_uuid16_create(&obex_uuid, OBEX_UUID);
+ proto[2] = sdp_list_append(0, &obex_uuid);
+ apseq = sdp_list_append(apseq, proto[2]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ for (i = 0; i < sizeof(formats); i++) {
+ dtds[i] = &dtd;
+ values[i] = &formats[i];
+ }
+ sflist = sdp_seq_alloc(dtds, values, sizeof(formats));
+ sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FORMATS_LIST, sflist);
+
+ sdp_set_info_attr(&record, "OBEX Object Push", 0, 0);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto end;
+ }
+
+ printf("OBEX Object Push service registered\n");
+
+end:
+ sdp_data_free(channel);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(proto[2], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_pbap(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, pbap_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[3];
+ sdp_record_t record;
+ uint8_t chan = si->channel ? si->channel : 19;
+ sdp_data_t *channel;
+ uint8_t formats[] = {0x01};
+ uint8_t dtd = SDP_UINT8;
+ sdp_data_t *sflist;
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&pbap_uuid, PBAP_PSE_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &pbap_uuid);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile[0].uuid, PBAP_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(0, profile);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap_uuid);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto[1] = sdp_list_append(0, &rfcomm_uuid);
+ channel = sdp_data_alloc(SDP_UINT8, &chan);
+ proto[1] = sdp_list_append(proto[1], channel);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ sdp_uuid16_create(&obex_uuid, OBEX_UUID);
+ proto[2] = sdp_list_append(0, &obex_uuid);
+ apseq = sdp_list_append(apseq, proto[2]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sflist = sdp_data_alloc(dtd,formats);
+ sdp_attr_add(&record, SDP_ATTR_SUPPORTED_REPOSITORIES, sflist);
+
+ sdp_set_info_attr(&record, "OBEX Phonebook Access Server", 0, 0);
+
+ if (sdp_device_record_register(session, &interface, &record,
+ SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto end;
+ }
+
+ printf("PBAP service registered\n");
+
+end:
+ sdp_data_free(channel);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(proto[2], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_ftp(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, ftrn_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[3];
+ sdp_record_t record;
+ uint8_t u8 = si->channel ? si->channel: 10;
+ sdp_data_t *channel;
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&ftrn_uuid, OBEX_FILETRANS_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &ftrn_uuid);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile[0].uuid, OBEX_FILETRANS_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(0, &profile[0]);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap_uuid);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto[1] = sdp_list_append(0, &rfcomm_uuid);
+ channel = sdp_data_alloc(SDP_UINT8, &u8);
+ proto[1] = sdp_list_append(proto[1], channel);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ sdp_uuid16_create(&obex_uuid, OBEX_UUID);
+ proto[2] = sdp_list_append(0, &obex_uuid);
+ apseq = sdp_list_append(apseq, proto[2]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sdp_set_info_attr(&record, "OBEX File Transfer", 0, 0);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto end;
+ }
+
+ printf("OBEX File Transfer service registered\n");
+
+end:
+ sdp_data_free(channel);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(proto[2], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_directprint(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, opush_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[3];
+ sdp_record_t record;
+ uint8_t chan = si->channel ? si->channel : 12;
+ sdp_data_t *channel;
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&opush_uuid, DIRECT_PRINTING_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &opush_uuid);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile[0].uuid, BASIC_PRINTING_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(0, profile);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap_uuid);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto[1] = sdp_list_append(0, &rfcomm_uuid);
+ channel = sdp_data_alloc(SDP_UINT8, &chan);
+ proto[1] = sdp_list_append(proto[1], channel);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ sdp_uuid16_create(&obex_uuid, OBEX_UUID);
+ proto[2] = sdp_list_append(0, &obex_uuid);
+ apseq = sdp_list_append(apseq, proto[2]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sdp_set_info_attr(&record, "Direct Printing", 0, 0);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto end;
+ }
+
+ printf("Direct Printing service registered\n");
+
+end:
+ sdp_data_free(channel);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(proto[2], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_nap(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, ftrn_uuid, l2cap_uuid, bnep_uuid;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t record;
+ uint16_t lp = 0x000f, ver = 0x0100;
+ sdp_data_t *psm, *version;
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&ftrn_uuid, NAP_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &ftrn_uuid);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile[0].uuid, NAP_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(0, &profile[0]);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap_uuid);
+ psm = sdp_data_alloc(SDP_UINT16, &lp);
+ proto[0] = sdp_list_append(proto[0], psm);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&bnep_uuid, BNEP_UUID);
+ proto[1] = sdp_list_append(0, &bnep_uuid);
+ version = sdp_data_alloc(SDP_UINT16, &ver);
+ proto[1] = sdp_list_append(proto[1], version);
+
+ {
+ uint16_t ptype[4] = { 0x0010, 0x0020, 0x0030, 0x0040 };
+ sdp_data_t *head, *pseq;
+ int p;
+
+ for (p = 0, head = NULL; p < 4; p++) {
+ sdp_data_t *data = sdp_data_alloc(SDP_UINT16, &ptype[p]);
+ head = sdp_seq_append(head, data);
+ }
+ pseq = sdp_data_alloc(SDP_SEQ16, head);
+ proto[1] = sdp_list_append(proto[1], pseq);
+ }
+
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sdp_set_info_attr(&record, "Network Access Point Service", 0, 0);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto end;
+ }
+
+ printf("NAP service registered\n");
+
+end:
+ sdp_data_free(version);
+ sdp_data_free(psm);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_gn(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, ftrn_uuid, l2cap_uuid, bnep_uuid;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t record;
+ uint16_t lp = 0x000f, ver = 0x0100;
+ sdp_data_t *psm, *version;
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&ftrn_uuid, GN_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &ftrn_uuid);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile[0].uuid, GN_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(0, &profile[0]);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap_uuid);
+ psm = sdp_data_alloc(SDP_UINT16, &lp);
+ proto[0] = sdp_list_append(proto[0], psm);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&bnep_uuid, BNEP_UUID);
+ proto[1] = sdp_list_append(0, &bnep_uuid);
+ version = sdp_data_alloc(SDP_UINT16, &ver);
+ proto[1] = sdp_list_append(proto[1], version);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sdp_set_info_attr(&record, "Group Network Service", 0, 0);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto end;
+ }
+
+ printf("GN service registered\n");
+
+end:
+ sdp_data_free(version);
+ sdp_data_free(psm);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_panu(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, ftrn_uuid, l2cap_uuid, bnep_uuid;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t record;
+ uint16_t lp = 0x000f, ver = 0x0100;
+ sdp_data_t *psm, *version;
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+ sdp_list_free(root, NULL);
+
+ sdp_uuid16_create(&ftrn_uuid, PANU_SVCLASS_ID);
+ svclass_id = sdp_list_append(NULL, &ftrn_uuid);
+ sdp_set_service_classes(&record, svclass_id);
+ sdp_list_free(svclass_id, NULL);
+
+ sdp_uuid16_create(&profile[0].uuid, PANU_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(NULL, &profile[0]);
+ sdp_set_profile_descs(&record, pfseq);
+ sdp_list_free(pfseq, NULL);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(NULL, &l2cap_uuid);
+ psm = sdp_data_alloc(SDP_UINT16, &lp);
+ proto[0] = sdp_list_append(proto[0], psm);
+ apseq = sdp_list_append(NULL, proto[0]);
+
+ sdp_uuid16_create(&bnep_uuid, BNEP_UUID);
+ proto[1] = sdp_list_append(NULL, &bnep_uuid);
+ version = sdp_data_alloc(SDP_UINT16, &ver);
+ proto[1] = sdp_list_append(proto[1], version);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(NULL, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sdp_set_info_attr(&record, "PAN User", NULL, NULL);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto end;
+ }
+
+ printf("PANU service registered\n");
+
+end:
+ sdp_data_free(version);
+ sdp_data_free(psm);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_hid_keyb(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_record_t record;
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, hidkb_uuid, l2cap_uuid, hidp_uuid;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[3];
+ sdp_data_t *psm, *lang_lst, *lang_lst2, *hid_spec_lst, *hid_spec_lst2;
+ unsigned int i;
+ uint8_t dtd = SDP_UINT16;
+ uint8_t dtd2 = SDP_UINT8;
+ uint8_t dtd_data = SDP_TEXT_STR8;
+ void *dtds[2];
+ void *values[2];
+ void *dtds2[2];
+ void *values2[2];
+ int leng[2];
+ uint8_t hid_spec_type = 0x22;
+ uint16_t hid_attr_lang[] = { 0x409, 0x100 };
+ static const uint16_t ctrl = 0x11;
+ static const uint16_t intr = 0x13;
+ static const uint16_t hid_attr[] = { 0x100, 0x111, 0x40, 0x0d, 0x01, 0x01 };
+ static const uint16_t hid_attr2[] = { 0x0, 0x01, 0x100, 0x1f40, 0x01, 0x01 };
+ const uint8_t hid_spec[] = {
+ 0x05, 0x01, // usage page
+ 0x09, 0x06, // keyboard
+ 0xa1, 0x01, // key codes
+ 0x85, 0x01, // minimum
+ 0x05, 0x07, // max
+ 0x19, 0xe0, // logical min
+ 0x29, 0xe7, // logical max
+ 0x15, 0x00, // report size
+ 0x25, 0x01, // report count
+ 0x75, 0x01, // input data variable absolute
+ 0x95, 0x08, // report count
+ 0x81, 0x02, // report size
+ 0x75, 0x08,
+ 0x95, 0x01,
+ 0x81, 0x01,
+ 0x75, 0x01,
+ 0x95, 0x05,
+ 0x05, 0x08,
+ 0x19, 0x01,
+ 0x29, 0x05,
+ 0x91, 0x02,
+ 0x75, 0x03,
+ 0x95, 0x01,
+ 0x91, 0x01,
+ 0x75, 0x08,
+ 0x95, 0x06,
+ 0x15, 0x00,
+ 0x26, 0xff,
+ 0x00, 0x05,
+ 0x07, 0x19,
+ 0x00, 0x2a,
+ 0xff, 0x00,
+ 0x81, 0x00,
+ 0x75, 0x01,
+ 0x95, 0x01,
+ 0x15, 0x00,
+ 0x25, 0x01,
+ 0x05, 0x0c,
+ 0x09, 0xb8,
+ 0x81, 0x06,
+ 0x09, 0xe2,
+ 0x81, 0x06,
+ 0x09, 0xe9,
+ 0x81, 0x02,
+ 0x09, 0xea,
+ 0x81, 0x02,
+ 0x75, 0x01,
+ 0x95, 0x04,
+ 0x81, 0x01,
+ 0xc0 // end tag
+ };
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ add_lang_attr(&record);
+
+ sdp_uuid16_create(&hidkb_uuid, HID_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &hidkb_uuid);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile[0].uuid, HID_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(0, profile);
+ sdp_set_profile_descs(&record, pfseq);
+
+ /* protocols */
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[1] = sdp_list_append(0, &l2cap_uuid);
+ psm = sdp_data_alloc(SDP_UINT16, &ctrl);
+ proto[1] = sdp_list_append(proto[1], psm);
+ apseq = sdp_list_append(0, proto[1]);
+
+ sdp_uuid16_create(&hidp_uuid, HIDP_UUID);
+ proto[2] = sdp_list_append(0, &hidp_uuid);
+ apseq = sdp_list_append(apseq, proto[2]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ /* additional protocols */
+ proto[1] = sdp_list_append(0, &l2cap_uuid);
+ psm = sdp_data_alloc(SDP_UINT16, &intr);
+ proto[1] = sdp_list_append(proto[1], psm);
+ apseq = sdp_list_append(0, proto[1]);
+
+ sdp_uuid16_create(&hidp_uuid, HIDP_UUID);
+ proto[2] = sdp_list_append(0, &hidp_uuid);
+ apseq = sdp_list_append(apseq, proto[2]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_add_access_protos(&record, aproto);
+
+ sdp_set_info_attr(&record, "HID Keyboard", NULL, NULL);
+
+ for (i = 0; i < sizeof(hid_attr) / 2; i++)
+ sdp_attr_add_new(&record,
+ SDP_ATTR_HID_DEVICE_RELEASE_NUMBER + i,
+ SDP_UINT16, &hid_attr[i]);
+
+ dtds[0] = &dtd2;
+ values[0] = &hid_spec_type;
+ dtds[1] = &dtd_data;
+ values[1] = (uint8_t *) hid_spec;
+ leng[0] = 0;
+ leng[1] = sizeof(hid_spec);
+ hid_spec_lst = sdp_seq_alloc_with_length(dtds, values, leng, 2);
+ hid_spec_lst2 = sdp_data_alloc(SDP_SEQ8, hid_spec_lst);
+ sdp_attr_add(&record, SDP_ATTR_HID_DESCRIPTOR_LIST, hid_spec_lst2);
+
+ for (i = 0; i < sizeof(hid_attr_lang) / 2; i++) {
+ dtds2[i] = &dtd;
+ values2[i] = &hid_attr_lang[i];
+ }
+
+ lang_lst = sdp_seq_alloc(dtds2, values2, sizeof(hid_attr_lang) / 2);
+ lang_lst2 = sdp_data_alloc(SDP_SEQ8, lang_lst);
+ sdp_attr_add(&record, SDP_ATTR_HID_LANG_ID_BASE_LIST, lang_lst2);
+
+ sdp_attr_add_new(&record, SDP_ATTR_HID_SDP_DISABLE, SDP_UINT16, &hid_attr2[0]);
+
+ for (i = 0; i < sizeof(hid_attr2) / 2 - 1; i++)
+ sdp_attr_add_new(&record, SDP_ATTR_HID_REMOTE_WAKEUP + i,
+ SDP_UINT16, &hid_attr2[i + 1]);
+
+ if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ return -1;
+ }
+
+ printf("HID keyboard service registered\n");
+
+ return 0;
+}
+
+static int add_hid_wiimote(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_record_t record;
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, hid_uuid, l2cap_uuid, hidp_uuid;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[3];
+ sdp_data_t *psm, *lang_lst, *lang_lst2, *hid_spec_lst, *hid_spec_lst2;
+ unsigned int i;
+ uint8_t dtd = SDP_UINT16;
+ uint8_t dtd2 = SDP_UINT8;
+ uint8_t dtd_data = SDP_TEXT_STR8;
+ void *dtds[2];
+ void *values[2];
+ void *dtds2[2];
+ void *values2[2];
+ int leng[2];
+ uint8_t hid_spec_type = 0x22;
+ uint16_t hid_attr_lang[] = { 0x409, 0x100 };
+ uint16_t ctrl = 0x11, intr = 0x13;
+ uint16_t hid_release = 0x0100, parser_version = 0x0111;
+ uint8_t subclass = 0x04, country = 0x33;
+ uint8_t virtual_cable = 0, reconnect = 1, sdp_disable = 0;
+ uint8_t battery = 1, remote_wakeup = 1;
+ uint16_t profile_version = 0x0100, superv_timeout = 0x0c80;
+ uint8_t norm_connect = 0, boot_device = 0;
+ const uint8_t hid_spec[] = {
+ 0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x10,
+ 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95,
+ 0x01, 0x06, 0x00, 0xff, 0x09, 0x01, 0x91, 0x00,
+ 0x85, 0x11, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00,
+ 0x85, 0x12, 0x95, 0x02, 0x09, 0x01, 0x91, 0x00,
+ 0x85, 0x13, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00,
+ 0x85, 0x14, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00,
+ 0x85, 0x15, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00,
+ 0x85, 0x16, 0x95, 0x15, 0x09, 0x01, 0x91, 0x00,
+ 0x85, 0x17, 0x95, 0x06, 0x09, 0x01, 0x91, 0x00,
+ 0x85, 0x18, 0x95, 0x15, 0x09, 0x01, 0x91, 0x00,
+ 0x85, 0x19, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00,
+ 0x85, 0x1a, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00,
+ 0x85, 0x20, 0x95, 0x06, 0x09, 0x01, 0x81, 0x00,
+ 0x85, 0x21, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
+ 0x85, 0x22, 0x95, 0x04, 0x09, 0x01, 0x81, 0x00,
+ 0x85, 0x30, 0x95, 0x02, 0x09, 0x01, 0x81, 0x00,
+ 0x85, 0x31, 0x95, 0x05, 0x09, 0x01, 0x81, 0x00,
+ 0x85, 0x32, 0x95, 0x0a, 0x09, 0x01, 0x81, 0x00,
+ 0x85, 0x33, 0x95, 0x11, 0x09, 0x01, 0x81, 0x00,
+ 0x85, 0x34, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
+ 0x85, 0x35, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
+ 0x85, 0x36, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
+ 0x85, 0x37, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
+ 0x85, 0x3d, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
+ 0x85, 0x3e, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
+ 0x85, 0x3f, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
+ 0xc0, 0x00
+ };
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&hid_uuid, HID_SVCLASS_ID);
+ svclass_id = sdp_list_append(NULL, &hid_uuid);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile[0].uuid, HID_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(NULL, profile);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[1] = sdp_list_append(0, &l2cap_uuid);
+ psm = sdp_data_alloc(SDP_UINT16, &ctrl);
+ proto[1] = sdp_list_append(proto[1], psm);
+ apseq = sdp_list_append(0, proto[1]);
+
+ sdp_uuid16_create(&hidp_uuid, HIDP_UUID);
+ proto[2] = sdp_list_append(0, &hidp_uuid);
+ apseq = sdp_list_append(apseq, proto[2]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ proto[1] = sdp_list_append(0, &l2cap_uuid);
+ psm = sdp_data_alloc(SDP_UINT16, &intr);
+ proto[1] = sdp_list_append(proto[1], psm);
+ apseq = sdp_list_append(0, proto[1]);
+
+ sdp_uuid16_create(&hidp_uuid, HIDP_UUID);
+ proto[2] = sdp_list_append(0, &hidp_uuid);
+ apseq = sdp_list_append(apseq, proto[2]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_add_access_protos(&record, aproto);
+
+ add_lang_attr(&record);
+
+ sdp_set_info_attr(&record, "Nintendo RVL-CNT-01",
+ "Nintendo", "Nintendo RVL-CNT-01");
+
+ sdp_attr_add_new(&record, SDP_ATTR_HID_DEVICE_RELEASE_NUMBER,
+ SDP_UINT16, &hid_release);
+
+ sdp_attr_add_new(&record, SDP_ATTR_HID_PARSER_VERSION,
+ SDP_UINT16, &parser_version);
+
+ sdp_attr_add_new(&record, SDP_ATTR_HID_DEVICE_SUBCLASS,
+ SDP_UINT8, &subclass);
+
+ sdp_attr_add_new(&record, SDP_ATTR_HID_COUNTRY_CODE,
+ SDP_UINT8, &country);
+
+ sdp_attr_add_new(&record, SDP_ATTR_HID_VIRTUAL_CABLE,
+ SDP_BOOL, &virtual_cable);
+
+ sdp_attr_add_new(&record, SDP_ATTR_HID_RECONNECT_INITIATE,
+ SDP_BOOL, &reconnect);
+
+ dtds[0] = &dtd2;
+ values[0] = &hid_spec_type;
+ dtds[1] = &dtd_data;
+ values[1] = (uint8_t *) hid_spec;
+ leng[0] = 0;
+ leng[1] = sizeof(hid_spec);
+ hid_spec_lst = sdp_seq_alloc_with_length(dtds, values, leng, 2);
+ hid_spec_lst2 = sdp_data_alloc(SDP_SEQ8, hid_spec_lst);
+ sdp_attr_add(&record, SDP_ATTR_HID_DESCRIPTOR_LIST, hid_spec_lst2);
+
+ for (i = 0; i < sizeof(hid_attr_lang) / 2; i++) {
+ dtds2[i] = &dtd;
+ values2[i] = &hid_attr_lang[i];
+ }
+
+ lang_lst = sdp_seq_alloc(dtds2, values2, sizeof(hid_attr_lang) / 2);
+ lang_lst2 = sdp_data_alloc(SDP_SEQ8, lang_lst);
+ sdp_attr_add(&record, SDP_ATTR_HID_LANG_ID_BASE_LIST, lang_lst2);
+
+ sdp_attr_add_new(&record, SDP_ATTR_HID_SDP_DISABLE,
+ SDP_BOOL, &sdp_disable);
+
+ sdp_attr_add_new(&record, SDP_ATTR_HID_BATTERY_POWER,
+ SDP_BOOL, &battery);
+
+ sdp_attr_add_new(&record, SDP_ATTR_HID_REMOTE_WAKEUP,
+ SDP_BOOL, &remote_wakeup);
+
+ sdp_attr_add_new(&record, SDP_ATTR_HID_PROFILE_VERSION,
+ SDP_UINT16, &profile_version);
+
+ sdp_attr_add_new(&record, SDP_ATTR_HID_SUPERVISION_TIMEOUT,
+ SDP_UINT16, &superv_timeout);
+
+ sdp_attr_add_new(&record, SDP_ATTR_HID_NORMALLY_CONNECTABLE,
+ SDP_BOOL, &norm_connect);
+
+ sdp_attr_add_new(&record, SDP_ATTR_HID_BOOT_DEVICE,
+ SDP_BOOL, &boot_device);
+
+ if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ return -1;
+ }
+
+ printf("Wii-Mote service registered\n");
+
+ return 0;
+}
+
+static int add_cip(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, l2cap, cmtp, cip;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t record;
+ uint16_t psm = si->psm ? si->psm : 0x1001;
+ uint8_t netid = si->network ? si->network : 0x02; // 0x02 = ISDN, 0x03 = GSM
+ sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid);
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&cip, CIP_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &cip);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile[0].uuid, CIP_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(0, &profile[0]);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap);
+ apseq = sdp_list_append(0, proto[0]);
+ proto[0] = sdp_list_append(proto[0], sdp_data_alloc(SDP_UINT16, &psm));
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&cmtp, CMTP_UUID);
+ proto[1] = sdp_list_append(0, &cmtp);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network);
+
+ sdp_set_info_attr(&record, "Common ISDN Access", 0, 0);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto end;
+ }
+
+ printf("CIP service registered\n");
+
+end:
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+ sdp_data_free(network);
+
+ return ret;
+}
+
+static int add_ctp(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, l2cap, tcsbin, ctp;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t record;
+ uint8_t netid = si->network ? si->network : 0x02; // 0x01-0x07 cf. p120 profile document
+ sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid);
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&ctp, CORDLESS_TELEPHONY_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &ctp);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile[0].uuid, CORDLESS_TELEPHONY_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(0, &profile[0]);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&tcsbin, TCS_BIN_UUID);
+ proto[1] = sdp_list_append(0, &tcsbin);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network);
+
+ sdp_set_info_attr(&record, "Cordless Telephony", 0, 0);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto end;
+ }
+
+ printf("CTP service registered\n");
+
+end:
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+ sdp_data_free(network);
+
+ return ret;
+}
+
+static int add_a2source(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, l2cap, avdtp, a2src;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t record;
+ sdp_data_t *psm, *version;
+ uint16_t lp = 0x0019, ver = 0x0100;
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&a2src, AUDIO_SOURCE_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &a2src);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile[0].uuid, ADVANCED_AUDIO_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(0, &profile[0]);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap);
+ psm = sdp_data_alloc(SDP_UINT16, &lp);
+ proto[0] = sdp_list_append(proto[0], psm);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&avdtp, AVDTP_UUID);
+ proto[1] = sdp_list_append(0, &avdtp);
+ version = sdp_data_alloc(SDP_UINT16, &ver);
+ proto[1] = sdp_list_append(proto[1], version);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sdp_set_info_attr(&record, "Audio Source", 0, 0);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto done;
+ }
+
+ printf("Audio source service registered\n");
+
+done:
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_a2sink(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, l2cap, avdtp, a2snk;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t record;
+ sdp_data_t *psm, *version;
+ uint16_t lp = 0x0019, ver = 0x0100;
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&a2snk, AUDIO_SINK_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &a2snk);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile[0].uuid, ADVANCED_AUDIO_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(0, &profile[0]);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap);
+ psm = sdp_data_alloc(SDP_UINT16, &lp);
+ proto[0] = sdp_list_append(proto[0], psm);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&avdtp, AVDTP_UUID);
+ proto[1] = sdp_list_append(0, &avdtp);
+ version = sdp_data_alloc(SDP_UINT16, &ver);
+ proto[1] = sdp_list_append(proto[1], version);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sdp_set_info_attr(&record, "Audio Sink", 0, 0);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto done;
+ }
+
+ printf("Audio sink service registered\n");
+
+done:
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_avrct(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, l2cap, avctp, avrct;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t record;
+ sdp_data_t *psm, *version, *features;
+ uint16_t lp = 0x0017, ver = 0x0100, feat = 0x000f;
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&avrct, AV_REMOTE_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &avrct);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(0, &profile[0]);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap);
+ psm = sdp_data_alloc(SDP_UINT16, &lp);
+ proto[0] = sdp_list_append(proto[0], psm);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&avctp, AVCTP_UUID);
+ proto[1] = sdp_list_append(0, &avctp);
+ version = sdp_data_alloc(SDP_UINT16, &ver);
+ proto[1] = sdp_list_append(proto[1], version);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ features = sdp_data_alloc(SDP_UINT16, &feat);
+ sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+ sdp_set_info_attr(&record, "AVRCP CT", 0, 0);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto done;
+ }
+
+ printf("Remote control service registered\n");
+
+done:
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_avrtg(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, l2cap, avctp, avrtg;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t record;
+ sdp_data_t *psm, *version, *features;
+ uint16_t lp = 0x0017, ver = 0x0100, feat = 0x000f;
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&avrtg, AV_REMOTE_TARGET_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &avrtg);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(0, &profile[0]);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap);
+ psm = sdp_data_alloc(SDP_UINT16, &lp);
+ proto[0] = sdp_list_append(proto[0], psm);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&avctp, AVCTP_UUID);
+ proto[1] = sdp_list_append(0, &avctp);
+ version = sdp_data_alloc(SDP_UINT16, &ver);
+ proto[1] = sdp_list_append(proto[1], version);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ features = sdp_data_alloc(SDP_UINT16, &feat);
+ sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+ sdp_set_info_attr(&record, "AVRCP TG", 0, 0);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto done;
+ }
+
+ printf("Remote target service registered\n");
+
+done:
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_udi_ue(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_record_t record;
+ sdp_list_t *root, *svclass, *proto;
+ uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid;
+ uint8_t channel = si->channel ? si->channel: 18;
+
+ memset(&record, 0, sizeof(record));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+ sdp_list_free(root, NULL);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto = sdp_list_append(proto, sdp_list_append(
+ sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
+
+ sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
+
+ sdp_uuid16_create(&svclass_uuid, UDI_MT_SVCLASS_ID);
+ svclass = sdp_list_append(NULL, &svclass_uuid);
+ sdp_set_service_classes(&record, svclass);
+ sdp_list_free(svclass, NULL);
+
+ sdp_set_info_attr(&record, "UDI UE", NULL, NULL);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ return -1;
+ }
+
+ printf("UDI UE service registered\n");
+
+ return 0;
+}
+
+static int add_udi_te(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_record_t record;
+ sdp_list_t *root, *svclass, *proto;
+ uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid;
+ uint8_t channel = si->channel ? si->channel: 19;
+
+ memset(&record, 0, sizeof(record));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+ sdp_list_free(root, NULL);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto = sdp_list_append(proto, sdp_list_append(
+ sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
+
+ sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
+
+ sdp_uuid16_create(&svclass_uuid, UDI_TA_SVCLASS_ID);
+ svclass = sdp_list_append(NULL, &svclass_uuid);
+ sdp_set_service_classes(&record, svclass);
+ sdp_list_free(svclass, NULL);
+
+ sdp_set_info_attr(&record, "UDI TE", NULL, NULL);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ return -1;
+ }
+
+ printf("UDI TE service registered\n");
+
+ return 0;
+}
+
+static unsigned char sr1_uuid[] = { 0xbc, 0x19, 0x9c, 0x24, 0x95, 0x8b, 0x4c, 0xc0,
+ 0xa2, 0xcb, 0xfd, 0x8a, 0x30, 0xbf, 0x32, 0x06 };
+
+static int add_sr1(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_record_t record;
+ sdp_list_t *root, *svclass;
+ uuid_t root_uuid, svclass_uuid;
+
+ memset(&record, 0, sizeof(record));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid128_create(&svclass_uuid, (void *) sr1_uuid);
+ svclass = sdp_list_append(NULL, &svclass_uuid);
+ sdp_set_service_classes(&record, svclass);
+
+ sdp_set_info_attr(&record, "TOSHIBA SR-1", NULL, NULL);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ return -1;
+ }
+
+ printf("Toshiba Speech Recognition SR-1 service record registered\n");
+
+ return 0;
+}
+
+static unsigned char syncmls_uuid[] = { 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x02 };
+
+static unsigned char syncmlc_uuid[] = { 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x02 };
+
+static int add_syncml(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_record_t record;
+ sdp_list_t *root, *svclass, *proto;
+ uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid;
+ uint8_t channel = si->channel ? si->channel: 15;
+
+ memset(&record, 0, sizeof(record));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid128_create(&svclass_uuid, (void *) syncmlc_uuid);
+ svclass = sdp_list_append(NULL, &svclass_uuid);
+ sdp_set_service_classes(&record, svclass);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto = sdp_list_append(proto, sdp_list_append(
+ sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
+
+ sdp_uuid16_create(&obex_uuid, OBEX_UUID);
+ proto = sdp_list_append(proto, sdp_list_append(NULL, &obex_uuid));
+
+ sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
+
+ sdp_set_info_attr(&record, "SyncML Client", NULL, NULL);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ return -1;
+ }
+
+ printf("SyncML Client service record registered\n");
+
+ return 0;
+}
+
+static unsigned char async_uuid[] = { 0x03, 0x50, 0x27, 0x8F, 0x3D, 0xCA, 0x4E, 0x62,
+ 0x83, 0x1D, 0xA4, 0x11, 0x65, 0xFF, 0x90, 0x6C };
+
+static int add_activesync(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_record_t record;
+ sdp_list_t *root, *svclass, *proto;
+ uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid;
+ uint8_t channel = si->channel ? si->channel: 21;
+
+ memset(&record, 0, sizeof(record));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto = sdp_list_append(proto, sdp_list_append(
+ sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
+
+ sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
+
+ sdp_uuid128_create(&svclass_uuid, (void *) async_uuid);
+ svclass = sdp_list_append(NULL, &svclass_uuid);
+ sdp_set_service_classes(&record, svclass);
+
+ sdp_set_info_attr(&record, "Microsoft ActiveSync", NULL, NULL);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ return -1;
+ }
+
+ printf("ActiveSync service record registered\n");
+
+ return 0;
+}
+
+static unsigned char hotsync_uuid[] = { 0xD8, 0x0C, 0xF9, 0xEA, 0x13, 0x4C, 0x11, 0xD5,
+ 0x83, 0xCE, 0x00, 0x30, 0x65, 0x7C, 0x54, 0x3C };
+
+static int add_hotsync(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_record_t record;
+ sdp_list_t *root, *svclass, *proto;
+ uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid;
+ uint8_t channel = si->channel ? si->channel: 22;
+
+ memset(&record, 0, sizeof(record));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto = sdp_list_append(proto, sdp_list_append(
+ sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
+
+ sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
+
+ sdp_uuid128_create(&svclass_uuid, (void *) hotsync_uuid);
+ svclass = sdp_list_append(NULL, &svclass_uuid);
+ sdp_set_service_classes(&record, svclass);
+
+ sdp_set_info_attr(&record, "PalmOS HotSync", NULL, NULL);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ return -1;
+ }
+
+ printf("HotSync service record registered\n");
+
+ return 0;
+}
+
+static unsigned char palmos_uuid[] = { 0xF5, 0xBE, 0xB6, 0x51, 0x41, 0x71, 0x40, 0x51,
+ 0xAC, 0xF5, 0x6C, 0xA7, 0x20, 0x22, 0x42, 0xF0 };
+
+static int add_palmos(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_record_t record;
+ sdp_list_t *root, *svclass;
+ uuid_t root_uuid, svclass_uuid;
+
+ memset(&record, 0, sizeof(record));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid128_create(&svclass_uuid, (void *) palmos_uuid);
+ svclass = sdp_list_append(NULL, &svclass_uuid);
+ sdp_set_service_classes(&record, svclass);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ return -1;
+ }
+
+ printf("PalmOS service record registered\n");
+
+ return 0;
+}
+
+static unsigned char nokid_uuid[] = { 0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x01 };
+
+static int add_nokiaid(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_record_t record;
+ sdp_list_t *root, *svclass;
+ uuid_t root_uuid, svclass_uuid;
+ uint16_t verid = 0x005f;
+ sdp_data_t *version = sdp_data_alloc(SDP_UINT16, &verid);
+
+ memset(&record, 0, sizeof(record));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid128_create(&svclass_uuid, (void *) nokid_uuid);
+ svclass = sdp_list_append(NULL, &svclass_uuid);
+ sdp_set_service_classes(&record, svclass);
+
+ sdp_attr_add(&record, SDP_ATTR_SERVICE_VERSION, version);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ sdp_data_free(version);
+ return -1;
+ }
+
+ printf("Nokia ID service record registered\n");
+
+ return 0;
+}
+
+static unsigned char pcsuite_uuid[] = { 0x00, 0x00, 0x50, 0x02, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x01 };
+
+static int add_pcsuite(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_record_t record;
+ sdp_list_t *root, *svclass, *proto;
+ uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid;
+ uint8_t channel = si->channel ? si->channel: 14;
+
+ memset(&record, 0, sizeof(record));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto = sdp_list_append(proto, sdp_list_append(
+ sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
+
+ sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
+
+ sdp_uuid128_create(&svclass_uuid, (void *) pcsuite_uuid);
+ svclass = sdp_list_append(NULL, &svclass_uuid);
+ sdp_set_service_classes(&record, svclass);
+
+ sdp_set_info_attr(&record, "Nokia PC Suite", NULL, NULL);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ return -1;
+ }
+
+ printf("Nokia PC Suite service registered\n");
+
+ return 0;
+}
+
+static unsigned char nftp_uuid[] = { 0x00, 0x00, 0x50, 0x05, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x01 };
+
+static unsigned char nsyncml_uuid[] = { 0x00, 0x00, 0x56, 0x01, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x01 };
+
+static unsigned char ngage_uuid[] = { 0x00, 0x00, 0x13, 0x01, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x01 };
+
+static unsigned char apple_uuid[] = { 0xf0, 0x72, 0x2e, 0x20, 0x0f, 0x8b, 0x4e, 0x90,
+ 0x8c, 0xc2, 0x1b, 0x46, 0xf5, 0xf2, 0xef, 0xe2 };
+
+static int add_apple(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_record_t record;
+ sdp_list_t *root;
+ uuid_t root_uuid;
+ uint32_t attr783 = 0x00000000;
+ uint32_t attr785 = 0x00000002;
+ uint16_t attr786 = 0x1234;
+
+ memset(&record, 0, sizeof(record));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_attr_add_new(&record, 0x0780, SDP_UUID128, (void *) apple_uuid);
+ sdp_attr_add_new(&record, 0x0781, SDP_TEXT_STR8, (void *) "Macmini");
+ sdp_attr_add_new(&record, 0x0782, SDP_TEXT_STR8, (void *) "PowerMac10,1");
+ sdp_attr_add_new(&record, 0x0783, SDP_UINT32, (void *) &attr783);
+ sdp_attr_add_new(&record, 0x0784, SDP_TEXT_STR8, (void *) "1.6.6f22");
+ sdp_attr_add_new(&record, 0x0785, SDP_UINT32, (void *) &attr785);
+ sdp_attr_add_new(&record, 0x0786, SDP_UUID16, (void *) &attr786);
+
+ sdp_set_info_attr(&record, "Apple Macintosh Attributes", NULL, NULL);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ return -1;
+ }
+
+ printf("Apple attribute service registered\n");
+
+ return 0;
+}
+
+static int add_isync(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_record_t record;
+ sdp_list_t *root, *svclass, *proto;
+ uuid_t root_uuid, svclass_uuid, serial_uuid, l2cap_uuid, rfcomm_uuid;
+ uint8_t channel = si->channel ? si->channel : 16;
+
+ memset(&record, 0, sizeof(record));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto = sdp_list_append(proto, sdp_list_append(
+ sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
+
+ sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
+
+ sdp_uuid16_create(&serial_uuid, SERIAL_PORT_SVCLASS_ID);
+ svclass = sdp_list_append(NULL, &serial_uuid);
+
+ sdp_uuid16_create(&svclass_uuid, APPLE_AGENT_SVCLASS_ID);
+ svclass = sdp_list_append(svclass, &svclass_uuid);
+
+ sdp_set_service_classes(&record, svclass);
+
+ sdp_set_info_attr(&record, "AppleAgent", "Bluetooth acceptor", "Apple Computer Ltd.");
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ return -1;
+ }
+
+ printf("Apple iSync service registered\n");
+
+ return 0;
+}
+
+static int add_semchla(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_record_t record;
+ sdp_profile_desc_t profile;
+ sdp_list_t *root, *svclass, *proto, *profiles;
+ uuid_t root_uuid, service_uuid, l2cap_uuid, semchla_uuid;
+ uint16_t psm = 0xf0f9;
+
+ memset(&record, 0, sizeof(record));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto = sdp_list_append(NULL, sdp_list_append(
+ sdp_list_append(NULL, &l2cap_uuid), sdp_data_alloc(SDP_UINT16, &psm)));
+
+ sdp_uuid32_create(&semchla_uuid, 0x8e770300);
+ proto = sdp_list_append(proto, sdp_list_append(NULL, &semchla_uuid));
+
+ sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
+
+ sdp_uuid32_create(&service_uuid, 0x8e771301);
+ svclass = sdp_list_append(NULL, &service_uuid);
+
+ sdp_set_service_classes(&record, svclass);
+
+ sdp_uuid32_create(&profile.uuid, 0x8e771302); // Headset
+ //sdp_uuid32_create(&profile.uuid, 0x8e771303); // Phone
+ profile.version = 0x0100;
+ profiles = sdp_list_append(NULL, &profile);
+ sdp_set_profile_descs(&record, profiles);
+
+ sdp_set_info_attr(&record, "SEMC HLA", NULL, NULL);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ return -1;
+ }
+
+ /* SEMC High Level Authentication */
+ printf("SEMC HLA service registered\n");
+
+ return 0;
+}
+
+static int add_gatt(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *apseq, *proto[2], *profiles, *root, *aproto;
+ uuid_t root_uuid, proto_uuid, gatt_uuid, l2cap;
+ sdp_profile_desc_t profile;
+ sdp_record_t record;
+ sdp_data_t *psm, *sh, *eh;
+ uint16_t att_psm = 27, start = 0x0001, end = 0x000f;
+ int ret;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+ sdp_list_free(root, NULL);
+
+ sdp_uuid16_create(&gatt_uuid, GENERIC_ATTRIB_SVCLASS_ID);
+ svclass_id = sdp_list_append(NULL, &gatt_uuid);
+ sdp_set_service_classes(&record, svclass_id);
+ sdp_list_free(svclass_id, NULL);
+
+ sdp_uuid16_create(&profile.uuid, GENERIC_ATTRIB_PROFILE_ID);
+ profile.version = 0x0100;
+ profiles = sdp_list_append(NULL, &profile);
+ sdp_set_profile_descs(&record, profiles);
+ sdp_list_free(profiles, NULL);
+
+ sdp_uuid16_create(&l2cap, L2CAP_UUID);
+ proto[0] = sdp_list_append(NULL, &l2cap);
+ psm = sdp_data_alloc(SDP_UINT16, &att_psm);
+ proto[0] = sdp_list_append(proto[0], psm);
+ apseq = sdp_list_append(NULL, proto[0]);
+
+ sdp_uuid16_create(&proto_uuid, ATT_UUID);
+ proto[1] = sdp_list_append(NULL, &proto_uuid);
+ sh = sdp_data_alloc(SDP_UINT16, &start);
+ proto[1] = sdp_list_append(proto[1], sh);
+ eh = sdp_data_alloc(SDP_UINT16, &end);
+ proto[1] = sdp_list_append(proto[1], eh);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(NULL, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sdp_set_info_attr(&record, "Generic Attribute Profile", "BlueZ", NULL);
+
+ sdp_set_url_attr(&record, "http://www.bluez.org/",
+ "http://www.bluez.org/", "http://www.bluez.org/");
+
+ sdp_set_service_id(&record, gatt_uuid);
+
+ ret = sdp_device_record_register(session, &interface, &record,
+ SDP_RECORD_PERSIST);
+ if (ret < 0)
+ printf("Service Record registration failed\n");
+ else
+ printf("Generic Attribute Profile Service registered\n");
+
+ sdp_data_free(psm);
+ sdp_data_free(sh);
+ sdp_data_free(eh);
+ sdp_list_free(proto[0], NULL);
+ sdp_list_free(proto[1], NULL);
+ sdp_list_free(apseq, NULL);
+ sdp_list_free(aproto, NULL);
+
+ return ret;
+}
+
+struct {
+ char *name;
+ uint32_t class;
+ int (*add)(sdp_session_t *sess, svc_info_t *si);
+ unsigned char *uuid;
+} service[] = {
+ { "DID", PNP_INFO_SVCLASS_ID, NULL, },
+
+ { "SP", SERIAL_PORT_SVCLASS_ID, add_sp },
+ { "DUN", DIALUP_NET_SVCLASS_ID, add_dun },
+ { "LAN", LAN_ACCESS_SVCLASS_ID, add_lan },
+ { "FAX", FAX_SVCLASS_ID, add_fax },
+ { "OPUSH", OBEX_OBJPUSH_SVCLASS_ID, add_opush },
+ { "FTP", OBEX_FILETRANS_SVCLASS_ID, add_ftp },
+ { "PRINT", DIRECT_PRINTING_SVCLASS_ID, add_directprint },
+
+ { "HS", HEADSET_SVCLASS_ID, add_headset },
+ { "HSAG", HEADSET_AGW_SVCLASS_ID, add_headset_ag },
+ { "HF", HANDSFREE_SVCLASS_ID, add_handsfree },
+ { "HFAG", HANDSFREE_AGW_SVCLASS_ID, add_handsfree_ag},
+ { "SAP", SAP_SVCLASS_ID, add_simaccess },
+ { "PBAP", PBAP_SVCLASS_ID, add_pbap, },
+
+ { "NAP", NAP_SVCLASS_ID, add_nap },
+ { "GN", GN_SVCLASS_ID, add_gn },
+ { "PANU", PANU_SVCLASS_ID, add_panu },
+
+ { "HCRP", HCR_SVCLASS_ID, NULL },
+ { "HID", HID_SVCLASS_ID, NULL },
+ { "KEYB", HID_SVCLASS_ID, add_hid_keyb },
+ { "WIIMOTE", HID_SVCLASS_ID, add_hid_wiimote },
+ { "CIP", CIP_SVCLASS_ID, add_cip },
+ { "CTP", CORDLESS_TELEPHONY_SVCLASS_ID, add_ctp },
+
+ { "A2SRC", AUDIO_SOURCE_SVCLASS_ID, add_a2source },
+ { "A2SNK", AUDIO_SINK_SVCLASS_ID, add_a2sink },
+ { "AVRCT", AV_REMOTE_SVCLASS_ID, add_avrct },
+ { "AVRTG", AV_REMOTE_TARGET_SVCLASS_ID, add_avrtg },
+
+ { "UDIUE", UDI_MT_SVCLASS_ID, add_udi_ue },
+ { "UDITE", UDI_TA_SVCLASS_ID, add_udi_te },
+
+ { "SEMCHLA", 0x8e771301, add_semchla },
+
+ { "SR1", 0, add_sr1, sr1_uuid },
+ { "SYNCML", 0, add_syncml, syncmlc_uuid },
+ { "SYNCMLSERV", 0, NULL, syncmls_uuid },
+ { "ACTIVESYNC", 0, add_activesync, async_uuid },
+ { "HOTSYNC", 0, add_hotsync, hotsync_uuid },
+ { "PALMOS", 0, add_palmos, palmos_uuid },
+ { "NOKID", 0, add_nokiaid, nokid_uuid },
+ { "PCSUITE", 0, add_pcsuite, pcsuite_uuid },
+ { "NFTP", 0, NULL, nftp_uuid },
+ { "NSYNCML", 0, NULL, nsyncml_uuid },
+ { "NGAGE", 0, NULL, ngage_uuid },
+ { "APPLE", 0, add_apple, apple_uuid },
+
+ { "ISYNC", APPLE_AGENT_SVCLASS_ID, add_isync, },
+ { "GATT", GENERIC_ATTRIB_SVCLASS_ID, add_gatt, },
+
+ { 0 }
+};
+
+/* Add local service */
+static int add_service(bdaddr_t *bdaddr, svc_info_t *si)
+{
+ sdp_session_t *sess;
+ int i, ret = -1;
+
+ if (!si->name)
+ return -1;
+
+ sess = sdp_connect(&interface, BDADDR_LOCAL, SDP_RETRY_IF_BUSY);
+ if (!sess)
+ return -1;
+
+ for (i = 0; service[i].name; i++)
+ if (!strcasecmp(service[i].name, si->name)) {
+ if (service[i].add)
+ ret = service[i].add(sess, si);
+ goto done;
+ }
+
+ printf("Unknown service name: %s\n", si->name);
+
+done:
+ free(si->name);
+ sdp_close(sess);
+
+ return ret;
+}
+
+static struct option add_options[] = {
+ { "help", 0, 0, 'h' },
+ { "handle", 1, 0, 'r' },
+ { "psm", 1, 0, 'p' },
+ { "channel", 1, 0, 'c' },
+ { "network", 1, 0, 'n' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *add_help =
+ "Usage:\n"
+ "\tadd [--handle=RECORD_HANDLE --channel=CHANNEL] service\n";
+
+static int cmd_add(int argc, char **argv)
+{
+ svc_info_t si;
+ int opt;
+
+ memset(&si, 0, sizeof(si));
+ si.handle = 0xffffffff;
+
+ for_each_opt(opt, add_options, 0) {
+ switch (opt) {
+ case 'r':
+ if (strncasecmp(optarg, "0x", 2))
+ si.handle = atoi(optarg);
+ else
+ si.handle = strtol(optarg + 2, NULL, 16);
+ break;
+ case 'p':
+ if (strncasecmp(optarg, "0x", 2))
+ si.psm = atoi(optarg);
+ else
+ si.psm = strtol(optarg + 2, NULL, 16);
+ break;
+ case 'c':
+ if (strncasecmp(optarg, "0x", 2))
+ si.channel = atoi(optarg);
+ else
+ si.channel = strtol(optarg + 2, NULL, 16);
+ break;
+ case 'n':
+ if (strncasecmp(optarg, "0x", 2))
+ si.network = atoi(optarg);
+ else
+ si.network = strtol(optarg + 2, NULL, 16);
+ break;
+ default:
+ printf("%s", add_help);
+ return -1;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ printf("%s", add_help);
+ return -1;
+ }
+
+ si.name = strdup(argv[0]);
+
+ return add_service(0, &si);
+}
+
+/* Delete local service */
+static int del_service(bdaddr_t *bdaddr, void *arg)
+{
+ uint32_t handle, range = 0x0000ffff;
+ sdp_list_t *attr;
+ sdp_session_t *sess;
+ sdp_record_t *rec;
+
+ if (!arg) {
+ printf("Record handle was not specified.\n");
+ return -1;
+ }
+
+ sess = sdp_connect(&interface, BDADDR_LOCAL, SDP_RETRY_IF_BUSY);
+ if (!sess) {
+ printf("No local SDP server!\n");
+ return -1;
+ }
+
+ handle = strtoul((char *)arg, 0, 16);
+ attr = sdp_list_append(0, &range);
+ rec = sdp_service_attr_req(sess, handle, SDP_ATTR_REQ_RANGE, attr);
+ sdp_list_free(attr, 0);
+
+ if (!rec) {
+ printf("Service Record not found.\n");
+ sdp_close(sess);
+ return -1;
+ }
+
+ if (sdp_device_record_unregister(sess, &interface, rec)) {
+ printf("Failed to unregister service record: %s\n", strerror(errno));
+ sdp_close(sess);
+ return -1;
+ }
+
+ printf("Service Record deleted.\n");
+ sdp_close(sess);
+
+ return 0;
+}
+
+static struct option del_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *del_help =
+ "Usage:\n"
+ "\tdel record_handle\n";
+
+static int cmd_del(int argc, char **argv)
+{
+ int opt;
+
+ for_each_opt(opt, del_options, 0) {
+ switch (opt) {
+ default:
+ printf("%s", del_help);
+ return -1;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ printf("%s", del_help);
+ return -1;
+ }
+
+ return del_service(NULL, argv[0]);
+}
+
+/*
+ * Perform an inquiry and search/browse all peer found.
+ */
+static void inquiry(handler_t handler, void *arg)
+{
+ inquiry_info ii[20];
+ uint8_t count = 0;
+ int i;
+
+ printf("Inquiring ...\n");
+ if (sdp_general_inquiry(ii, 20, 8, &count) < 0) {
+ printf("Inquiry failed\n");
+ return;
+ }
+
+ for (i = 0; i < count; i++)
+ handler(&ii[i].bdaddr, arg);
+}
+
+static void doprintf(void *data, const char *str)
+{
+ printf("%s", str);
+}
+
+/*
+ * Search for a specific SDP service
+ */
+static int do_search(bdaddr_t *bdaddr, struct search_context *context)
+{
+ sdp_list_t *attrid, *search, *seq, *next;
+ uint32_t range = 0x0000ffff;
+ char str[20];
+ sdp_session_t *sess;
+
+ if (!bdaddr) {
+ inquiry(do_search, context);
+ return 0;
+ }
+
+ sess = sdp_connect(&interface, bdaddr, SDP_RETRY_IF_BUSY);
+ ba2str(bdaddr, str);
+ if (!sess) {
+ printf("Failed to connect to SDP server on %s: %s\n", str, strerror(errno));
+ return -1;
+ }
+
+ if (context->view != RAW_VIEW) {
+ if (context->svc)
+ printf("Searching for %s on %s ...\n", context->svc, str);
+ else
+ printf("Browsing %s ...\n", str);
+ }
+
+ attrid = sdp_list_append(0, &range);
+ search = sdp_list_append(0, &context->group);
+ if (sdp_service_search_attr_req(sess, search, SDP_ATTR_REQ_RANGE, attrid, &seq)) {
+ printf("Service Search failed: %s\n", strerror(errno));
+ sdp_close(sess);
+ return -1;
+ }
+ sdp_list_free(attrid, 0);
+ sdp_list_free(search, 0);
+
+ for (; seq; seq = next) {
+ sdp_record_t *rec = (sdp_record_t *) seq->data;
+ struct search_context sub_context;
+
+ switch (context->view) {
+ case DEFAULT_VIEW:
+ /* Display user friendly form */
+ print_service_attr(rec);
+ printf("\n");
+ break;
+ case TREE_VIEW:
+ /* Display full tree */
+ print_tree_attr(rec);
+ printf("\n");
+ break;
+ case XML_VIEW:
+ /* Display raw XML tree */
+ convert_sdp_record_to_xml(rec, 0, doprintf);
+ break;
+ default:
+ /* Display raw tree */
+ print_raw_attr(rec);
+ break;
+ }
+
+ if (sdp_get_group_id(rec, &sub_context.group) != -1) {
+ /* Set the subcontext for browsing the sub tree */
+ memcpy(&sub_context, context, sizeof(struct search_context));
+ /* Browse the next level down if not done */
+ if (sub_context.group.value.uuid16 != context->group.value.uuid16)
+ do_search(bdaddr, &sub_context);
+ }
+ next = seq->next;
+ free(seq);
+ sdp_record_free(rec);
+ }
+
+ sdp_close(sess);
+ return 0;
+}
+
+static struct option browse_options[] = {
+ { "help", 0, 0, 'h' },
+ { "tree", 0, 0, 't' },
+ { "raw", 0, 0, 'r' },
+ { "xml", 0, 0, 'x' },
+ { "uuid", 1, 0, 'u' },
+ { "l2cap", 0, 0, 'l' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *browse_help =
+ "Usage:\n"
+ "\tbrowse [--tree] [--raw] [--xml] [--uuid uuid] [--l2cap] [bdaddr]\n";
+
+/*
+ * Browse the full SDP database (i.e. list all services starting from the
+ * root/top-level).
+ */
+static int cmd_browse(int argc, char **argv)
+{
+ struct search_context context;
+ int opt, num;
+
+ /* Initialise context */
+ memset(&context, '\0', sizeof(struct search_context));
+ /* We want to browse the top-level/root */
+ sdp_uuid16_create(&context.group, PUBLIC_BROWSE_GROUP);
+
+ for_each_opt(opt, browse_options, 0) {
+ switch (opt) {
+ case 't':
+ context.view = TREE_VIEW;
+ break;
+ case 'r':
+ context.view = RAW_VIEW;
+ break;
+ case 'x':
+ context.view = XML_VIEW;
+ break;
+ case 'u':
+ if (sscanf(optarg, "%i", &num) != 1 || num < 0 || num > 0xffff) {
+ printf("Invalid uuid %s\n", optarg);
+ return -1;
+ }
+ sdp_uuid16_create(&context.group, num);
+ break;
+ case 'l':
+ sdp_uuid16_create(&context.group, L2CAP_UUID);
+ break;
+ default:
+ printf("%s", browse_help);
+ return -1;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc >= 1) {
+ bdaddr_t bdaddr;
+ estr2ba(argv[0], &bdaddr);
+ return do_search(&bdaddr, &context);
+ }
+
+ return do_search(NULL, &context);
+}
+
+static struct option search_options[] = {
+ { "help", 0, 0, 'h' },
+ { "bdaddr", 1, 0, 'b' },
+ { "tree", 0, 0, 't' },
+ { "raw", 0, 0, 'r' },
+ { "xml", 0, 0, 'x' },
+ { 0, 0, 0, 0}
+};
+
+static const char *search_help =
+ "Usage:\n"
+ "\tsearch [--bdaddr bdaddr] [--tree] [--raw] [--xml] SERVICE\n"
+ "SERVICE is a name (string) or UUID (0x1002)\n";
+
+/*
+ * Search for a specific SDP service
+ *
+ * Note : we should support multiple services on the command line :
+ * sdptool search 0x0100 0x000f 0x1002
+ * (this would search a service supporting both L2CAP and BNEP directly in
+ * the top level browse group)
+ */
+static int cmd_search(int argc, char **argv)
+{
+ struct search_context context;
+ unsigned char *uuid = NULL;
+ uint32_t class = 0;
+ bdaddr_t bdaddr;
+ int has_addr = 0;
+ int i;
+ int opt;
+
+ /* Initialise context */
+ memset(&context, '\0', sizeof(struct search_context));
+
+ for_each_opt(opt, search_options, 0) {
+ switch (opt) {
+ case 'b':
+ estr2ba(optarg, &bdaddr);
+ has_addr = 1;
+ break;
+ case 't':
+ context.view = TREE_VIEW;
+ break;
+ case 'r':
+ context.view = RAW_VIEW;
+ break;
+ case 'x':
+ context.view = XML_VIEW;
+ break;
+ default:
+ printf("%s", search_help);
+ return -1;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ printf("%s", search_help);
+ return -1;
+ }
+
+ /* Note : we need to find a way to support search combining
+ * multiple services */
+ context.svc = strdup(argv[0]);
+ if (!strncasecmp(context.svc, "0x", 2)) {
+ int num;
+ /* This is a UUID16, just convert to int */
+ sscanf(context.svc + 2, "%X", &num);
+ class = num;
+ printf("Class 0x%X\n", class);
+ } else {
+ /* Convert class name to an UUID */
+
+ for (i = 0; service[i].name; i++)
+ if (strcasecmp(context.svc, service[i].name) == 0) {
+ class = service[i].class;
+ uuid = service[i].uuid;
+ break;
+ }
+ if (!class && !uuid) {
+ printf("Unknown service %s\n", context.svc);
+ return -1;
+ }
+ }
+
+ if (class) {
+ if (class & 0xffff0000)
+ sdp_uuid32_create(&context.group, class);
+ else {
+ uint16_t class16 = class & 0xffff;
+ sdp_uuid16_create(&context.group, class16);
+ }
+ } else
+ sdp_uuid128_create(&context.group, uuid);
+
+ if (has_addr)
+ return do_search(&bdaddr, &context);
+
+ return do_search(NULL, &context);
+}
+
+/*
+ * Show how to get a specific SDP record by its handle.
+ * Not really useful to the user, just show how it can be done...
+ */
+static int get_service(bdaddr_t *bdaddr, struct search_context *context, int quite)
+{
+ sdp_list_t *attrid;
+ uint32_t range = 0x0000ffff;
+ sdp_record_t *rec;
+ sdp_session_t *session = sdp_connect(&interface, bdaddr, SDP_RETRY_IF_BUSY);
+
+ if (!session) {
+ char str[20];
+ ba2str(bdaddr, str);
+ printf("Failed to connect to SDP server on %s: %s\n", str, strerror(errno));
+ return -1;
+ }
+
+ attrid = sdp_list_append(0, &range);
+ rec = sdp_service_attr_req(session, context->handle, SDP_ATTR_REQ_RANGE, attrid);
+ sdp_list_free(attrid, 0);
+ sdp_close(session);
+
+ if (!rec) {
+ if (!quite) {
+ printf("Service get request failed.\n");
+ return -1;
+ } else
+ return 0;
+ }
+
+ switch (context->view) {
+ case DEFAULT_VIEW:
+ /* Display user friendly form */
+ print_service_attr(rec);
+ printf("\n");
+ break;
+ case TREE_VIEW:
+ /* Display full tree */
+ print_tree_attr(rec);
+ printf("\n");
+ break;
+ case XML_VIEW:
+ /* Display raw XML tree */
+ convert_sdp_record_to_xml(rec, 0, doprintf);
+ break;
+ default:
+ /* Display raw tree */
+ print_raw_attr(rec);
+ break;
+ }
+
+ sdp_record_free(rec);
+ return 0;
+}
+
+static struct option records_options[] = {
+ { "help", 0, 0, 'h' },
+ { "tree", 0, 0, 't' },
+ { "raw", 0, 0, 'r' },
+ { "xml", 0, 0, 'x' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *records_help =
+ "Usage:\n"
+ "\trecords [--tree] [--raw] [--xml] bdaddr\n";
+
+/*
+ * Request possible SDP service records
+ */
+static int cmd_records(int argc, char **argv)
+{
+ struct search_context context;
+ uint32_t base[] = { 0x10000, 0x10300, 0x10500,
+ 0x1002e, 0x110b, 0x90000, 0x2008000,
+ 0x4000000, 0x100000, 0x1000000,
+ 0x4f491100, 0x4f491200 };
+ bdaddr_t bdaddr;
+ unsigned int i, n, num = 32;
+ int opt, err = 0;
+
+ /* Initialise context */
+ memset(&context, '\0', sizeof(struct search_context));
+
+ for_each_opt(opt, records_options, 0) {
+ switch (opt) {
+ case 't':
+ context.view = TREE_VIEW;
+ break;
+ case 'r':
+ context.view = RAW_VIEW;
+ break;
+ case 'x':
+ context.view = XML_VIEW;
+ break;
+ default:
+ printf("%s", records_help);
+ return -1;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ printf("%s", records_help);
+ return -1;
+ }
+
+ /* Convert command line parameters */
+ estr2ba(argv[0], &bdaddr);
+
+ for (i = 0; i < sizeof(base) / sizeof(uint32_t); i++)
+ for (n = 0; n < num; n++) {
+ context.handle = base[i] + n;
+ err = get_service(&bdaddr, &context, 1);
+ if (err < 0)
+ goto done;
+ }
+
+done:
+ return 0;
+}
+
+static struct option get_options[] = {
+ { "help", 0, 0, 'h' },
+ { "bdaddr", 1, 0, 'b' },
+ { "tree", 0, 0, 't' },
+ { "raw", 0, 0, 'r' },
+ { "xml", 0, 0, 'x' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *get_help =
+ "Usage:\n"
+ "\tget [--tree] [--raw] [--xml] [--bdaddr bdaddr] record_handle\n";
+
+/*
+ * Get a specific SDP record on the local SDP server
+ */
+static int cmd_get(int argc, char **argv)
+{
+ struct search_context context;
+ bdaddr_t bdaddr;
+ int has_addr = 0;
+ int opt;
+
+ /* Initialise context */
+ memset(&context, '\0', sizeof(struct search_context));
+
+ for_each_opt(opt, get_options, 0) {
+ switch (opt) {
+ case 'b':
+ estr2ba(optarg, &bdaddr);
+ has_addr = 1;
+ break;
+ case 't':
+ context.view = TREE_VIEW;
+ break;
+ case 'r':
+ context.view = RAW_VIEW;
+ break;
+ case 'x':
+ context.view = XML_VIEW;
+ break;
+ default:
+ printf("%s", get_help);
+ return -1;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ printf("%s", get_help);
+ return -1;
+ }
+
+ /* Convert command line parameters */
+ context.handle = strtoul(argv[0], 0, 16);
+
+ return get_service(has_addr ? &bdaddr : BDADDR_LOCAL, &context, 0);
+}
+
+static struct {
+ char *cmd;
+ int (*func)(int argc, char **argv);
+ char *doc;
+} command[] = {
+ { "search", cmd_search, "Search for a service" },
+ { "browse", cmd_browse, "Browse all available services" },
+ { "records", cmd_records, "Request all records" },
+ { "add", cmd_add, "Add local service" },
+ { "del", cmd_del, "Delete local service" },
+ { "get", cmd_get, "Get local service" },
+ { "setattr", cmd_setattr, "Set/Add attribute to a SDP record" },
+ { "setseq", cmd_setseq, "Set/Add attribute sequence to a SDP record" },
+ { 0, 0, 0 }
+};
+
+static void usage(void)
+{
+ int i, pos = 0;
+
+ printf("sdptool - SDP tool v%s\n", VERSION);
+ printf("Usage:\n"
+ "\tsdptool [options] <command> [command parameters]\n");
+ printf("Options:\n"
+ "\t-h\t\tDisplay help\n"
+ "\t-i\t\tSpecify source interface\n");
+
+ printf("Commands:\n");
+ for (i = 0; command[i].cmd; i++)
+ printf("\t%-4s\t\t%s\n", command[i].cmd, command[i].doc);
+
+ printf("\nServices:\n\t");
+ for (i = 0; service[i].name; i++) {
+ printf("%s ", service[i].name);
+ pos += strlen(service[i].name) + 1;
+ if (pos > 60) {
+ printf("\n\t");
+ pos = 0;
+ }
+ }
+ printf("\n");
+}
+
+static struct option main_options[] = {
+ { "help", 0, 0, 'h' },
+ { "device", 1, 0, 'i' },
+ { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ int i, opt;
+
+ bacpy(&interface, BDADDR_ANY);
+
+ while ((opt=getopt_long(argc, argv, "+i:h", main_options, NULL)) != -1) {
+ switch(opt) {
+ case 'i':
+ if (!strncmp(optarg, "hci", 3))
+ hci_devba(atoi(optarg + 3), &interface);
+ else
+ str2ba(optarg, &interface);
+ break;
+
+ case 'h':
+ usage();
+ exit(0);
+
+ default:
+ exit(1);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ if (argc < 1) {
+ usage();
+ exit(1);
+ }
+
+ for (i = 0; command[i].cmd; i++)
+ if (strncmp(command[i].cmd, argv[0], 4) == 0)
+ return command[i].func(argc, argv);
+
+ return 1;
+}
diff --git a/tools/ubcsp.c b/tools/ubcsp.c
new file mode 100644
index 0000000..93b8c0f
--- /dev/null
+++ b/tools/ubcsp.c
@@ -0,0 +1,1180 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2000-2005 CSR Ltd.
+ *
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/*****************************************************************************/
+/*****************************************************************************/
+/*****************************************************************************/
+/** **/
+/** ubcsp,c **/
+/** **/
+/** MicroBCSP - a very low cost implementation of the BCSP protocol **/
+/** **/
+/*****************************************************************************/
+
+#include "ubcsp.h"
+
+#if SHOW_PACKET_ERRORS || SHOW_LE_STATES
+#include <stdio.h>
+#include <windows.h>
+#endif
+
+static uint16 ubcsp_calc_crc (uint8 ch, uint16 crc);
+static uint16 ubcsp_crc_reverse (uint16);
+
+/*****************************************************************************/
+/** **/
+/** Constant Data - ROM **/
+/** **/
+/*****************************************************************************/
+
+/* This is the storage for the link establishment messages */
+
+static const uint8 ubcsp_le_buffer[4][4] =
+ {
+ { 0xDA, 0xDC, 0xED, 0xED },
+ { 0xAC, 0xAF, 0xEF, 0xEE },
+ { 0xAD, 0xEF, 0xAC, 0xED },
+ { 0xDE, 0xAD, 0xD0, 0xD0 },
+ };
+
+/* These are the link establishment headers */
+/* The two version are for the CRC and non-CRC varients */
+
+#if UBCSP_CRC
+static const uint8 ubcsp_send_le_header[4] =
+ {
+ 0x40, 0x41, 0x00, 0x7E
+ };
+#else
+static const uint8 ubcsp_send_le_header[4] =
+ {
+ 0x00, 0x41, 0x00, 0xBE
+ };
+#endif
+
+/*****************************************************************************/
+/** **/
+/** Static Data - RAM **/
+/** **/
+/*****************************************************************************/
+
+/* This is the storage for all state data for ubcsp */
+
+static struct ubcsp_configuration ubcsp_config;
+
+/* This is the ACK packet header - this will be overwritten when
+ we create an ack packet */
+
+static uint8 ubcsp_send_ack_header[4] =
+ {
+ 0x00, 0x00, 0x00, 0x00
+ };
+
+/* This is the deslip lookup table */
+
+static const uint8 ubcsp_deslip[2] =
+ {
+ SLIP_FRAME, SLIP_ESCAPE,
+ };
+
+/* This is a state machine table for link establishment */
+
+static uint8 next_le_packet[16] =
+ {
+ ubcsp_le_sync, // uninit
+ ubcsp_le_conf, // init
+ ubcsp_le_none, // active
+ ubcsp_le_none,
+ ubcsp_le_sync_resp, // sync_resp
+ ubcsp_le_sync_resp,
+ ubcsp_le_none,
+ ubcsp_le_none,
+ ubcsp_le_none, // conf_resp
+ ubcsp_le_conf_resp,
+ ubcsp_le_conf_resp,
+ ubcsp_le_none,
+ };
+
+/* This is the storage required for building send and crc data */
+
+static uint8 ubcsp_send_header[4];
+static uint8 ubcsp_send_crc[2];
+
+/* This is where the receive header is stored before the payload arrives */
+
+static uint8 ubcsp_receive_header[4];
+
+/*****************************************************************************/
+/** **/
+/** Code - ROM or RAM **/
+/** **/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/** **/
+/** ubcsp_initialize **/
+/** **/
+/** This initializes the state of the ubcsp engine to a known values **/
+/** **/
+/*****************************************************************************/
+
+void ubcsp_initialize (void)
+{
+ ubcsp_config.ack_number = 0;
+ ubcsp_config.sequence_number = 0;
+ ubcsp_config.send_ptr = 0;
+ ubcsp_config.send_size = 0;
+ ubcsp_config.receive_index = -4;
+
+ ubcsp_config.delay = 0;
+
+#if SHOW_LE_STATES
+ printf ("Hello Link Uninitialized\n");
+#endif
+
+ ubcsp_config.link_establishment_state = ubcsp_le_uninitialized;
+ ubcsp_config.link_establishment_packet = ubcsp_le_sync;
+}
+
+/*****************************************************************************/
+/** **/
+/** ubcsp_send_packet **/
+/** **/
+/** This sends a packet structure for sending to the ubcsp engine **/
+/** This can only be called when the activity indication from ubcsp_poll **/
+/** indicates that a packet can be sent with UBCSP_PACKET_SENT **/
+/** **/
+/*****************************************************************************/
+
+void ubcsp_send_packet (struct ubcsp_packet *send_packet)
+{
+ /* Initialize the send data to the packet we want to send */
+
+ ubcsp_config.send_packet = send_packet;
+
+ /* we cannot send the packet at the moment
+ when we can at the moment, just set things to 0 */
+
+ ubcsp_config.send_size = 0;
+ ubcsp_config.send_ptr = 0;
+}
+
+/*****************************************************************************/
+/** **/
+/** ubcsp_receive_packet **/
+/** **/
+/** This sends a packet structure for receiving to the ubcsp engine **/
+/** This can only be called when the activity indication from ubcsp_poll **/
+/** indicates that a packet can be sent with UBCSP_PACKET_RECEIVED **/
+/** **/
+/*****************************************************************************/
+
+void ubcsp_receive_packet (struct ubcsp_packet *receive_packet)
+{
+ /* Initialize the receive data to the packet we want to receive */
+
+ ubcsp_config.receive_packet = receive_packet;
+
+ /* setup to receive the header first */
+
+ ubcsp_config.receive_index = -4;
+}
+
+/*****************************************************************************/
+/** **/
+/** ubcsp_calc_crc **/
+/** **/
+/** Takes the next 8 bit value ch, and updates the crc with this value **/
+/** **/
+/*****************************************************************************/
+
+
+#ifdef UBCSP_CRC
+
+static uint16 ubcsp_calc_crc (uint8 ch, uint16 crc)
+{
+ /* Calculate the CRC using the above 16 entry lookup table */
+
+ static const uint16 crc_table[] =
+ {
+ 0x0000, 0x1081, 0x2102, 0x3183,
+ 0x4204, 0x5285, 0x6306, 0x7387,
+ 0x8408, 0x9489, 0xa50a, 0xb58b,
+ 0xc60c, 0xd68d, 0xe70e, 0xf78f
+ };
+
+ /* Do this four bits at a time - more code, less space */
+
+ crc = (crc >> 4) ^ crc_table[(crc ^ ch) & 0x000f];
+ crc = (crc >> 4) ^ crc_table[(crc ^ (ch >> 4)) & 0x000f];
+
+ return crc;
+}
+
+/*****************************************************************************/
+/** **/
+/** ubcsp_crc_reverse **/
+/** **/
+/** Reserves the bits in crc and returns the new value **/
+/** **/
+/*****************************************************************************/
+
+static uint16 ubcsp_crc_reverse (uint16 crc)
+{
+ int32
+ b,
+ rev;
+
+ /* Reserse the bits to compute the actual CRC value */
+
+ for (b = 0, rev=0; b < 16; b++)
+ {
+ rev = rev << 1;
+ rev |= (crc & 1);
+ crc = crc >> 1;
+ }
+
+ return rev;
+}
+
+#endif
+
+/*****************************************************************************/
+/** **/
+/** ubcsp_put_slip_uart **/
+/** **/
+/** Outputs a single octet to the uart **/
+/** If the octet needs to be escaped, then output the escape value **/
+/** and then store the second octet to be output later **/
+/** **/
+/*****************************************************************************/
+
+static void ubcsp_put_slip_uart (uint8 ch)
+{
+ /* output a single UART octet */
+
+ /* If it needs to be escaped, then output the escape octet
+ and set the send_slip_escape so that the next time we
+ output the second octet for the escape correctly.
+ This is done right at the top of ubcsp_poll */
+
+ if (ch == SLIP_FRAME)
+ {
+ put_uart (SLIP_ESCAPE);
+ ubcsp_config.send_slip_escape = SLIP_ESCAPE_FRAME;
+ }
+ else if (ch == SLIP_ESCAPE)
+ {
+ put_uart (SLIP_ESCAPE);
+ ubcsp_config.send_slip_escape = SLIP_ESCAPE_ESCAPE;
+ }
+ else
+ {
+ /* Not escaped, so just output octet */
+
+ put_uart (ch);
+ }
+}
+
+/*****************************************************************************/
+/** **/
+/** ubcsp_which_le_payload **/
+/** **/
+/** Check the payload of this packet, and determine which of the four **/
+/** link establishment packets this was. **/
+/** Can return 5 if it is not a valid link establishment packet **/
+/** **/
+/*****************************************************************************/
+
+static uint32 ubcsp_which_le_payload (const uint8 *payload)
+{
+ static int32
+ octet,
+ loop;
+
+ /* Search through the various link establishment payloads to find
+ which one we have received */
+
+ for (loop = 0; loop < 4; loop ++)
+ {
+ for (octet = 0; octet < 4; octet ++)
+ {
+ if (payload[octet] != ubcsp_le_buffer[loop][octet])
+ {
+ /* Bad match, just to loop again */
+ goto bad_match_loop;
+ }
+ }
+
+ /* All the octets matched, return the value */
+
+ return loop;
+
+ /* Jumps out of octet loop if we got a bad match */
+bad_match_loop:
+ {}
+ }
+
+ /* Non of the link establishment payloads matched - return invalid value */
+
+ return 5;
+}
+
+/*****************************************************************************/
+/** **/
+/** ubcsp_recevied_packet **/
+/** **/
+/** This function is called when we have a SLIP END octet and a full **/
+/** packet header and possibly data in the receive packet **/
+/** **/
+/*****************************************************************************/
+
+static uint8 ubcsp_recevied_packet (void)
+{
+ static uint8
+ receive_crc,
+ receive_seq,
+ receive_ack,
+ activity;
+
+#if UBCSP_CRC
+ static int32
+ loop;
+
+ static uint16
+ crc;
+#endif
+
+ static uint16
+ length;
+
+ /* Keep track of what activity this received packet will cause */
+
+ activity = 0;
+
+ /*** Do all error checks that we can ***/
+
+ /* First check the header checksum */
+
+ if (((ubcsp_receive_header[0] + ubcsp_receive_header[1] + ubcsp_receive_header[2] + ubcsp_receive_header[3]) & 0xff) != 0xff)
+ {
+ /* Header Checksum Error */
+
+#if SHOW_PACKET_ERRORS
+ printf ("\n######################## Header Checksum Error %02X %02X %02X %02X\n",
+ ubcsp_receive_header[0],
+ ubcsp_receive_header[1],
+ ubcsp_receive_header[2],
+ ubcsp_receive_header[3]);
+#endif
+
+ /* If we have a header checksum error, send an ack in return
+ this gets a packet to be resent as quickly as possible */
+
+ ubcsp_config.send_ack = 1;
+
+ return activity;
+ }
+
+ /* Decode the received packets header */
+
+ ubcsp_config.receive_packet->reliable = (ubcsp_receive_header[0] & 0x80) >> 7;
+
+ receive_crc = (ubcsp_receive_header[0] & 0x40) >> 6;
+ receive_ack = (ubcsp_receive_header[0] & 0x38) >> 3;
+ receive_seq = (ubcsp_receive_header[0] & 0x07);
+
+ ubcsp_config.receive_packet->channel = (ubcsp_receive_header[1] & 0x0f);
+
+ length =
+ ((ubcsp_receive_header[1] & 0xf0) >> 4) |
+ (ubcsp_receive_header[2] << 4);
+
+#if SHOW_PACKET_ERRORS
+ if (ubcsp_config.receive_packet->reliable)
+ {
+ printf (" : %10d Recv SEQ: %d ACK %d\n",
+ GetTickCount () % 100000,
+ receive_seq,
+ receive_ack);
+ }
+ else if (ubcsp_config.receive_packet->channel != 1)
+ {
+ printf (" : %10d Recv ACK %d\n",
+ GetTickCount () % 100000,
+ receive_ack);
+ }
+#endif
+
+ /* Check for length errors */
+
+#if UBCSP_CRC
+ if (receive_crc)
+ {
+ /* If this packet had a CRC, then the length of the payload
+ should be 2 less than the received size of the payload */
+
+ if (length + 2 != ubcsp_config.receive_index)
+ {
+ /* Slip Length Error */
+
+#if SHOW_PACKET_ERRORS
+ printf ("\n######################## Slip Length Error (With CRC) %d,%d\n", length, ubcsp_config.receive_index - 2);
+#endif
+
+ /* If we have a payload length error, send an ack in return
+ this gets a packet to be resent as quickly as possible */
+
+ ubcsp_config.send_ack = 1;
+ return activity;
+ }
+
+ /* We have a CRC at the end of this packet */
+
+ ubcsp_config.receive_index -= 2;
+
+ /* Calculate the packet CRC */
+
+ crc = 0xffff;
+
+ /* CRC the packet header */
+
+ for (loop = 0; loop < 4; loop ++)
+ {
+ crc = ubcsp_calc_crc (ubcsp_receive_header[loop], crc);
+ }
+
+ /* CRC the packet payload - without the CRC bytes */
+
+ for (loop = 0; loop < ubcsp_config.receive_index; loop ++)
+ {
+ crc = ubcsp_calc_crc (ubcsp_config.receive_packet->payload[loop], crc);
+ }
+
+ /* Reverse the CRC */
+
+ crc = ubcsp_crc_reverse (crc);
+
+ /* Check the CRC is correct */
+
+ if
+ (
+ (((crc & 0xff00) >> 8) != ubcsp_config.receive_packet->payload[ubcsp_config.receive_index]) ||
+ ((crc & 0xff) != ubcsp_config.receive_packet->payload[ubcsp_config.receive_index + 1])
+ )
+ {
+#if SHOW_PACKET_ERRORS
+ printf ("\n######################## CRC Error\n");
+#endif
+
+ /* If we have a packet crc error, send an ack in return
+ this gets a packet to be resent as quickly as possible */
+
+ ubcsp_config.send_ack = 1;
+ return activity;
+ }
+ }
+ else
+ {
+#endif
+ /* No CRC present, so just check the length of payload with that received */
+
+ if (length != ubcsp_config.receive_index)
+ {
+ /* Slip Length Error */
+
+#if SHOW_PACKET_ERRORS
+ printf ("\n######################## Slip Length Error (No CRC) %d,%d\n", length, ubcsp_config.receive_index);
+#endif
+
+ /* If we have a payload length error, send an ack in return
+ this gets a packet to be resent as quickly as possible */
+
+ ubcsp_config.send_ack = 1;
+ return activity;
+ }
+#if UBCSP_CRC
+ }
+#endif
+
+ /*** We have a fully formed packet having passed all data integrity checks ***/
+
+ /* Check if we have an ACK for the last packet we sent */
+
+ if (receive_ack != ubcsp_config.sequence_number)
+ {
+ /* Since we only have a window size of 1, if the ACK is not equal to SEQ
+ then the packet was sent */
+
+ if
+ (
+ (ubcsp_config.send_packet) &&
+ (ubcsp_config.send_packet->reliable)
+ )
+ {
+ /* We had sent a reliable packet, so clear this packet
+ Then increament the sequence number for the next packet */
+
+ ubcsp_config.send_packet = 0;
+ ubcsp_config.sequence_number ++;
+ ubcsp_config.delay = 0;
+
+ /* Notify the caller that we have SENT a packet */
+
+ activity |= UBCSP_PACKET_SENT;
+ }
+ }
+
+ /*** Now we can concentrate of the packet we have received ***/
+
+ /* Check for Link Establishment packets */
+
+ if (ubcsp_config.receive_packet->channel == 1)
+ {
+ /* Link Establishment */
+
+ ubcsp_config.delay = 0;
+
+ /* Find which link establishment packet this payload means
+ This could return 5, meaning none */
+
+ switch (ubcsp_which_le_payload (ubcsp_config.receive_packet->payload))
+ {
+ case 0:
+ {
+ /* SYNC Recv'd */
+
+#if SHOW_LE_STATES
+ printf ("Recv SYNC\n");
+#endif
+
+ /* If we receive a SYNC, then we respond to it with a SYNC RESP
+ but only if we are not active.
+ If we are active, then we have a PEER RESET */
+
+ if (ubcsp_config.link_establishment_state < ubcsp_le_active)
+ {
+ ubcsp_config.link_establishment_resp = 1;
+ }
+ else
+ {
+ /* Peer reset !!!! */
+
+#if SHOW_LE_STATES
+ printf ("\n\n\n\n\nPEER RESET\n\n");
+#endif
+
+ /* Reinitialize the link */
+
+ ubcsp_initialize ();
+
+ /* Tell the host what has happened */
+
+ return UBCSP_PEER_RESET;
+ }
+ break;
+ }
+
+ case 1:
+ {
+ /* SYNC RESP Recv'd */
+
+#if SHOW_LE_STATES
+ printf ("Recv SYNC RESP\n");
+#endif
+
+ /* If we receive a SYNC RESP, push us into the initialized state */
+
+ if (ubcsp_config.link_establishment_state < ubcsp_le_initialized)
+ {
+#if SHOW_LE_STATES
+ printf ("Link Initialized\n");
+#endif
+ ubcsp_config.link_establishment_state = ubcsp_le_initialized;
+ }
+
+ break;
+ }
+
+ case 2:
+ {
+ /* CONF Recv'd */
+
+#if SHOW_LE_STATES
+ printf ("Recv CONF\n");
+#endif
+
+ /* If we receive a CONF, and we are initialized or active
+ then respond with a CONF RESP */
+
+ if (ubcsp_config.link_establishment_state >= ubcsp_le_initialized)
+ {
+ ubcsp_config.link_establishment_resp = 2;
+ }
+
+ break;
+ }
+
+ case 3:
+ {
+ /* CONF RESP Recv'd */
+
+#if SHOW_LE_STATES
+ printf ("Recv CONF RESP\n");
+#endif
+
+ /* If we received a CONF RESP, then push us into the active state */
+
+ if (ubcsp_config.link_establishment_state < ubcsp_le_active)
+ {
+#if SHOW_LE_STATES
+ printf ("Link Active\n");
+#endif
+
+ ubcsp_config.link_establishment_state = ubcsp_le_active;
+ ubcsp_config.send_size = 0;
+
+ return activity | UBCSP_PACKET_SENT;
+ }
+
+ break;
+ }
+ }
+
+ /* We have finished processing Link Establishment packets */
+ }
+ else if (ubcsp_config.receive_index)
+ {
+ /* We have some payload data we need to process
+ but only if we are active - otherwise, we just ignore it */
+
+ if (ubcsp_config.link_establishment_state == ubcsp_le_active)
+ {
+ if (ubcsp_config.receive_packet->reliable)
+ {
+ /* If the packet we've just received was reliable
+ then send an ACK */
+
+ ubcsp_config.send_ack = 1;
+
+ /* We the sequence number we received is the same as
+ the last ACK we sent, then we have received a packet in sequence */
+
+ if (receive_seq == ubcsp_config.ack_number)
+ {
+ /* Increase the ACK number - which will be sent in the next ACK
+ or normal packet we send */
+
+ ubcsp_config.ack_number ++;
+
+ /* Set the values in the receive_packet structure, so the caller
+ knows how much data we have */
+
+ ubcsp_config.receive_packet->length = length;
+ ubcsp_config.receive_packet = 0;
+
+ /* Tell the caller that we have received a packet, and that it
+ will be ACK'ed */
+
+ activity |= UBCSP_PACKET_RECEIVED | UBCSP_PACKET_ACK;
+ }
+ }
+ else
+ {
+ /* Set the values in the receive_packet structure, so the caller
+ knows how much data we have */
+
+ ubcsp_config.receive_packet->length = length;
+ ubcsp_config.receive_packet = 0;
+
+ /* Tell the caller that we have received a packet */
+
+ activity |= UBCSP_PACKET_RECEIVED;
+ }
+ }
+ }
+
+ /* Just return any activity that occured */
+
+ return activity;
+}
+
+/*****************************************************************************/
+/** **/
+/** ubcsp_setup_packet **/
+/** **/
+/** This function is called to setup a packet to be sent **/
+/** This allows just a header, or a header and payload to be sent **/
+/** It also allows the header checksum to be precalcuated **/
+/** or calculated here **/
+/** part1 is always 4 bytes **/
+/** **/
+/*****************************************************************************/
+
+static void ubcsp_setup_packet (uint8 *part1, uint8 calc, uint8 *part2, uint16 len2)
+{
+ /* If we need to calculate the checksum, do that now */
+
+ if (calc)
+ {
+ part1[3] =
+ ~(part1[0] + part1[1] + part1[2]);
+ }
+
+ /* Setup the header send pointer and size so we can clock this out */
+
+ ubcsp_config.send_ptr = part1;
+ ubcsp_config.send_size = 4;
+
+ /* Setup the payload send pointer and size */
+
+ ubcsp_config.next_send_ptr = part2;
+ ubcsp_config.next_send_size = len2;
+
+#if UBCSP_CRC
+ /* Initialize the crc as required */
+
+ ubcsp_config.send_crc = -1;
+
+ ubcsp_config.need_send_crc = 1;
+#endif
+}
+
+/*****************************************************************************/
+/** **/
+/** ubcsp_sent_packet **/
+/** **/
+/** Called when we have finished sending a packet **/
+/** If this packet was unreliable, then notify caller, and clear the data **/
+/** **/
+/*****************************************************************************/
+
+static uint8 ubcsp_sent_packet (void)
+{
+ if (ubcsp_config.send_packet)
+ {
+ if (!ubcsp_config.send_packet->reliable)
+ {
+ /* We had a packet sent that was unreliable */
+
+ /* Forget about this packet */
+
+ ubcsp_config.send_packet = 0;
+
+ /* Notify caller that they can send another one */
+
+ return UBCSP_PACKET_SENT;
+ }
+ }
+
+ /* We didn't have a packet, or it was reliable
+ Must wait for ACK before allowing another packet to be sent */
+
+ return 0;
+}
+
+/*****************************************************************************/
+/** **/
+/** ubcsp_poll **/
+/** **/
+/** This is the main function for ubcsp **/
+/** It performs a number of tasks **/
+/** **/
+/** 1) Send another octet to the UART - escaping as required **/
+/** 2) Setup the payload to be sent after the header has been sent **/
+/** 3) Send the CRC for the packet if required **/
+/** **/
+/** 4) Calculate the next Link Establishment State **/
+/** 5) Send a Link Establishment packet **/
+/** 6) Send a normal packet if available **/
+/** 7) Send an ACK packet if required **/
+/** **/
+/** 8) Receive octets from UART and deslip them as required **/
+/** 9) Place received octets into receive header or receive payload buffer **/
+/** 10) Process received packet when SLIP_END is received **/
+/** **/
+/** 11) Keep track of ability of caller to delay recalling **/
+/** **/
+/*****************************************************************************/
+
+uint8 ubcsp_poll (uint8 *activity)
+{
+ uint8
+ delay = UBCSP_POLL_TIME_IMMEDIATE;
+
+ uint8
+ value;
+
+ /* Assume no activity to start with */
+
+ *activity = 0;
+
+ /* If we don't have to delay, then send something if we can */
+
+ if (!ubcsp_config.delay)
+ {
+ /* Do we have something we are sending to send */
+
+ if (ubcsp_config.send_size)
+ {
+ /* We have something to send so send it */
+
+ if (ubcsp_config.send_slip_escape)
+ {
+ /* Last time we send a SLIP_ESCAPE octet
+ this time send the second escape code */
+
+ put_uart (ubcsp_config.send_slip_escape);
+
+ ubcsp_config.send_slip_escape = 0;
+ }
+ else
+ {
+#if UBCSP_CRC
+ /* get the value to send, and calculate CRC as we go */
+
+ value = *ubcsp_config.send_ptr ++;
+
+ ubcsp_config.send_crc = ubcsp_calc_crc (value, ubcsp_config.send_crc);
+
+ /* Output the octet */
+
+ ubcsp_put_slip_uart (value);
+#else
+ /* Just output the octet*/
+
+ ubcsp_put_slip_uart (*ubcsp_config.send_ptr ++);
+#endif
+ }
+
+ /* If we did output a SLIP_ESCAPE, then don't process the end of a block */
+
+ if ((!ubcsp_config.send_slip_escape) && ((ubcsp_config.send_size = ubcsp_config.send_size - 1) == 0))
+ {
+ /*** We are at the end of a block - either header or payload ***/
+
+ /* setup the next block */
+
+ ubcsp_config.send_ptr = ubcsp_config.next_send_ptr;
+ ubcsp_config.send_size = ubcsp_config.next_send_size;
+ ubcsp_config.next_send_ptr = 0;
+ ubcsp_config.next_send_size = 0;
+
+#if UBCSP_CRC
+ /* If we have no successor block
+ then we might need to send the CRC */
+
+ if (!ubcsp_config.send_ptr)
+ {
+ if (ubcsp_config.need_send_crc)
+ {
+ /* reverse the CRC from what we computed along the way */
+
+ ubcsp_config.need_send_crc = 0;
+
+ ubcsp_config.send_crc = ubcsp_crc_reverse (ubcsp_config.send_crc);
+
+ /* Save in the send_crc buffer */
+
+ ubcsp_send_crc[0] = (uint8) (ubcsp_config.send_crc >> 8);
+ ubcsp_send_crc[1] = (uint8) ubcsp_config.send_crc;
+
+ /* Setup to send this buffer */
+
+ ubcsp_config.send_ptr = ubcsp_send_crc;
+ ubcsp_config.send_size = 2;
+ }
+ else
+ {
+ /* We don't need to send the crc
+ either we just have, or this packet doesn't include it */
+
+ /* Output the end of FRAME marker */
+
+ put_uart (SLIP_FRAME);
+
+ /* Check if this is an unreliable packet */
+
+ *activity |= ubcsp_sent_packet ();
+
+ /* We've sent the packet, so don't need to have be called quickly soon */
+
+ delay = UBCSP_POLL_TIME_DELAY;
+ }
+ }
+#else
+ /* If we have no successor block
+ then we might need to send the CRC */
+
+ if (!ubcsp_config.send_ptr)
+ {
+ /* Output the end of FRAME marker */
+
+ put_uart (SLIP_FRAME);
+
+ /* Check if this is an unreliable packet */
+
+ *activity |= ubcsp_sent_packet ();
+
+ /* We've sent the packet, so don't need to have be called quickly soon */
+
+ delay = UBCSP_POLL_TIME_DELAY;
+ }
+#endif
+ }
+ }
+ else if (ubcsp_config.link_establishment_packet == ubcsp_le_none)
+ {
+ /* We didn't have something to send
+ AND we have no Link Establishment packet to send */
+
+ if (ubcsp_config.link_establishment_resp & 2)
+ {
+ /* Send the start of FRAME packet */
+
+ put_uart (SLIP_FRAME);
+
+ /* We did require a RESP packet - so setup the send */
+
+ ubcsp_setup_packet ((uint8*) ubcsp_send_le_header, 0, (uint8*) ubcsp_le_buffer[ubcsp_le_conf_resp], 4);
+
+ /* We have now "sent" this packet */
+
+ ubcsp_config.link_establishment_resp = 0;
+ }
+ else if (ubcsp_config.send_packet)
+ {
+ /* There is a packet ready to be sent */
+
+ /* Send the start of FRAME packet */
+
+ put_uart (SLIP_FRAME);
+
+ /* Encode up the packet header using ACK and SEQ numbers */
+
+ ubcsp_send_header[0] =
+ (ubcsp_config.send_packet->reliable << 7) |
+#if UBCSP_CRC
+ 0x40 | /* Always use CRC's */
+#endif
+ (ubcsp_config.ack_number << 3) |
+ (ubcsp_config.sequence_number);
+
+ /* Encode up the packet header's channel and length */
+ ubcsp_send_header[1] =
+ (ubcsp_config.send_packet->channel & 0x0f) |
+ ((ubcsp_config.send_packet->length << 4) & 0xf0);
+
+ ubcsp_send_header[2] =
+ (ubcsp_config.send_packet->length >> 4) & 0xff;
+
+ /* Let the ubcsp_setup_packet function calculate the header checksum */
+
+ ubcsp_setup_packet ((uint8*) ubcsp_send_header, 1, ubcsp_config.send_packet->payload, ubcsp_config.send_packet->length);
+
+ /* Don't need to send an ACK - we just place on in this packet */
+
+ ubcsp_config.send_ack = 0;
+
+#if SHOW_PACKET_ERRORS
+ printf (" : %10d Send %d Ack %d\n",
+ GetTickCount () % 100000,
+ ubcsp_config.sequence_number,
+ ubcsp_config.ack_number);
+#endif
+ }
+ else if (ubcsp_config.send_ack)
+ {
+ /* Send the start of FRAME packet */
+
+ put_uart (SLIP_FRAME);
+
+#if SHOW_PACKET_ERRORS
+ printf (" : %10d Send ACK %d\n",
+ GetTickCount () % 100000,
+ ubcsp_config.ack_number);
+#endif
+
+ /* The ack packet is already computed apart from the first octet */
+
+ ubcsp_send_ack_header[0] =
+#if UBCSP_CRC
+ 0x40 |
+#endif
+ (ubcsp_config.ack_number << 3);
+
+ /* Let the ubcsp_setup_packet function calculate the header checksum */
+
+ ubcsp_setup_packet (ubcsp_send_ack_header, 1, 0, 0);
+
+ /* We've now sent the ack */
+
+ ubcsp_config.send_ack = 0;
+ }
+ else
+ {
+ /* We didn't have a Link Establishment response packet,
+ a normal packet or an ACK packet to send */
+
+ delay = UBCSP_POLL_TIME_DELAY;
+ }
+ }
+ else
+ {
+#if SHOW_PACKET_ERRORS
+// printf (" : %10d Send LE %d\n",
+// GetTickCount () % 100000,
+// ubcsp_config.link_establishment_packet);
+#endif
+
+ /* Send A Link Establishment Message */
+
+ put_uart (SLIP_FRAME);
+
+ /* Send the Link Establishment header followed by the
+ Link Establishment packet */
+
+ ubcsp_setup_packet ((uint8*) ubcsp_send_le_header, 0, (uint8*) ubcsp_le_buffer[ubcsp_config.link_establishment_packet], 4);
+
+ /* start sending immediately */
+
+ ubcsp_config.delay = 0;
+
+ /* workout what the next link establishment packet should be */
+
+ ubcsp_config.link_establishment_packet = next_le_packet[ubcsp_config.link_establishment_state + ubcsp_config.link_establishment_resp * 4];
+
+ /* We have now delt with any response packet that we needed */
+
+ ubcsp_config.link_establishment_resp = 0;
+
+ return 0;
+ }
+ }
+
+ /* We now need to receive any octets from the UART */
+
+ while ((ubcsp_config.receive_packet) && (get_uart (&value)))
+ {
+ /* If the last octet was SLIP_ESCAPE, then special processing is required */
+
+ if (ubcsp_config.receive_slip_escape)
+ {
+ /* WARNING - out of range values are not detected !!!
+ This will probably be caught with the checksum or CRC check */
+
+ value = ubcsp_deslip[value - SLIP_ESCAPE_FRAME];
+
+ ubcsp_config.receive_slip_escape = 0;
+ }
+ else
+ {
+ /* Check for the SLIP_FRAME octet - must be start or end of packet */
+ if (value == SLIP_FRAME)
+ {
+ /* If we had a full header then we have a packet */
+
+ if (ubcsp_config.receive_index >= 0)
+ {
+ /* process the received packet */
+
+ *activity |= ubcsp_recevied_packet ();
+
+ if (*activity & UBCSP_PACKET_ACK)
+ {
+ /* We need to ACK this packet, then don't delay its sending */
+ ubcsp_config.delay = 0;
+ }
+ }
+
+ /* Setup to receive the next packet */
+
+ ubcsp_config.receive_index = -4;
+
+ /* Ok, next octet */
+
+ goto finished_receive;
+ }
+ else if (value == SLIP_ESCAPE)
+ {
+ /* If we receive a SLIP_ESCAPE,
+ then remember to process the next special octet */
+
+ ubcsp_config.receive_slip_escape = 1;
+
+ goto finished_receive;
+ }
+ }
+
+ if (ubcsp_config.receive_index < 0)
+ {
+ /* We are still receiving the header */
+
+ ubcsp_receive_header[ubcsp_config.receive_index + 4] = value;
+
+ ubcsp_config.receive_index ++;
+ }
+ else if (ubcsp_config.receive_index < ubcsp_config.receive_packet->length)
+ {
+ /* We are receiving the payload */
+ /* We might stop comming here if we are receiving a
+ packet which is longer than the receive_packet->length
+ given by the host */
+
+ ubcsp_config.receive_packet->payload[ubcsp_config.receive_index] = value;
+
+ ubcsp_config.receive_index ++;
+ }
+
+finished_receive:
+ {
+ }
+ }
+
+ if (ubcsp_config.delay > 0)
+ {
+ /* We were delayed so delay some more
+ this could be cancelled if we received something */
+
+ ubcsp_config.delay --;
+ }
+ else
+ {
+ /* We had no delay, so use the delay we just decided to us */
+
+ ubcsp_config.delay = delay;
+ }
+
+ /* Report the current delay to the user */
+
+ return ubcsp_config.delay;
+}
diff --git a/tools/ubcsp.h b/tools/ubcsp.h
new file mode 100644
index 0000000..6a74e9a
--- /dev/null
+++ b/tools/ubcsp.h
@@ -0,0 +1,208 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2000-2005 CSR Ltd.
+ *
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef UBCSP_INCLUDE_H
+#define UBCSP_INCLUDE_H
+
+/*****************************************************************************/
+/*****************************************************************************/
+/*****************************************************************************/
+/** **/
+/** ubcsp.h **/
+/** **/
+/** MicroBCSP - a very low cost implementation of the BCSP protocol **/
+/** **/
+/*****************************************************************************/
+
+/* If we wish to use CRC's, then change 0 to 1 in the next line */
+#define UBCSP_CRC 1
+
+/* Define some basic types - change these for your architecture */
+typedef unsigned char uint8;
+typedef unsigned short uint16;
+typedef unsigned int uint32;
+typedef signed char int8;
+typedef signed short int16;
+typedef signed int int32;
+
+/* The defines below require a printf function to be available */
+
+/* Do we want to show packet errors in debug output */
+#define SHOW_PACKET_ERRORS 0
+
+/* Do we want to show Link Establishment State transitions in debug output */
+#define SHOW_LE_STATES 0
+
+/*****************************************************************************/
+/** **/
+/** ubcsp_packet **/
+/** **/
+/** This is description of a bcsp packet for the upper layer **/
+/** **/
+/*****************************************************************************/
+
+struct ubcsp_packet
+{
+ uint8 channel; /* Which Channel this packet is to/from */
+ uint8 reliable; /* Is this packet reliable */
+ uint8 use_crc; /* Does this packet use CRC data protection */
+ uint16 length; /* What is the length of the payload data */
+ uint8 *payload; /* The payload data itself - size of length */
+};
+
+/*****************************************************************************/
+/** **/
+/** ubcsp_configuration **/
+/** **/
+/** This is the main configuration of the ubcsp engine **/
+/** All state variables are stored in this structure **/
+/** **/
+/*****************************************************************************/
+
+enum ubcsp_link_establishment_state
+{
+ ubcsp_le_uninitialized,
+ ubcsp_le_initialized,
+ ubcsp_le_active
+};
+
+enum ubcsp_link_establishment_packet
+{
+ ubcsp_le_sync,
+ ubcsp_le_sync_resp,
+ ubcsp_le_conf,
+ ubcsp_le_conf_resp,
+ ubcsp_le_none
+};
+
+struct ubcsp_configuration
+{
+ uint8 link_establishment_state;
+ uint8 link_establishment_resp;
+ uint8 link_establishment_packet;
+
+ uint8 sequence_number:3;
+ uint8 ack_number:3;
+ uint8 send_ack;
+ struct ubcsp_packet *send_packet;
+ struct ubcsp_packet *receive_packet;
+
+ uint8 receive_header_checksum;
+ uint8 receive_slip_escape;
+ int32 receive_index;
+
+ uint8 send_header_checksum;
+#ifdef UBCSP_CRC
+ uint8 need_send_crc;
+ uint16 send_crc;
+#endif
+ uint8 send_slip_escape;
+
+ uint8 *send_ptr;
+ int32 send_size;
+ uint8 *next_send_ptr;
+ int32 next_send_size;
+
+ int8 delay;
+};
+
+/*****************************************************************************/
+/** **/
+/** ubcsp_poll sets activity from an OR of these flags **/
+/** **/
+/*****************************************************************************/
+
+#define UBCSP_PACKET_SENT 0x01
+#define UBCSP_PACKET_RECEIVED 0x02
+#define UBCSP_PEER_RESET 0x04
+#define UBCSP_PACKET_ACK 0x08
+
+/*****************************************************************************/
+/** **/
+/** This is the functional interface for ucbsp **/
+/** **/
+/*****************************************************************************/
+
+void ubcsp_initialize (void);
+void ubcsp_send_packet (struct ubcsp_packet *send_packet);
+void ubcsp_receive_packet (struct ubcsp_packet *receive_packet);
+uint8 ubcsp_poll (uint8 *activity);
+
+/*****************************************************************************/
+/** **/
+/** Slip Escape Values **/
+/** **/
+/*****************************************************************************/
+
+#define SLIP_FRAME 0xC0
+#define SLIP_ESCAPE 0xDB
+#define SLIP_ESCAPE_FRAME 0xDC
+#define SLIP_ESCAPE_ESCAPE 0xDD
+
+/*****************************************************************************/
+/*****************************************************************************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/** **/
+/** These functions need to be linked into your system **/
+/** **/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/** **/
+/** put_uart outputs a single octet over the UART Tx line **/
+/** **/
+/*****************************************************************************/
+
+extern void put_uart (uint8);
+
+/*****************************************************************************/
+/** **/
+/** get_uart receives a single octet over the UART Rx line **/
+/** if no octet is available, then this returns 0 **/
+/** if an octet was read, then this is returned in the argument and **/
+/** the function returns 1 **/
+/** **/
+/*****************************************************************************/
+
+extern uint8 get_uart (uint8 *);
+
+/*****************************************************************************/
+/** **/
+/** These defines should be changed to your systems concept of 100ms **/
+/** **/
+/*****************************************************************************/
+
+#define UBCSP_POLL_TIME_IMMEDIATE 0
+#define UBCSP_POLL_TIME_DELAY 25
+
+/*****************************************************************************/
+/*****************************************************************************/
+/*****************************************************************************/
+#endif
diff --git a/tracer/main.c b/tracer/main.c
new file mode 100644
index 0000000..0806ffe
--- /dev/null
+++ b/tracer/main.c
@@ -0,0 +1,152 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 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 <string.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <syslog.h>
+
+#include <glib.h>
+
+#ifdef HAVE_CAPNG
+#include <cap-ng.h>
+#endif
+
+static GMainLoop *event_loop;
+
+static void sig_term(int sig)
+{
+ g_main_loop_quit(event_loop);
+}
+
+static gboolean option_detach = TRUE;
+static gboolean option_debug = FALSE;
+
+static GOptionEntry options[] = {
+ { "nodaemon", 'n', G_OPTION_FLAG_REVERSE,
+ G_OPTION_ARG_NONE, &option_detach,
+ "Don't run as daemon in background" },
+ { "debug", 'd', 0, G_OPTION_ARG_NONE, &option_debug,
+ "Enable debug information output" },
+ { NULL },
+};
+
+static void debug(const char *format, ...)
+{
+ va_list ap;
+
+ if (!option_debug)
+ return;
+
+ va_start(ap, format);
+
+ vsyslog(LOG_DEBUG, format, ap);
+
+ va_end(ap);
+}
+
+static void sig_debug(int sig)
+{
+ option_debug = !option_debug;
+}
+
+int main(int argc, char *argv[])
+{
+ GOptionContext *context;
+ GError *err = NULL;
+ struct sigaction sa;
+
+#ifdef HAVE_CAPNG
+ /* Drop capabilities */
+ capng_clear(CAPNG_SELECT_BOTH);
+ capng_updatev(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED,
+ CAP_NET_BIND_SERVICE, CAP_NET_ADMIN,
+ CAP_NET_RAW, CAP_IPC_LOCK, -1);
+ capng_apply(CAPNG_SELECT_BOTH);
+#endif
+
+ context = g_option_context_new(NULL);
+ g_option_context_add_main_entries(context, options, NULL);
+
+ if (g_option_context_parse(context, &argc, &argv, &err) == FALSE) {
+ if (err != NULL) {
+ g_printerr("%s\n", err->message);
+ g_error_free(err);
+ } else
+ g_printerr("An unknown error occurred\n");
+ exit(1);
+ }
+
+ g_option_context_free(context);
+
+ if (option_detach == TRUE) {
+ if (daemon(0, 0)) {
+ perror("Can't start daemon");
+ exit(1);
+ }
+ }
+
+ umask(0077);
+
+ openlog("hcitrace", LOG_PID | LOG_NDELAY | LOG_PERROR, LOG_DAEMON);
+
+ syslog(LOG_INFO, "HCI trace deamon %s", VERSION);
+
+ 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_debug;
+ sigaction(SIGUSR2, &sa, NULL);
+
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &sa, NULL);
+
+ if (option_debug == TRUE) {
+ syslog(LOG_INFO, "Enabling debug information");
+ }
+
+ event_loop = g_main_loop_new(NULL, FALSE);
+
+ debug("Entering main loop");
+
+ g_main_loop_run(event_loop);
+
+ g_main_loop_unref(event_loop);
+
+ syslog(LOG_INFO, "Exit");
+
+ closelog();
+
+ return 0;
+}
diff --git a/ylwrap b/ylwrap
new file mode 100755
index 0000000..84d5634
--- /dev/null
+++ b/ylwrap
@@ -0,0 +1,222 @@
+#! /bin/sh
+# ylwrap - wrapper for lex/yacc invocations.
+
+scriptversion=2009-04-28.21; # UTC
+
+# Copyright (C) 1996, 1997, 1998, 1999, 2001, 2002, 2003, 2004, 2005,
+# 2007, 2009 Free Software Foundation, Inc.
+#
+# Written by Tom Tromey <tromey@cygnus.com>.
+#
+# 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, 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, see <http://www.gnu.org/licenses/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# This file is maintained in Automake, please report
+# bugs to <bug-automake@gnu.org> or send patches to
+# <automake-patches@gnu.org>.
+
+case "$1" in
+ '')
+ echo "$0: No files given. Try \`$0 --help' for more information." 1>&2
+ exit 1
+ ;;
+ --basedir)
+ basedir=$2
+ shift 2
+ ;;
+ -h|--h*)
+ cat <<\EOF
+Usage: ylwrap [--help|--version] INPUT [OUTPUT DESIRED]... -- PROGRAM [ARGS]...
+
+Wrapper for lex/yacc invocations, renaming files as desired.
+
+ INPUT is the input file
+ OUTPUT is one file PROG generates
+ DESIRED is the file we actually want instead of OUTPUT
+ PROGRAM is program to run
+ ARGS are passed to PROG
+
+Any number of OUTPUT,DESIRED pairs may be used.
+
+Report bugs to <bug-automake@gnu.org>.
+EOF
+ exit $?
+ ;;
+ -v|--v*)
+ echo "ylwrap $scriptversion"
+ exit $?
+ ;;
+esac
+
+
+# The input.
+input="$1"
+shift
+case "$input" in
+ [\\/]* | ?:[\\/]*)
+ # Absolute path; do nothing.
+ ;;
+ *)
+ # Relative path. Make it absolute.
+ input="`pwd`/$input"
+ ;;
+esac
+
+pairlist=
+while test "$#" -ne 0; do
+ if test "$1" = "--"; then
+ shift
+ break
+ fi
+ pairlist="$pairlist $1"
+ shift
+done
+
+# The program to run.
+prog="$1"
+shift
+# Make any relative path in $prog absolute.
+case "$prog" in
+ [\\/]* | ?:[\\/]*) ;;
+ *[\\/]*) prog="`pwd`/$prog" ;;
+esac
+
+# FIXME: add hostname here for parallel makes that run commands on
+# other machines. But that might take us over the 14-char limit.
+dirname=ylwrap$$
+trap "cd '`pwd`'; rm -rf $dirname > /dev/null 2>&1" 1 2 3 15
+mkdir $dirname || exit 1
+
+cd $dirname
+
+case $# in
+ 0) "$prog" "$input" ;;
+ *) "$prog" "$@" "$input" ;;
+esac
+ret=$?
+
+if test $ret -eq 0; then
+ set X $pairlist
+ shift
+ first=yes
+ # Since DOS filename conventions don't allow two dots,
+ # the DOS version of Bison writes out y_tab.c instead of y.tab.c
+ # and y_tab.h instead of y.tab.h. Test to see if this is the case.
+ y_tab_nodot="no"
+ if test -f y_tab.c || test -f y_tab.h; then
+ y_tab_nodot="yes"
+ fi
+
+ # The directory holding the input.
+ input_dir=`echo "$input" | sed -e 's,\([\\/]\)[^\\/]*$,\1,'`
+ # Quote $INPUT_DIR so we can use it in a regexp.
+ # FIXME: really we should care about more than `.' and `\'.
+ input_rx=`echo "$input_dir" | sed 's,\\\\,\\\\\\\\,g;s,\\.,\\\\.,g'`
+
+ while test "$#" -ne 0; do
+ from="$1"
+ # Handle y_tab.c and y_tab.h output by DOS
+ if test $y_tab_nodot = "yes"; then
+ if test $from = "y.tab.c"; then
+ from="y_tab.c"
+ else
+ if test $from = "y.tab.h"; then
+ from="y_tab.h"
+ fi
+ fi
+ fi
+ if test -f "$from"; then
+ # If $2 is an absolute path name, then just use that,
+ # otherwise prepend `../'.
+ case "$2" in
+ [\\/]* | ?:[\\/]*) target="$2";;
+ *) target="../$2";;
+ esac
+
+ # We do not want to overwrite a header file if it hasn't
+ # changed. This avoid useless recompilations. However the
+ # parser itself (the first file) should always be updated,
+ # because it is the destination of the .y.c rule in the
+ # Makefile. Divert the output of all other files to a temporary
+ # file so we can compare them to existing versions.
+ if test $first = no; then
+ realtarget="$target"
+ target="tmp-`echo $target | sed s/.*[\\/]//g`"
+ fi
+ # Edit out `#line' or `#' directives.
+ #
+ # We don't want the resulting debug information to point at
+ # an absolute srcdir; it is better for it to just mention the
+ # .y file with no path.
+ #
+ # We want to use the real output file name, not yy.lex.c for
+ # instance.
+ #
+ # We want the include guards to be adjusted too.
+ FROM=`echo "$from" | sed \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'\
+ -e 's/[^ABCDEFGHIJKLMNOPQRSTUVWXYZ]/_/g'`
+ TARGET=`echo "$2" | sed \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'\
+ -e 's/[^ABCDEFGHIJKLMNOPQRSTUVWXYZ]/_/g'`
+
+ sed -e "/^#/!b" -e "s,$input_rx,," -e "s,$from,$2," \
+ -e "s,$FROM,$TARGET," "$from" >"$target" || ret=$?
+
+ # Check whether header files must be updated.
+ if test $first = no; then
+ if test -f "$realtarget" && cmp -s "$realtarget" "$target"; then
+ echo "$2" is unchanged
+ rm -f "$target"
+ else
+ echo updating "$2"
+ mv -f "$target" "$realtarget"
+ fi
+ fi
+ else
+ # A missing file is only an error for the first file. This
+ # is a blatant hack to let us support using "yacc -d". If -d
+ # is not specified, we don't want an error when the header
+ # file is "missing".
+ if test $first = yes; then
+ ret=1
+ fi
+ fi
+ shift
+ shift
+ first=no
+ done
+else
+ ret=$?
+fi
+
+# Remove the directory.
+cd ..
+rm -rf $dirname
+
+exit $ret
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End: